import { useCallback, useEffect } from 'react';
import { useWorkspace } from '../../components/Workspace';
import { useActiveChatSession, useFilesRefresh, usePreviewRefresh } from '../../channels';
import { useChat } from '../../hooks/useChat';
import { EmbeddableChat } from './EmbeddableChat';
import { AgentIdentityBar } from './AgentIdentityBar';
import { useAgentPanel, type UseAgentPanelType } from './useAgentPanel';

type ChatPanelInnerProps = {
  scoped: boolean;
  cwdParam?: { root?: string; path: string };
  promptPrefix?: string;
  chatContext: Record<string, string | undefined>;
  setActiveSession: (id: string | null) => void;
  onTurnComplete?: (hadToolCalls: boolean) => void;
  sessionKey?: string;
  agentPanel: UseAgentPanelType;
};

const ChatPanelInner = ({
  scoped,
  cwdParam,
  promptPrefix,
  chatContext,
  setActiveSession,
  onTurnComplete,
  sessionKey,
  agentPanel,
}: ChatPanelInnerProps) => {
  const chat = useChat(sessionKey, undefined, {
    replaceUrl: false,
    projectScoped: scoped,
    onTurnComplete,
    ...chatContext,
  });

  useEffect(() => {
    setActiveSession(chat.sessionId);
  }, [chat.sessionId]);

  return (
    <div className="flex h-full min-h-0 flex-col">
      <AgentIdentityBar panel={agentPanel} />
      <EmbeddableChat
        className="min-h-0 flex-1"
        chat={chat}
        cwd={cwdParam}
        replaceUrl={false}
        promptPrefix={promptPrefix}
        {...chatContext}
      />
    </div>
  );
};

// `promptPrefix` is a system prompt only this app understands, so the screen that wants one passes it
// here rather than through the workspace context — a screen supplies its own chat panel through
// `components`, which is what that prop exists for. The registry mounts this with no prefix.
type ChatPanelWrapperProps = { panelId: string; promptPrefix?: string };

export const ChatPanelWrapper = ({ panelId, promptPrefix }: ChatPanelWrapperProps) => {
  const { workspace, cwd, root } = useWorkspace();
  const scoped = cwd !== '~';

  // `contextId` stays the raw workspace key: it is the same string `agent_panels.dashboard_id` holds, and
  // a stored address should not be two different strings depending on which feature wrote it. The bare id
  // is for deciding, which is what `kind` and `id` are doing above it.
  const chatContext =
    workspace?.kind === 'screen' && workspace.id === 'email'
      ? { context: 'email' as const }
      : workspace?.kind === 'dashboard'
        ? { context: 'dashboard' as const, contextId: workspace.key }
        : {};

  const agentPanel = useAgentPanel(panelId);

  const [, setActiveSession] = useActiveChatSession();
  const [, setPreviewRefresh] = usePreviewRefresh();
  const [, bumpFilesRefresh] = useFilesRefresh();

  const onTurnComplete = useCallback(
    (hadToolCalls: boolean) => {
      if (hadToolCalls) {
        setPreviewRefresh(Date.now());
        bumpFilesRefresh();
      }
    },
    [setPreviewRefresh, bumpFilesRefresh],
  );

  // A named agent's own directory wins over the dashboard's. It has to: `deliverToAgentPanel` runs an
  // incoming handoff in `resolveBaseCwd(email, row.cwd)`, so if a turn the human types ran anywhere else
  // the same agent would be working in two directories depending on who spoke to it. Both paths are the
  // same home-relative string resolved by the same function.
  const cwdParam = agentPanel.agent?.cwd ? { path: agentPanel.agent.cwd } : scoped ? { root, path: cwd } : undefined;

  // A named panel is one session forever, so its key has to be decided before `useChat` mounts — the
  // hook seeds its state from the first `initialSessionId` it sees and the socket's resume handshake
  // sends whatever is in that state on connect. Remounting on the key is the honest way to say "this is
  // now a different conversation": it happens exactly twice in a panel's life, when it is named and if
  // it is released, and both are the user asking for precisely that.
  const key = agentPanel.agent?.sessionKey ?? 'anonymous';

  // Until the address book has answered, a named panel must not mount an anonymous chat — that would
  // start a throwaway session and stream the answer into a transcript nobody can find again.
  if (agentPanel.isLoading) return null;

  return (
    <ChatPanelInner
      key={key}
      scoped={scoped}
      cwdParam={cwdParam}
      promptPrefix={promptPrefix}
      chatContext={chatContext}
      setActiveSession={setActiveSession}
      onTurnComplete={onTurnComplete}
      sessionKey={agentPanel.agent?.sessionKey}
      agentPanel={agentPanel}
    />
  );
};
