import { useEffect, useCallback, useRef } from "react";
import { useWebSocket } from "./useWebSocket";
import {
  useAgentStore,
  type AgentState,
  type AgentHealthStatus,
} from "../stores/agentStore";
import type { TradeRecord, DashboardAlert } from "../types/models";

// WebSocket message types from backend
interface AgentBalanceMessage {
  type: "agent_balance";
  agent_id: string;
  balance: number;
  profit_loss: number;
  profit_loss_percent: number;
  timestamp: string;
}

interface AgentTradeMessage {
  type: "agent_trade";
  trade: TradeRecord;
  timestamp: string;
}

interface AgentStatusMessage {
  type: "agent_status";
  agent_id: string;
  status: "running" | "paused" | "stopped" | "error";
  health_status: AgentHealthStatus;
  timestamp: string;
}

interface AgentInitMessage {
  type: "agent_init";
  data?: {
    agents: AgentState[];
    metrics?: {
      total_agents: number;
      active_agents: number;
      bankrupt_agents: number;
      total_trades: number;
      total_volume: number;
      total_pnl: number;
      avg_win_rate: number;
    };
    recent_trades?: TradeRecord[];
    alerts?: Array<{
      alert_id: string;
      alert_type: string;
      level: string;
      agent_id?: string;
      title: string;
      message: string;
      details?: Record<string, unknown>;
      acknowledged?: boolean;
      created_at?: string;
    }>;
  };
  timestamp: string;
}

type AgentWebSocketMessage =
  | AgentBalanceMessage
  | AgentTradeMessage
  | AgentStatusMessage
  | AgentInitMessage;

// Toast notification callback type
export type ToastCallback = (
  message: string,
  type: "success" | "error" | "warning" | "info",
) => void;

interface UseAgentUpdatesOptions {
  url?: string;
  onToast?: ToastCallback;
  enabled?: boolean;
}

