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

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

interface ChatHistoryHandle {
  recordChat: (entry: { prompt: string; transcript: import('../types/terminal').TerminalEntry[]; completedAt?: Date }) => void;
}

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

const COMMAND_FAILURE_MESSAGE = 'Command execution failed.';
const CHAT_FAILURE_MESSAGE = 'Chat stream failed.';
const CHAT_CANCELLED_MESSAGE = 'Chat cancelled.';

export const useTerminalOrchestrator = ({
  tabs,
  tickerHistory,
  portfolioWorkflow,
  setActiveView,
  chatHistory,
}: UseTerminalOrchestratorArgs) => {
  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 workspacesRef = useRef<Workspace[]>(tabs.workspaces);

  useEffect(() => {
    workspacesRef.current = tabs.workspaces;
  }, [tabs.workspaces]);

  const findWorkspace = useCallback((workspaceId: string) => {
    return workspacesRef.current.find((workspace) => workspace.id === workspaceId) ?? null;
  }, []);

  const isActiveRequest = useCallback(
    (workspaceId: string, requestId: string) => {
      const workspace = findWorkspace(workspaceId);
      return workspace?.terminal.activeRequest?.requestId === requestId;
    },
    [findWorkspace],
  );

  const finishWorkspaceRequest = useCallback(
    (workspaceId: string, requestId: string) => {
      if (!isActiveRequest(workspaceId, requestId)) {
        return false;
      }

      tabs.updateWorkspaceTerminal(workspaceId, (terminal) => ({
        ...terminal,
        isProcessing: false,
        activeRequest: null,
        pendingApproval: null,
      }));
      return true;
    },
    [isActiveRequest, tabs],
  );

  const appendWorkspaceSystemMessage = useCallback(
    (workspaceId: string, content: string) => {
      if (!findWorkspace(workspaceId)) {
        return;
      }

      tabs.appendWorkspaceEntry(
        workspaceId,
        createEntry({
          type: 'system',
          content,
        }),
      );
    },
    [findWorkspace, tabs],
  );

  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);
    commandIndexRefs.current[workspaceId] = -1;
  }, [tabs]);

  const appendResolvedCommandResponse = useCallback(
    (
      workspaceId: string,
      command: string | undefined,
      response: TerminalCommandResponse,
    ): string | null => {
      if (!findWorkspace(workspaceId)) {
        return null;
      }

      const entry = createEntry(
        response.kind === 'text'
          ? { type: 'response', content: response.content }
          : { type: 'panel', content: response.panel },
      );
      tabs.appendWorkspaceEntry(workspaceId, entry);

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

      const tickerSymbol = extractTickerSymbolFromResponse(response);
      if (tickerSymbol) {
        void tickerHistory.recordTicker(tickerSymbol);
      }

      return entry.id;
    },
    [findWorkspace, portfolioWorkflow, tabs, tickerHistory],
  );

  const replacePanelResponse = useCallback(
    (
      workspaceId: string,
      panelEntryId: string,
      response: TerminalCommandResponse,
    ) => {
      if (!findWorkspace(workspaceId)) {
        return;
      }

      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);
      }
    },
    [findWorkspace, tabs, tickerHistory],
  );

  const refreshPanels = useEffectEvent(async (event: MappingsUpdatedEvent) => {
    await refreshAffectedEarningsPanels({
      workspaces: workspacesRef.current,
      event,
      execute: (panel) =>
        terminalBridge.executeTerminalCommand({
          workspaceId: panel.workspaceId,
          input: panel.sourceCommand,
        }),
      replacePanel: (panel, response) => {
        replacePanelResponse(panel.workspaceId, panel.panelEntryId, response);
      },
      reportFailure: (panel, reason) => {
        appendWorkspaceSystemMessage(panel.workspaceId, reason);
      },
      inFlightKeys: refreshInFlightKeysRef.current,
    });

    await refreshAffectedFinancialsPanels({
      workspaces: workspacesRef.current,
      event,
      execute: (panel) =>
        terminalBridge.executeTerminalCommand({
          workspaceId: panel.workspaceId,
          input: panel.sourceCommand,
        }),
      replacePanel: (panel, response) => {
        replacePanelResponse(panel.workspaceId, panel.panelEntryId, response);
      },
      reportFailure: (panel, reason) => {
        appendWorkspaceSystemMessage(panel.workspaceId, reason);
      },
      inFlightKeys: refreshInFlightKeysRef.current,
    });
  });

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

      if (!resolvedCommand || !currentWorkspace || currentWorkspace.terminal.isProcessing) {
        return;
      }

      setActiveView('terminal');

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

      pushCommandHistory(workspaceId, resolvedCommand);
      tabs.setWorkspaceInputDraft(workspaceId, '');
      tabs.setWorkspacePendingApproval(workspaceId, null);

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

      if (isSlashCommand) {
        const requestId = createTerminalRequestId();
        const commandEntry = createEntry({ type: 'command', content: resolvedCommand });

        clearWorkspaceSession(workspaceId);
        tabs.appendWorkspaceEntry(workspaceId, commandEntry);
        tabs.setWorkspaceProcessing(workspaceId, true);
        tabs.setWorkspaceActiveRequest(workspaceId, { kind: 'slash', requestId });

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

          if (!finishWorkspaceRequest(workspaceId, requestId)) {
            return;
          }

          appendResolvedCommandResponse(workspaceId, resolvedCommand, response);
        } catch (error) {
          if (!finishWorkspaceRequest(workspaceId, requestId)) {
            return;
          }

          tabs.appendWorkspaceEntry(
            workspaceId,
            createEntry({
              type: 'error',
              content:
                error instanceof Error ? error.message : COMMAND_FAILURE_MESSAGE,
            }),
          );
          portfolioWorkflow.noteCommandError(workspaceId, resolvedCommand);
        }

        return;
      }

      const requestId = createTerminalRequestId();
      const panelContext = extractChatPanelContext(currentWorkspace.terminal.history);
      const commandEntry = createEntry({ type: 'command', content: resolvedCommand });
      let lastSequenceSeen = 0;
      let activeTextEntryId: string | null = null;
      let activeThinkingEntryId: string | null = null;
      const chatTranscriptEntryIds = new Set<string>();

      chatTranscriptEntryIds.add(commandEntry.id);
      tabs.appendWorkspaceEntry(workspaceId, commandEntry);
      tabs.setWorkspaceProcessing(workspaceId, true);
      tabs.setWorkspaceActiveRequest(workspaceId, { kind: 'chat', requestId });

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

        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 updateErrorEntry = (message: string) => {
        const entryId = activeTextEntryId ?? appendStreamEntry('response', 'plain');
        closeThinkingSegment();
        activeTextEntryId = entryId;
        tabs.updateWorkspaceEntry(workspaceId, entryId, (entry) => ({
          ...entry,
          type: 'error',
          content: message,
          renderMode: 'plain',
          timestamp: new Date(),
        }));
      };

      const processStreamItem = (
        event: Omit<AgentStreamItemEvent, 'response'> & {
          response?: TerminalCommandResponse;
        },
      ) => {
        if (!isActiveRequest(workspaceId, requestId) || 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();
            const toolEntry = createEntry({ type: 'command', content: event.command });
            tabs.appendWorkspaceEntry(workspaceId, toolEntry);
            chatTranscriptEntryIds.add(toolEntry.id);
            return;
          }
          case 'tool_result': {
            if (!event.command || !event.response) {
              return;
            }
            closeThinkingSegment();
            closeTextSegment();
            const resultEntryId = appendResolvedCommandResponse(workspaceId, event.command, event.response);
            if (resultEntryId) {
              chatTranscriptEntryIds.add(resultEntryId);
            }
            return;
          }
          case 'approval_required': {
            if (!event.approvalId || !event.command || !event.title || !event.message) {
              return;
            }

            const approval: PendingAgentApproval = {
              approvalId: event.approvalId,
              command: event.command,
              requestId: event.requestId,
              workspaceId: event.workspaceId,
              title: event.title,
              message: event.message,
            };
            tabs.setWorkspacePendingApproval(workspaceId, approval);
            return;
          }
          case 'stream_complete': {
            tabs.setWorkspaceSession(workspaceId, event.sessionId);
            closeThinkingSegment();
            closeTextSegment();
            finishWorkspaceRequest(workspaceId, requestId);

            if (chatHistory) {
              const ws = findWorkspace(workspaceId);
              if (ws) {
                const transcript = ws.terminal.history.filter((e) =>
                  chatTranscriptEntryIds.has(e.id),
                );
                chatHistory.recordChat({
                  prompt: resolvedCommand,
                  transcript,
                });
              }
            }
            return;
          }
          case 'cancelled': {
            closeThinkingSegment();
            closeTextSegment();
            if (finishWorkspaceRequest(workspaceId, requestId)) {
              appendWorkspaceSystemMessage(workspaceId, CHAT_CANCELLED_MESSAGE);
            }
            return;
          }
          case 'error': {
            updateErrorEntry(event.errorMessage ?? CHAT_FAILURE_MESSAGE);
            chatTranscriptEntryIds.add(activeTextEntryId ?? '');
            finishWorkspaceRequest(workspaceId, requestId);

            if (chatHistory) {
              const ws = findWorkspace(workspaceId);
              if (ws) {
                const transcript = ws.terminal.history.filter((e) =>
                  chatTranscriptEntryIds.has(e.id),
                );
                if (transcript.some((e) => e.type === 'response' || e.type === 'error' || e.type === 'panel')) {
                  chatHistory.recordChat({
                    prompt: resolvedCommand,
                    transcript,
                  });
                }
              }
            }
            return;
          }
          default:
            return;
        }
      };

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

        if (isActiveRequest(workspaceId, requestId)) {
          tabs.setWorkspaceSession(workspaceId, start.sessionId);
        }
      } catch (error) {
        if (!isActiveRequest(workspaceId, requestId)) {
          return;
        }

        updateErrorEntry(error instanceof Error ? error.message : CHAT_FAILURE_MESSAGE);
        finishWorkspaceRequest(workspaceId, requestId);
      }
    },
    [
      appendResolvedCommandResponse,
      appendWorkspaceSystemMessage,
      chatHistory,
      clearWorkspaceSession,
      findWorkspace,
      finishWorkspaceRequest,
      isActiveRequest,
      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 refreshPanels(payload);
        }
      })
      .then((listener) => {
        if (disposed) {
          listener();
          return;
        }
        unlisten = listener;
      });

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

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

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

  const resolvePendingAgentApproval = useCallback(
    (workspaceId: string, approved: boolean) => {
      const workspace = findWorkspace(workspaceId);
      const pendingApproval = workspace?.terminal.pendingApproval;
      if (!pendingApproval) {
        return;
      }

      void terminalBridge
        .resolveAgentToolApproval({
          approvalId: pendingApproval.approvalId,
          approved,
        })
        .finally(() => {
          const latestWorkspace = findWorkspace(workspaceId);
          if (
            latestWorkspace?.terminal.pendingApproval?.approvalId ===
            pendingApproval.approvalId
          ) {
            tabs.setWorkspacePendingApproval(workspaceId, null);
          }
        });
    },
    [findWorkspace, tabs],
  );

  const cancelWorkspaceRequest = useCallback(
    async (workspaceId: string) => {
      const workspace = findWorkspace(workspaceId);
      const activeRequest = workspace?.terminal.activeRequest;
      if (!activeRequest || activeRequest.kind !== 'chat') {
        return;
      }

      await terminalBridge.cancelChatStream({
        workspaceId,
        requestId: activeRequest.requestId,
      });
    },
    [findWorkspace],
  );

  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 {
    cancelWorkspaceRequest,
    commandInputRef,
    clearWorkspaceSession,
    handleClearPortfolioAction,
    handleCommand,
    handleStartPortfolioAction,
    handleUpdatePortfolioDraft,
    resetCommandIndex,
    resolvePendingAgentApproval,
    runCommand,
  };
};
