import { useEffect, useMemo, useRef } from 'react';
import { useParams, useNavigate } from 'react-router';
import type { LayoutNode, SelectedSession } from 'officerdev';
import { WorkspaceView } from 'officerdev';
import { useIsMobile } from 'hooks/useIsMobile';
import { useClient } from 'hooks/useClient';
import { useDashboardState } from 'state/useDashboardState';
import type { ClaudeSessionDetail } from 'state/useClaudeSessions';
import { usePanelChannel } from 'hooks/usePanelChannel';
import { defaultLayout } from './defaultLayout';

// Allowed panel app types for the /chat screen
const ALLOWED_APP_TYPES = new Set<string | null>(['chat-session-list', 'chat-detail', null]);

// How many messages to render on first open (anchored to the bottom); scroll-up pages older ones in.
const CHAT_TAIL = 20;

/** Recursively fix any panel that uses a wrong app type (e.g. officerdev/chat) */
function normalizeLayout(node: LayoutNode): LayoutNode {
  if (node.type === 'panel') {
    if (!ALLOWED_APP_TYPES.has(node.appType)) {
      return { ...node, appType: 'chat-detail' };
    }
    return node;
  }
  const children = node.children.map((c) => {
    const fixed = normalizeLayout(c.node);
    return fixed === c.node ? c : { ...c, node: fixed };
  });
  const changed = children.some((c, i) => c !== node.children[i]);
  return changed ? { ...node, children } : node;
}

type SessionListPageProps = {
  isNew?: boolean;
};

export const SessionListPage = ({ isNew }: SessionListPageProps) => {
  const { sessionId } = useParams<{ sessionId: string }>();
  const [selected, setSelected] = usePanelChannel<SelectedSession>('chat:selected-session', null);
  const [, setActiveCwd] = usePanelChannel<string | null>('chat:active-cwd', null);
  const client = useClient();
  const selectedRef = useRef(selected);
  selectedRef.current = selected;
  const rawWorkspace = useDashboardState<LayoutNode>('screens/chat', defaultLayout);
  const isMobile = useIsMobile();
  const navigate = useNavigate();
  const mobilePanelId = isMobile && (sessionId || isNew) ? 'chat-detail' : undefined;

  // Normalize synchronously so the wrong panel never renders
  const workspace = useMemo(() => {
    const fixed = normalizeLayout(rawWorkspace.value);
    if (fixed === rawWorkspace.value) return rawWorkspace;
    return { ...rawWorkspace, value: fixed };
  }, [rawWorkspace]);

  // Persist the fix to the backend
  useEffect(() => {
    if (rawWorkspace.isLoaded && workspace.value !== rawWorkspace.value) {
      rawWorkspace.setValue(workspace.value);
    }
  }, [rawWorkspace.isLoaded, workspace.value, rawWorkspace.value]);

  // Fresh /chat/<id> (deep-link or refresh): all we have is the id. Resolve the session by id — the
  // backend scans project groups — so the cwd picker lands on its real dir AND the chat resumes, exactly
  // as clicking it from the list would. Guarded so it never clobbers an already-loaded selection.
  // Transcripts get extremely long, so we load only the tail window (?limit) and anchor to the bottom;
  // scrolling up lazy-loads older messages. `total`/`offset` tell the chat where the window sits.
  useEffect(() => {
    if (isNew) {
      setSelected({ id: `new:${Date.now()}` });
      return;
    }
    if (!sessionId) return;
    if (selectedRef.current?.id === sessionId && selectedRef.current.resumeSessionId) return;
    let cancelled = false;
    (async () => {
      try {
        const detail = await client.get<ClaudeSessionDetail>(`/chat/sessions/${sessionId}?limit=${CHAT_TAIL}`);
        if (cancelled) return;
        setActiveCwd(detail.cwd || null);
        setSelected({
          id: sessionId,
          model: detail.model,
          resumeSessionId: sessionId,
          initialMessages: detail.messages as unknown as NonNullable<SelectedSession>['initialMessages'],
          total: detail.total,
          initialOffset: detail.offset,
        });
      } catch {
        if (!cancelled) setSelected({ id: sessionId });
      }
    })();
    return () => {
      cancelled = true;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [sessionId, isNew]);

  return (
    <div className="h-full w-full pt-2">
      <WorkspaceView
        workspace={workspace}
        locked
        mobilePanelId={mobilePanelId}
        onMobilePanelChange={(id) => {
          if (!id) navigate('/chat', { replace: true });
        }}
      />
    </div>
  );
};