export function useAgentUpdates({
  url = typeof window !== "undefined"
    ? process.env.NEXT_PUBLIC_WS_URL ||
      `ws://${window.location.hostname}:8000/ws`
    : "ws://localhost:8000/ws",
  onToast,
  enabled = true,
}: UseAgentUpdatesOptions = {}) {
  const setAgent = useAgentStore((state) => state.setAgent);
  const setAlerts = useAgentStore((state) => state.setAlerts);
  const updateAgentBalance = useAgentStore((state) => state.updateAgentBalance);
  const updateAgentStatus = useAgentStore((state) => state.updateAgentStatus);
  const addTrade = useAgentStore((state) => state.addTrade);
  const setConnectionStatus = useAgentStore(
    (state) => state.setConnectionStatus,
  );
  const getAllAgents = useAgentStore((state) => state.getAllAgents);

  // Use refs to avoid re-renders when callbacks change
  const onToastRef = useRef(onToast);
  const hasConnectedRef = useRef(false);

  // Update ref in useEffect to avoid modifying during render
  useEffect(() => {
    onToastRef.current = onToast;
  }, [onToast]);

  const showToast = useCallback(
    (message: string, type: "success" | "error" | "warning" | "info") => {
      onToastRef.current?.(message, type);
    },
    [],
  );

  const handleMessage = useCallback(
    (data: AgentWebSocketMessage) => {
      switch (data.type) {
        case "agent_init":
          // Initialize all agents, trades, and alerts from nested data structure
          const agentData = data.data;
          if (agentData?.agents) {
            agentData.agents.forEach((agent) => {
              setAgent(agent);
            });
            console.log(
              `[AgentUpdates] Initialized ${agentData.agents.length} agents`,
            );
          }
          if (agentData?.recent_trades && agentData.recent_trades.length > 0) {
            agentData.recent_trades.forEach((trade) => {
              addTrade(trade);
            });
          }
          if (agentData?.alerts && agentData.alerts.length > 0) {
            setAlerts(
              agentData.alerts.map((a) => ({
                alert_id: a.alert_id,
                alert_type: a.alert_type,
                level: a.level as DashboardAlert["level"],
                agent_id: a.agent_id,
                title: a.title,
                message: a.message,
                details: a.details,
                acknowledged: a.acknowledged,
                created_at: a.created_at,
              })),
            );
          }
          break;

        case "agent_balance":
          updateAgentBalance(
            data.agent_id,
            data.balance,
            data.profit_loss,
            data.profit_loss_percent,
          );
          // Show toast for significant balance changes (>5%)
          if (Math.abs(data.profit_loss_percent) > 5) {
            const direction = data.profit_loss >= 0 ? "gained" : "lost";
            const idStr = data.agent_id ? data.agent_id.slice(0, 8) : "Unknown";
            showToast(
              `Agent ${idStr}... ${direction} ${Math.abs(data.profit_loss_percent).toFixed(2)}%`,
              data.profit_loss >= 0 ? "success" : "warning",
            );
          }
          break;

        case "agent_trade":
          addTrade(data.trade);
          showToast(
            `New trade: ${data.trade.side.toUpperCase()} ${data.trade.quantity} ${data.trade.symbol} @ $${data.trade.price}`,
            "info",
          );
          break;

        case "agent_status":
          updateAgentStatus(data.agent_id, data.status, data.health_status);
          const statusMessages: Record<string, string> = {
            running: "started trading",
            paused: "paused trading",
            stopped: "stopped trading",
            error: "encountered an error",
          };
          const idStrStatus = data.agent_id
            ? data.agent_id.slice(0, 8)
            : "Unknown";
          showToast(
            `Agent ${idStrStatus}... ${statusMessages[data.status] || "status changed"}`,
            data.status === "error" ? "error" : "info",
          );
          break;

        default:
          console.warn(
            "[AgentUpdates] Unknown message type:",
            (data as { type: string }).type,
          );
      }
    },
    [
      setAgent,
      setAlerts,
      updateAgentBalance,
      updateAgentStatus,
      addTrade,
      showToast,
    ],
  );

  const handleConnect = useCallback(() => {
    console.log("[AgentUpdates] WebSocket connected");
    setConnectionStatus(true);
    hasConnectedRef.current = true;
    showToast("Connected to agent updates", "success");
  }, [setConnectionStatus, showToast]);

  const handleDisconnect = useCallback(() => {
    console.log("[AgentUpdates] WebSocket disconnected");
    setConnectionStatus(false);
    showToast("Disconnected from agent updates", "warning");
  }, [setConnectionStatus, showToast]);

  const handleError = useCallback(
    (error: Event) => {
      // Only log errors after first successful connection to avoid transient errors
      if (hasConnectedRef.current) {
        console.error("[AgentUpdates] WebSocket error:", error);
        showToast("Connection error - retrying...", "error");
      }
    },
    [showToast],
  );

  const ws = useWebSocket({
    url,
    onMessage: handleMessage as (data: unknown) => void,
    onConnect: handleConnect,
    onDisconnect: handleDisconnect,
    onError: handleError,
    maxReconnectAttempts: 10,
    heartbeatInterval: 30000,
  });

  // Manual connect/disconnect based on enabled flag
  useEffect(() => {
    if (enabled) {
      ws.connect();
    } else {
      ws.disconnect();
    }
  }, [enabled, ws.connect, ws.disconnect]);

  return {
    isConnected: ws.isConnected,
    isConnecting: ws.isConnecting,
    reconnectAttempts: ws.reconnectAttempts,
    agents: getAllAgents(),
    connect: ws.connect,
    disconnect: ws.disconnect,
  };
}

// Hook for single agent updates
export function useSingleAgentUpdates(
  agentId: string,
  options: Omit<UseAgentUpdatesOptions, "enabled"> = {},
) {
  const { agents, ...rest } = useAgentUpdates(options);
  const agent = agents.find((a) => a.id === agentId);

  return {
    agent,
    ...rest,
  };
}
