import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import type { ReactNode } from 'react';
import { io, type Socket } from 'socket.io-client';
import { mobileRuntime } from '@/lib/runtime';
import { mobileTelemetry, trackMobileError } from '@/lib/telemetry';
import { useMobileAuth } from '@/providers/MobileAuthProvider';
import { buildTradingSocketOptions, isUnauthorizedSocketError } from '../../shared/realtime.js';

type HealthSnapshot = {
  tradingControl?: {
    mode: 'RUNNING' | 'PAUSED';
    lastChangedBy: string;
    lastChangedAt: number;
    reason?: string;
  };
};

type BotState = {
  symbols: Record<string, {
    price: number;
    change24h: number;
    signal?: string;
    activePosition?: {
      side: 'BUY' | 'SELL';
      entryPrice: number;
      size: number;
      stopLoss: number;
      takeProfit: number;
      unrealizedPnl?: number;
      unrealizedPnlPercent?: number;
      marketValue?: number;
      profileId?: string;
      profileName?: string;
      tradeId?: string;
    } | null;
    priceHistory?: Array<{ timestamp: number; price: number }>;
    profileSignals?: Record<string, {
      signal: string;
      profileName?: string;
    }>;
  }>;
  alerts: Array<{
    timestamp: number;
    type: 'signal' | 'pulse' | 'error' | 'info';
    symbol: string;
    message: string;
    profileId?: string;
  }>;
  positions: Array<{
    id: string;
    symbol: string;
    side: 'BUY' | 'SELL';
    size: number;
    entryPrice: number;
    currentPrice: number;
    stopLoss: number;
    takeProfit: number;
    unrealizedPnl: number;
    unrealizedPnlPercent: number;
    marketValue: number;
    profileId?: string;
    profileName?: string;
    tradeId?: string;
  }>;
  orders: Array<{
    id: string;
    symbol: string;
    type: string;
    side: string;
    qty: number;
    price: number;
    status: string;
    timestamp: number;
    profileId?: string;
    action?: 'ENTRY' | 'EXIT';
    source?: 'BOT' | 'MANUAL';
  }>;
  history: Array<{
    symbol: string;
    side: string;
    entryPrice: number;
    exitPrice: number;
    size: number;
    pnl: number;
    pnlPercent: number;
    reason: string;
    timestamp: number;
    profileId?: string;
    profileName?: string;
    source?: 'BOT' | 'MANUAL';
  }>;
  settings: {
    executionMode: string;
    riskPerTrade: number;
    totalCapital: number;
    maxOpenTrades: number;
    isAlgoEnabled: boolean;
  };
  uptime: number;
  health?: HealthSnapshot;
};

export interface TradingPortfolioSummary {
  netPnl: number;
  netPnlPercent: number;
  totalCapital: number;
  deployed: number;
  available: number;
  utilization: number;
  realizedPnl: number;
  unrealizedPnl: number;
}

interface TradingDataContextValue {
  botState: BotState | null;
  loading: boolean;
  error: string | null;
  connected: boolean;
  connectionState: 'live' | 'degraded' | 'offline';
  lastUpdatedAt: number | null;
  refresh: () => Promise<void>;
  pauseTrading: (reason?: string) => Promise<{ error?: string }>;
  resumeTrading: (reason?: string) => Promise<{ error?: string }>;
  portfolio: TradingPortfolioSummary;
  marketTicker: Array<{ symbol: string; price: number; change: number }>;
}

const TradingDataContext = createContext<TradingDataContextValue | null>(null);
const tradingSocketUrl = mobileRuntime.tradingApiUrl.replace(/\/api$/, '');

const EMPTY_STATE: TradingPortfolioSummary = {
  netPnl: 0,
  netPnlPercent: 0,
  totalCapital: 0,
  deployed: 0,
  available: 0,
  utilization: 0,
  realizedPnl: 0,
  unrealizedPnl: 0,
};

