/**
 * Trading Agents Store
 * Zustand store for managing Trading Agents (Atlas, Orion, Nova)
 */

import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import type {
  AgentType,
  TradingBot,
  BotStatus,
  TradingBotConfig,
  AgentPosition,
  AgentTrade,
  SignalInput,
  AgentStatusResponse,
  AgentMetrics,
} from '../types/tradingAgents.types';
import {
  getAllAgentsSummary,
  getAgentStatus,
  getAgentMetrics,
  startAgent,
  stopAgent,
  pauseAgent,
  resumeAgent,
  getAgentPositions,
  getAgentTrades,
  closePosition,
  closeAllPositions,
  sendSignal,
  getAgentsHealth,
} from '../services/agents.service';
import {
  getAgentDisplayName,
  getAgentDescription,
  statusToDisplayStatus,
} from '../types/tradingAgents.types';

// ============================================================================
// State Interface
// ============================================================================

interface AgentsState {
  // Bots data
  bots: TradingBot[];
  selectedBot: TradingBot | null;

  // Positions & Trades
  positions: AgentPosition[];
  trades: AgentTrade[];

  // Loading states
  isLoading: boolean;
  loadingPositions: boolean;
  loadingTrades: boolean;
  loadingMetrics: boolean;

  // Error state
  error: string | null;

  // Service status
  serviceAvailable: boolean;

  // Actions - Bot Management
  fetchBots: () => Promise<void>;
  selectBot: (botName: AgentType) => void;
  refreshBot: (botName: AgentType) => Promise<void>;
  startBot: (botName: AgentType, config: TradingBotConfig) => Promise<void>;
  stopBot: (botName: AgentType) => Promise<void>;
  pauseBot: (botName: AgentType) => Promise<void>;
  resumeBot: (botName: AgentType) => Promise<void>;

  // Actions - Metrics
  updateBotMetrics: (botName: AgentType, metrics: Partial<AgentMetrics>) => void;
  fetchBotMetrics: (botName: AgentType) => Promise<void>;

  // Actions - Positions & Trades
  fetchPositions: (botName: AgentType) => Promise<void>;
  fetchTrades: (botName: AgentType, options?: { limit?: number; offset?: number; symbol?: string }) => Promise<void>;
  closePosition: (botName: AgentType, positionId: string) => Promise<void>;
  closeAllPositions: (botName: AgentType) => Promise<void>;

  // Actions - Signals
  sendSignal: (botName: AgentType, signal: SignalInput) => Promise<void>;

  // Utility actions
  clearError: () => void;
  reset: () => void;
}

// ============================================================================
// Initial State
// ============================================================================

const AGENT_TYPES: AgentType[] = ['atlas', 'orion', 'nova'];

const initialBots: TradingBot[] = AGENT_TYPES.map((name) => ({
  id: name,
  name,
  displayName: getAgentDisplayName(name),
  description: getAgentDescription(name),
  status: 'stopped' as BotStatus,
  equity: 0,
  positions: 0,
  todayPnl: 0,
  metrics: undefined,
}));

const initialState = {
  bots: initialBots,
  selectedBot: null,
  positions: [],
  trades: [],
  isLoading: false,
  loadingPositions: false,
  loadingTrades: false,
  loadingMetrics: false,
  error: null,
  serviceAvailable: false,
};

// ============================================================================
// Helper Functions
// ============================================================================

/**
 * Convert backend status response to frontend TradingBot format
 */
function mapStatusToBotData(status: AgentStatusResponse, existingBot?: TradingBot): TradingBot {
  return {
    id: status.agent_name,
    name: status.agent_name,
    displayName: existingBot?.displayName || getAgentDisplayName(status.agent_name),
    description: existingBot?.description || getAgentDescription(status.agent_name),
    status: statusToDisplayStatus(status.status),
    equity: status.equity,
    positions: status.positions,
    todayPnl: status.today_pnl,
    metrics: existingBot?.metrics,
  };
}

/**
 * Convert backend metrics response to frontend AgentMetrics format
 */
function mapMetricsResponse(metrics: AgentMetrics): AgentMetrics {
  return {
    total_trades: metrics.total_trades,
    winning_trades: metrics.winning_trades,
    losing_trades: metrics.losing_trades,
    win_rate: metrics.win_rate,
    total_profit: metrics.total_profit,
    total_loss: metrics.total_loss,
    net_pnl: metrics.net_pnl,
    max_drawdown: metrics.max_drawdown,
    current_drawdown: metrics.current_drawdown,
    sharpe_ratio: metrics.sharpe_ratio,
    sortino_ratio: metrics.sortino_ratio,
    profit_factor: metrics.profit_factor,
  };
}

