import { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';
import { CommandInputHandle } from '../components/Terminal/CommandInput';
import { createEntry } from './useTerminal';
import { usePortfolioWorkflow, isPortfolioCommand } from './usePortfolioWorkflow';
import { useTabs } from './useTabs';
import { useTickerHistory } from './useTickerHistory';
import { extractChatPanelContext } from '../lib/chatPanelContext';
import { mappingsBridge } from '../lib/mappingsBridge';
import { refreshAffectedEarningsPanels } from '../lib/mappingsRefresh';
import type { MappingsUpdatedEvent } from '../types/mappings';
import {
  extractTickerSymbolFromResponse,
  resolveTickerCommandFallback,
} from '../lib/tickerHistory';
import { terminalBridge } from '../lib/terminalBridge';
import {
  AgentStreamItemEvent,
  PortfolioAction,
  PortfolioActionDraft,
  PortfolioActionSeed,
  TerminalCommandResponse,
} from '../types/terminal';

type AppView = 'terminal' | 'research' | 'settings';

export interface PendingAgentApproval {
  approvalId: string;
  command: string;
  requestId: string;
  workspaceId: string;
  title: string;
  message: string;
}

interface UseTerminalOrchestratorArgs {
  tabs: ReturnType<typeof useTabs>;
  tickerHistory: ReturnType<typeof useTickerHistory>;
  portfolioWorkflow: ReturnType<typeof usePortfolioWorkflow>;
  setActiveView: React.Dispatch<React.SetStateAction<AppView>>;
}

export const useTerminalOrchestrator = ({
  tabs,
  tickerHistory,
  portfolioWorkflow,
  setActiveView,
}: UseTerminalOrchestratorArgs) => {
  const [isProcessing, setIsProcessing] = useState(false);
  const [pendingAgentApproval, setPendingAgentApproval] =
    useState<PendingAgentApproval | null>(null);
  const commandHistoryRefs = useRef<Record<string, string[]>>({});
  const commandIndexRefs = useRef<Record<string, number>>({});
  const commandInputRef = useRef<CommandInputHandle | null>(null);
  const refreshInFlightKeysRef = useRef<Set<string>>(new Set());

  const pushCommandHistory = useCallback((workspaceId: string, command: string) => {
    if (!commandHistoryRefs.current[workspaceId]) {
      commandHistoryRefs.current[workspaceId] = [];
    }
    commandHistoryRefs.current[workspaceId].push(command);
    commandIndexRefs.current[workspaceId] = -1;
  }, []);

  const clearWorkspaceSession = useCallback((workspaceId: string) => {
    tabs.clearWorkspace(workspaceId);
    tabs.setWorkspaceSession(workspaceId, undefined);
    commandIndexRefs.current[workspaceId] = -1;
  }, [tabs]);

  const appendResolvedCommandResponse = useCallback(
    (
      workspaceId: string,
      command: string | undefined,
      response: TerminalCommandResponse,
    ) => {
      tabs.appendWorkspaceEntry(
        workspaceId,
        createEntry(
          response.kind === 'text'
            ? { type: 'response', content: response.content }
            : { type: 'panel', content: response.panel },
        ),
      );

      if (command) {
        portfolioWorkflow.noteCommandResponse(workspaceId, command, response);
      }

      const tickerSymbol = extractTickerSymbolFromResponse(response);
      if (tickerSymbol) {
        void tickerHistory.recordTicker(tickerSymbol);
      }
    },
    [portfolioWorkflow, tabs, tickerHistory],
  );

  const replaceEarningsPanelResponse = useCallback(
    (
      workspaceId: string,
      panelEntryId: string,
      response: TerminalCommandResponse,
    ) => {
      tabs.updateWorkspaceEntry(workspaceId, panelEntryId, (entry) => ({
        ...entry,
        content: response.kind === 'panel' ? response.panel : entry.content,
        timestamp: new Date(),
      }));

      const tickerSymbol = extractTickerSymbolFromResponse(response);
      if (tickerSymbol) {
        void tickerHistory.recordTicker(tickerSymbol);
      }
    },
    [tabs, tickerHistory],
  );

  const refreshEarningsPanels = useEffectEvent(
    async (event: MappingsUpdatedEvent) => {
      await refreshAffectedEarningsPanels({
        workspaces: tabs.workspaces,
        event,
        execute: (panel) =>
          terminalBridge.executeTerminalCommand({
            workspaceId: panel.workspaceId,
            input: panel.sourceCommand,
          }),
        replacePanel: (panel, response) => {
          replaceEarningsPanelResponse(panel.workspaceId, panel.panelEntryId, response);
        },
        reportFailure: (panel, reason) => {
          tabs.appendWorkspaceEntry(
            panel.workspaceId,
            createEntry({
              type: 'system',
              content: reason,
            }),
          );
        },
        inFlightKeys: refreshInFlightKeysRef.current,
      });
    },
  );

  const handleCommand = useCallback(async (command: string) => {
    const trimmedCommand = command.trim();
    const latestTicker = tickerHistory.history[0]?.company.symbol;
    const resolvedCommand = resolveTickerCommandFallback(trimmedCommand, latestTicker);
    const workspaceId = tabs.activeWorkspaceId;
    const currentWorkspace = tabs.workspaces.find(
      (workspace) => workspace.id === workspaceId,
    );
    const isSlashCommand = resolvedCommand.startsWith('/');

    if (!resolvedCommand) {
      return;
    }

    setActiveView('terminal');

    if (resolvedCommand === '/clear' || resolvedCommand.toLowerCase() === 'clear') {
      clearWorkspaceSession(workspaceId);
      return;
    }

    pushCommandHistory(workspaceId, resolvedCommand);
    setIsProcessing(true);
    if (isPortfolioCommand(resolvedCommand)) {
      portfolioWorkflow.noteCommandStart(workspaceId, resolvedCommand);
    }

    if (isSlashCommand) {
      const commandEntry = createEntry({ type: 'command', content: resolvedCommand });
      clearWorkspaceSession(workspaceId);
      tabs.appendWorkspaceEntry(workspaceId, commandEntry);

      try {
        const response = await terminalBridge.executeTerminalCommand({
          workspaceId,
          input: resolvedCommand,
        });

        appendResolvedCommandResponse(workspaceId, resolvedCommand, response);
      } catch (error) {
        tabs.appendWorkspaceEntry(
          workspaceId,
          createEntry({
            type: 'error',
            content: error instanceof Error ? error.message : 'Command execution failed.',
          }),
        );
        portfolioWorkflow.noteCommandError(workspaceId, resolvedCommand);
      } finally {
        setIsProcessing(false);
      }

      return;
    }

    const panelContext = extractChatPanelContext(currentWorkspace?.history ?? []);
    const commandEntry = createEntry({ type: 'command', content: resolvedCommand });
    let lastSequenceSeen = 0;
    let activeTextEntryId: string | null = null;
    let activeThinkingEntryId: string | null = null;

    tabs.appendWorkspaceEntry(workspaceId, commandEntry);

    const appendStreamEntry = (
      type: 'response' | 'thinking',
      renderMode: 'plain' | 'markdown',
    ) => {
      const entry = createEntry({
        type,
        content: '',
        renderMode,
      });
      tabs.appendWorkspaceEntry(workspaceId, entry);

      if (type === 'response') {
        activeTextEntryId = entry.id;
      } else {
        activeThinkingEntryId = entry.id;
      }

      return entry.id;
    };

    const closeTextSegment = () => {
      activeTextEntryId = null;
    };

    const closeThinkingSegment = () => {
      activeThinkingEntryId = null;
    };

    const appendDeltaToEntry = (
      entryId: string,
      delta: string,
      renderMode: 'plain' | 'markdown',
    ) => {
      tabs.updateWorkspaceEntry(workspaceId, entryId, (entry) => ({
        ...entry,
        content: typeof entry.content === 'string' ? `${entry.content}${delta}` : delta,
        renderMode,
        timestamp: new Date(),
      }));
    };

    const processStreamItem = (
      event: Omit<AgentStreamItemEvent, 'response'> & {
        response?: TerminalCommandResponse;
      },
    ) => {
      if (event.sequence <= lastSequenceSeen) {
        return;
      }
      lastSequenceSeen = event.sequence;

      switch (event.kind) {
        case 'reasoning_delta': {
          if (!event.delta) {
            return;
          }
          closeTextSegment();
          const entryId =
            activeThinkingEntryId ?? appendStreamEntry('thinking', 'plain');
          appendDeltaToEntry(entryId, event.delta, 'plain');
          return;
        }
        case 'text_delta': {
          if (!event.delta) {
            return;
          }
          closeThinkingSegment();
          const entryId =
            activeTextEntryId ?? appendStreamEntry('response', 'markdown');
          appendDeltaToEntry(entryId, event.delta, 'markdown');
          return;
        }
        case 'tool_command': {
          if (!event.command) {
            return;
          }
          closeThinkingSegment();
          closeTextSegment();
          tabs.appendWorkspaceEntry(
            workspaceId,
            createEntry({ type: 'command', content: event.command }),
          );
          return;
        }
        case 'tool_result': {
          if (!event.command || !event.response) {
            return;
          }
          closeThinkingSegment();
          closeTextSegment();
          appendResolvedCommandResponse(workspaceId, event.command, event.response);
          return;
        }
        case 'approval_required': {
          if (!event.approvalId || !event.command || !event.title || !event.message) {
            return;
          }
          setPendingAgentApproval({
            approvalId: event.approvalId,
            command: event.command,
            requestId: event.requestId,
            workspaceId: event.workspaceId,
            title: event.title,
            message: event.message,
          });
          return;
        }
        case 'stream_complete': {
          tabs.setWorkspaceSession(workspaceId, event.sessionId);
          closeThinkingSegment();
          closeTextSegment();
          setIsProcessing(false);
          return;
        }
        case 'error': {
          const entryId = activeTextEntryId ?? appendStreamEntry('response', 'plain');
          closeThinkingSegment();
          activeTextEntryId = entryId;
          tabs.updateWorkspaceEntry(workspaceId, entryId, (entry) => ({
            ...entry,
            type: 'error',
            content: event.errorMessage ?? 'Chat stream failed.',
            renderMode: 'plain',
            timestamp: new Date(),
          }));
          setIsProcessing(false);
          return;
        }
        default:
          return;
      }
    };

    try {
      const start = await terminalBridge.startChatStream(
        {
          workspaceId,
          sessionId: currentWorkspace?.chatSessionId,
          prompt: resolvedCommand,
          agentProfile: 'toolUse',
          panelContext,
        },
        {
          onStreamItem: processStreamItem,
        },
      );

      tabs.setWorkspaceSession(workspaceId, start.sessionId);
    } catch (error) {
      const entryId = activeTextEntryId ?? appendStreamEntry('response', 'plain');
      tabs.updateWorkspaceEntry(workspaceId, entryId, (entry) => ({
        ...entry,
        type: 'error',
        content: error instanceof Error ? error.message : 'Chat stream failed.',
        renderMode: 'plain',
        timestamp: new Date(),
      }));
      setIsProcessing(false);
    }
  }, [
    appendResolvedCommandResponse,
    clearWorkspaceSession,
    portfolioWorkflow,
    pushCommandHistory,
    setActiveView,
    tabs,
    tickerHistory,
  ]);

  useEffect(() => {
    tabs.workspaces.forEach((workspace) => {
      commandHistoryRefs.current[workspace.id] ??= [];
      commandIndexRefs.current[workspace.id] ??= -1;
    });
  }, [tabs.workspaces]);

  useEffect(() => {
    let disposed = false;
    let unlisten: (() => void) | undefined;

    void mappingsBridge.listenForUpdates((payload) => {
      if (!disposed) {
        void refreshEarningsPanels(payload);
      }
    }).then((listener) => {
      if (disposed) {
        listener();
        return;
      }
      unlisten = listener;
    });

    return () => {
      disposed = true;
      unlisten?.();
    };
  }, [refreshEarningsPanels]);

  const runCommand = useCallback((command: string) => {
    void handleCommand(command);
  }, [handleCommand]);

  const resetCommandIndex = useCallback(() => {
    commandIndexRefs.current[tabs.activeWorkspaceId] = -1;
  }, [tabs.activeWorkspaceId]);

  const resolvePendingAgentApproval = useCallback((approved: boolean) => {
    if (!pendingAgentApproval) {
      return;
    }

    void terminalBridge
      .resolveAgentToolApproval({
        approvalId: pendingAgentApproval.approvalId,
        approved,
      })
      .finally(() => setPendingAgentApproval(null));
  }, [pendingAgentApproval]);

  const handleStartPortfolioAction = useCallback(
    (action: PortfolioAction, seed?: PortfolioActionSeed) => {
      setActiveView('terminal');
      portfolioWorkflow.startPortfolioAction(tabs.activeWorkspaceId, action, seed);
    },
    [portfolioWorkflow, setActiveView, tabs.activeWorkspaceId],
  );

  const handleUpdatePortfolioDraft = useCallback(
    (patch: Partial<PortfolioActionDraft>) => {
      portfolioWorkflow.updateDraft(tabs.activeWorkspaceId, patch);
    },
    [portfolioWorkflow, tabs.activeWorkspaceId],
  );

  const handleClearPortfolioAction = useCallback(() => {
    portfolioWorkflow.clearPortfolioAction(tabs.activeWorkspaceId);
  }, [portfolioWorkflow, tabs.activeWorkspaceId]);

  return {
    commandInputRef,
    clearWorkspaceSession,
    handleClearPortfolioAction,
    handleCommand,
    handleStartPortfolioAction,
    handleUpdatePortfolioDraft,
    isProcessing,
    pendingAgentApproval,
    resetCommandIndex,
    resolvePendingAgentApproval,
    runCommand,
  };
};