export function TradingDataProvider({ children }: { children: ReactNode }) {
  const { accessToken, user, invalidateSession } = useMobileAuth();
  const [botState, setBotState] = useState<BotState | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [connected, setConnected] = useState(false);
  const [lastUpdatedAt, setLastUpdatedAt] = useState<number | null>(null);
  const [statusClock, setStatusClock] = useState(() => Date.now());

  const markStateSynced = useCallback(() => {
    setLastUpdatedAt(Date.now());
  }, []);

  const fetchState = useCallback(async () => {
    if (!accessToken || !user) {
      setBotState(null);
      setConnected(false);
      setLoading(false);
      return;
    }

    setLoading(true);
    try {
      const response = await fetch(`${mobileRuntime.tradingApiUrl}/state`, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      });

      if (!response.ok) {
        if (response.status === 401 || response.status === 403) {
          await invalidateSession(`Trading session expired (${response.status})`);
          return;
        }
        throw new Error(`Trading state request failed (${response.status})`);
      }

      const data = (await response.json()) as BotState;
      setBotState(data);
      setConnected(true);
      setError(null);
      markStateSynced();
    } catch (fetchError) {
      setConnected(false);
      setError(fetchError instanceof Error ? fetchError.message : 'Failed to load trading state');
      trackMobileError('trading_data', 'state_fetch_failed', fetchError);
    } finally {
      setLoading(false);
    }
  }, [accessToken, user, invalidateSession, markStateSynced]);

  useEffect(() => {
    void fetchState();
    if (!accessToken || !user) {
      return;
    }
    const interval = setInterval(() => {
      void fetchState();
    }, 60000);
    return () => clearInterval(interval);
  }, [accessToken, user, fetchState]);

  useEffect(() => {
    const interval = setInterval(() => {
      setStatusClock(Date.now());
    }, 30000);

    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    if (!accessToken || !user) {
      return;
    }

    let socket: Socket | null = null;

    const mergePartialState = (nextState: BotState) => {
      setBotState((prev) => ({
        ...(prev || nextState),
        ...nextState,
        health: nextState.health || prev?.health,
      }));
    };

    socket = io(tradingSocketUrl, buildTradingSocketOptions(accessToken));

    socket.on('connect', () => {
      setConnected(true);
      setError(null);
      mobileTelemetry.trackEvent('info', 'realtime', 'socket_connected');
    });

    socket.on('disconnect', () => {
      setConnected(false);
      mobileTelemetry.trackEvent('warn', 'realtime', 'socket_disconnected');
    });

    socket.on('connect_error', (socketError) => {
      setError(socketError.message);
      trackMobileError('realtime', 'socket_connect_failed', socketError);
      if (isUnauthorizedSocketError(socketError.message)) {
        void invalidateSession(socketError.message);
      }
    });

    socket.on('state', (nextState: BotState) => {
      mergePartialState(nextState);
      markStateSynced();
    });

    socket.on('health_update', (health: HealthSnapshot) => {
      markStateSynced();
      setBotState((prev) => (prev ? { ...prev, health } : prev));
    });

    socket.on('symbol_update', ({ symbol, data }: { symbol: string; data: BotState['symbols'][string] }) => {
      markStateSynced();
      setBotState((prev) => {
        if (!prev) return prev;
        return {
          ...prev,
          symbols: {
            ...prev.symbols,
            [symbol]: data,
          },
        };
      });
    });

    socket.on('new_alert', (alert: BotState['alerts'][number]) => {
      markStateSynced();
      setBotState((prev) => {
        if (!prev) return prev;
        return {
          ...prev,
          alerts: [alert, ...prev.alerts].slice(0, 25),
        };
      });
    });

    socket.on('positions_update', (positions: BotState['positions']) => {
      markStateSynced();
      setBotState((prev) => (prev ? { ...prev, positions } : prev));
    });

    socket.on('orders_update', (orders: BotState['orders']) => {
      markStateSynced();
      setBotState((prev) => (prev ? { ...prev, orders } : prev));
    });

    socket.on('history_update', (trade: BotState['history'][number]) => {
      markStateSynced();
      setBotState((prev) => {
        if (!prev) return prev;
        return {
          ...prev,
          history: [trade, ...prev.history].slice(0, 100),
        };
      });
    });

    socket.on('settings_update', (settings: BotState['settings']) => {
      markStateSynced();
      setBotState((prev) => (prev ? { ...prev, settings } : prev));
    });

    socket.on('account_snapshot', () => {
      void fetchState();
    });

    socket.on('order_failure', () => {
      void fetchState();
    });

    socket.on('operational_event', () => {
      void fetchState();
    });

    return () => {
      socket?.close();
    };
  }, [accessToken, user, fetchState, invalidateSession, markStateSynced]);

  const postTradingAction = useCallback(
    async (path: '/internal/trading/pause' | '/internal/trading/resume', reason?: string) => {
      if (!accessToken) {
        return { error: 'Not authenticated' };
      }

      try {
        const response = await fetch(`${mobileRuntime.tradingApiUrl.replace(/\/api$/, '')}${path}`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${accessToken}`,
          },
          body: JSON.stringify({
            reason: reason || 'Requested from mobile trading surface',
          }),
        });

        const body = await response.json().catch(() => ({} as { error?: string }));
        if (!response.ok) {
          if (response.status === 401 || response.status === 403) {
            await invalidateSession(body.error || `Trading control unauthorized (${response.status})`);
            return { error: body.error || `Request failed (${response.status})` };
          }
          mobileTelemetry.trackEvent('warn', 'controls', 'trading_action_failed', {
            message: body.error || `Request failed (${response.status})`,
            tags: { path },
          });
          return { error: body.error || `Request failed (${response.status})` };
        }
        await fetchState();
        mobileTelemetry.trackEvent('info', 'controls', 'trading_action_succeeded', {
          tags: { path },
        });
        return {};
      } catch (actionError) {
        trackMobileError('controls', 'trading_action_failed', actionError, { path });
        return { error: actionError instanceof Error ? actionError.message : 'Trading action failed' };
      }
    },
    [accessToken, fetchState, invalidateSession]
  );

  const portfolio = useMemo<TradingPortfolioSummary>(() => {
    if (!botState) {
      return EMPTY_STATE;
    }
    const unrealizedPnl = botState.positions.reduce((sum, position) => sum + Number(position.unrealizedPnl || 0), 0);
    const deployed = botState.positions.reduce((sum, position) => sum + Number(position.marketValue || 0), 0);
    const realizedPnl = botState.history.reduce((sum, trade) => sum + Number(trade.pnl || 0), 0);
    const totalCapital = Number(botState.settings.totalCapital || 0);
    const available = Math.max(totalCapital - deployed, 0);
    const netPnl = realizedPnl + unrealizedPnl;
    const netPnlPercent = totalCapital > 0 ? (netPnl / totalCapital) * 100 : 0;
    const utilization = totalCapital > 0 ? Math.min((deployed / totalCapital) * 100, 100) : 0;

    return {
      netPnl,
      netPnlPercent,
      totalCapital,
      deployed,
      available,
      utilization,
      realizedPnl,
      unrealizedPnl,
    };
  }, [botState]);

  const marketTicker = useMemo(
    () =>
      Object.entries(botState?.symbols || {}).map(([symbol, data]) => ({
        symbol,
        price: Number(data.price || 0),
        change: Number(data.change24h || 0),
      })),
    [botState]
  );

  const connectionState = useMemo<'live' | 'degraded' | 'offline'>(() => {
    if (!connected && !botState) {
      return 'offline';
    }

    const staleMs = lastUpdatedAt ? statusClock - lastUpdatedAt : Number.POSITIVE_INFINITY;
    if (connected && !error && staleMs < 90_000) {
      return 'live';
    }

    if (botState || connected) {
      return 'degraded';
    }

    return 'offline';
  }, [connected, botState, error, lastUpdatedAt, statusClock]);

  const value = useMemo<TradingDataContextValue>(
    () => ({
      botState,
      loading,
      error,
      connected,
      connectionState,
      lastUpdatedAt,
      refresh: fetchState,
      pauseTrading: (reason?: string) => postTradingAction('/internal/trading/pause', reason),
      resumeTrading: (reason?: string) => postTradingAction('/internal/trading/resume', reason),
      portfolio,
      marketTicker,
    }),
    [botState, loading, error, connected, connectionState, lastUpdatedAt, fetchState, postTradingAction, portfolio, marketTicker]
  );

  return <TradingDataContext.Provider value={value}>{children}</TradingDataContext.Provider>;
}

export function useTradingData() {
  const context = useContext(TradingDataContext);
  if (!context) {
    throw new Error('useTradingData must be used within a TradingDataProvider');
  }
  return context;
}