// ============================================================================
// Store
// ============================================================================

export const useAgentsStore = create<AgentsState>()(
  devtools(
    (set, get) => ({
      ...initialState,

      // ======================================================================
      // Bot Management Actions
      // ======================================================================

      fetchBots: async () => {
        set({ isLoading: true, error: null });

        try {
          // Check service health first
          const health = await getAgentsHealth();
          const serviceAvailable = health.status === 'healthy';

          if (!serviceAvailable) {
            set({
              serviceAvailable: false,
              isLoading: false,
              bots: initialBots,
              error: 'Trading Agents service is not available',
            });
            return;
          }

          // Fetch all agents summary
          const summaries = await getAllAgentsSummary();

          // Map summaries to bots
          const bots = AGENT_TYPES.map((agentType) => {
            const summary = summaries.find((s) => s.name === agentType);
            const existingBot = get().bots.find((b) => b.name === agentType);

            if (!summary) {
              return existingBot || initialBots.find((b) => b.name === agentType)!;
            }

            return {
              id: agentType,
              name: agentType,
              displayName: getAgentDisplayName(agentType),
              description: getAgentDescription(agentType),
              status: statusToDisplayStatus(summary.status),
              equity: summary.equity,
              positions: summary.positions,
              todayPnl: summary.todayPnl,
              metrics: existingBot?.metrics,
            };
          });

          set({
            bots,
            serviceAvailable: true,
            isLoading: false,
          });
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to fetch bots';
          set({
            error: message,
            isLoading: false,
            serviceAvailable: false,
          });
          console.error('Error fetching bots:', error);
        }
      },

      selectBot: (botName: AgentType) => {
        const bot = get().bots.find((b) => b.name === botName);
        if (bot) {
          set({ selectedBot: bot, positions: [], trades: [] });
          // Auto-fetch positions and trades for selected bot
          get().fetchPositions(botName);
          get().fetchTrades(botName);
          get().fetchBotMetrics(botName);
        }
      },

      refreshBot: async (botName: AgentType) => {
        try {
          const [status, metrics] = await Promise.all([
            getAgentStatus(botName),
            getAgentMetrics(botName).catch(() => null),
          ]);

          const existingBot = get().bots.find((b) => b.name === botName);
          const updatedBot = mapStatusToBotData(status, existingBot);

          if (metrics) {
            updatedBot.metrics = mapMetricsResponse(metrics);
          }

          set((state) => ({
            bots: state.bots.map((b) => (b.name === botName ? updatedBot : b)),
            selectedBot: state.selectedBot?.name === botName ? updatedBot : state.selectedBot,
          }));
        } catch (error) {
          console.error(`Error refreshing bot ${botName}:`, error);
        }
      },

      startBot: async (botName: AgentType, config: TradingBotConfig) => {
        set({ error: null });

        try {
          const status = await startAgent(botName, config);
          const existingBot = get().bots.find((b) => b.name === botName);
          const updatedBot = mapStatusToBotData(status, existingBot);

          set((state) => ({
            bots: state.bots.map((b) => (b.name === botName ? updatedBot : b)),
            selectedBot: state.selectedBot?.name === botName ? updatedBot : state.selectedBot,
          }));

          // Refresh full data after starting
          await get().refreshBot(botName);
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to start bot';
          set({ error: message });
          throw error;
        }
      },

      stopBot: async (botName: AgentType) => {
        set({ error: null });

        try {
          const status = await stopAgent(botName);
          const existingBot = get().bots.find((b) => b.name === botName);
          const updatedBot = mapStatusToBotData(status, existingBot);

          set((state) => ({
            bots: state.bots.map((b) => (b.name === botName ? updatedBot : b)),
            selectedBot: state.selectedBot?.name === botName ? updatedBot : state.selectedBot,
            positions: state.selectedBot?.name === botName ? [] : state.positions,
          }));
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to stop bot';
          set({ error: message });
          throw error;
        }
      },

      pauseBot: async (botName: AgentType) => {
        set({ error: null });

        try {
          const status = await pauseAgent(botName);
          const existingBot = get().bots.find((b) => b.name === botName);
          const updatedBot = mapStatusToBotData(status, existingBot);

          set((state) => ({
            bots: state.bots.map((b) => (b.name === botName ? updatedBot : b)),
            selectedBot: state.selectedBot?.name === botName ? updatedBot : state.selectedBot,
          }));
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to pause bot';
          set({ error: message });
          throw error;
        }
      },

      resumeBot: async (botName: AgentType) => {
        set({ error: null });

        try {
          const status = await resumeAgent(botName);
          const existingBot = get().bots.find((b) => b.name === botName);
          const updatedBot = mapStatusToBotData(status, existingBot);

          set((state) => ({
            bots: state.bots.map((b) => (b.name === botName ? updatedBot : b)),
            selectedBot: state.selectedBot?.name === botName ? updatedBot : state.selectedBot,
          }));
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to resume bot';
          set({ error: message });
          throw error;
        }
      },

      // ======================================================================
      // Metrics Actions
      // ======================================================================

      updateBotMetrics: (botName: AgentType, metricsUpdate: Partial<AgentMetrics>) => {
        set((state) => ({
          bots: state.bots.map((b) =>
            b.name === botName
              ? { ...b, metrics: { ...b.metrics, ...metricsUpdate } as AgentMetrics }
              : b
          ),
          selectedBot:
            state.selectedBot?.name === botName
              ? { ...state.selectedBot, metrics: { ...state.selectedBot.metrics, ...metricsUpdate } as AgentMetrics }
              : state.selectedBot,
        }));
      },

      fetchBotMetrics: async (botName: AgentType) => {
        set({ loadingMetrics: true });

        try {
          const metrics = await getAgentMetrics(botName);
          const mappedMetrics = mapMetricsResponse(metrics);

          get().updateBotMetrics(botName, mappedMetrics);
          set({ loadingMetrics: false });
        } catch (error) {
          console.error(`Error fetching metrics for ${botName}:`, error);
          set({ loadingMetrics: false });
        }
      },

      // ======================================================================
      // Positions & Trades Actions
      // ======================================================================

      fetchPositions: async (botName: AgentType) => {
        set({ loadingPositions: true, error: null });

        try {
          const positions = await getAgentPositions(botName);
          set({ positions, loadingPositions: false });
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to fetch positions';
          set({ error: message, loadingPositions: false, positions: [] });
          console.error('Error fetching positions:', error);
        }
      },

      fetchTrades: async (botName: AgentType, options?) => {
        set({ loadingTrades: true, error: null });

        try {
          const trades = await getAgentTrades(botName, options);
          set({ trades, loadingTrades: false });
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to fetch trades';
          set({ error: message, loadingTrades: false, trades: [] });
          console.error('Error fetching trades:', error);
        }
      },

      closePosition: async (botName: AgentType, positionId: string) => {
        set({ error: null });

        try {
          await closePosition(botName, positionId);
          // Refresh positions and bot data after closing
          await Promise.all([
            get().fetchPositions(botName),
            get().refreshBot(botName),
          ]);
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to close position';
          set({ error: message });
          throw error;
        }
      },

      closeAllPositions: async (botName: AgentType) => {
        set({ error: null });

        try {
          await closeAllPositions(botName);
          // Refresh positions and bot data after closing all
          await Promise.all([
            get().fetchPositions(botName),
            get().refreshBot(botName),
          ]);
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to close all positions';
          set({ error: message });
          throw error;
        }
      },

      // ======================================================================
      // Signal Actions
      // ======================================================================

      sendSignal: async (botName: AgentType, signal: SignalInput) => {
        set({ error: null });

        try {
          await sendSignal(botName, signal);
          // Signal sent successfully - no state update needed
        } catch (error) {
          const message = error instanceof Error ? error.message : 'Failed to send signal';
          set({ error: message });
          throw error;
        }
      },

      // ======================================================================
      // Utility Actions
      // ======================================================================

      clearError: () => {
        set({ error: null });
      },

      reset: () => {
        set(initialState);
      },
    }),
    {
      name: 'agents-store',
    }
  )
);

// ============================================================================
// Selectors (for performance optimization)
// ============================================================================

export const useBots = () => useAgentsStore((state) => state.bots);
export const useSelectedBot = () => useAgentsStore((state) => state.selectedBot);
export const useBotPositions = () => useAgentsStore((state) => state.positions);
export const useBotTrades = () => useAgentsStore((state) => state.trades);
export const useAgentsLoading = () => useAgentsStore((state) => state.isLoading);
export const useAgentsError = () => useAgentsStore((state) => state.error);
export const useServiceAvailable = () => useAgentsStore((state) => state.serviceAvailable);
export const useLoadingPositions = () => useAgentsStore((state) => state.loadingPositions);
export const useLoadingTrades = () => useAgentsStore((state) => state.loadingTrades);
export const useLoadingMetrics = () => useAgentsStore((state) => state.loadingMetrics);

export default useAgentsStore;
