import figures from 'figures';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { useTerminalSize } from '../../../hooks/useTerminalSize.js';
import { type KeyboardEvent, Box, Text } from '@anthropic/ink';
import { useKeybinding, useKeybindings } from '../../../keybindings/useKeybinding.js';
import { useAppState } from '../../../state/AppState.js';
import type { Question } from '@claude-code-best/builtin-tools/tools/AskUserQuestionTool/AskUserQuestionTool.js';
import { getExternalEditor } from '../../../utils/editor.js';
import { toIDEDisplayName } from '../../../utils/ide.js';
import { editPromptInEditor } from '../../../utils/promptEditor.js';
import { Divider } from '@anthropic/ink';
import TextInput from '../../TextInput.js';
import { PermissionRequestTitle } from '../PermissionRequestTitle.js';
import { PreviewBox } from './PreviewBox.js';
import { QuestionNavigationBar } from './QuestionNavigationBar.js';
import type { QuestionState } from './use-multiple-choice-state.js';

type Props = {
  question: Question;
  questions: Question[];
  currentQuestionIndex: number;
  answers: Record<string, string>;
  questionStates: Record<string, QuestionState>;
  hideSubmitTab?: boolean;
  minContentHeight?: number;
  minContentWidth?: number;
  onUpdateQuestionState: (questionText: string, updates: Partial<QuestionState>, isMultiSelect: boolean) => void;
  onAnswer: (questionText: string, label: string | string[], textInput?: string, shouldAdvance?: boolean) => void;
  onTextInputFocus: (isInInput: boolean) => void;
  onCancel: () => void;
  onTabPrev?: () => void;
  onTabNext?: () => void;
  onRespondToClaude: () => void;
  onFinishPlanInterview: () => void;
};

/**
 * A side-by-side question view for questions with preview content.
 * Displays a vertical option list on the left with a preview panel on the right.
 */
export function PreviewQuestionView({
  question,
  questions,
  currentQuestionIndex,
  answers,
  questionStates,
  hideSubmitTab = false,
  minContentHeight,
  minContentWidth,
  onUpdateQuestionState,
  onAnswer,
  onTextInputFocus,
  onCancel,
  onTabPrev,
  onTabNext,
  onRespondToClaude,
  onFinishPlanInterview,
}: Props): React.ReactNode {
  const isInPlanMode = useAppState(s => s.toolPermissionContext.mode) === 'plan';
  const [isFooterFocused, setIsFooterFocused] = useState(false);
  const [footerIndex, setFooterIndex] = useState(0);
  const [isInNotesInput, setIsInNotesInput] = useState(false);
  const [cursorOffset, setCursorOffset] = useState(0);

  const editor = getExternalEditor();
  const editorName = editor ? toIDEDisplayName(editor) : null;

  const questionText = question.question;
  const questionState = questionStates[questionText];

  // Only real options — no "Other" for preview questions
  const allOptions = question.options;

  // Track which option is focused (for preview display)
  const [focusedIndex, setFocusedIndex] = useState(0);

  // Reset focusedIndex when navigating to a different question
  const prevQuestionText = useRef(questionText);
  if (prevQuestionText.current !== questionText) {
    prevQuestionText.current = questionText;
    const selected = questionState?.selectedValue as string | undefined;
    const idx = selected ? allOptions.findIndex(opt => opt.label === selected) : -1;
    setFocusedIndex(idx >= 0 ? idx : 0);
  }

  const focusedOption = allOptions[focusedIndex];
  const selectedValue = questionState?.selectedValue as string | undefined;
  const notesValue = questionState?.textInputValue || '';

  const handleSelectOption = useCallback(
    (index: number) => {
      const option = allOptions[index];
      if (!option) return;

      setFocusedIndex(index);
      onUpdateQuestionState(questionText, { selectedValue: option.label }, false);

      onAnswer(questionText, option.label);
    },
    [allOptions, questionText, onUpdateQuestionState, onAnswer],
  );

  const handleNavigate = useCallback(
    (direction: 'up' | 'down' | number) => {
      if (isInNotesInput) return;

      let newIndex: number;
      if (typeof direction === 'number') {
        newIndex = direction;
      } else if (direction === 'up') {
        newIndex = focusedIndex > 0 ? focusedIndex - 1 : focusedIndex;
      } else {
        newIndex = focusedIndex < allOptions.length - 1 ? focusedIndex + 1 : focusedIndex;
      }

      if (newIndex >= 0 && newIndex < allOptions.length) {
        setFocusedIndex(newIndex);
      }
    },
    [focusedIndex, allOptions.length, isInNotesInput],
  );

  // Handle ctrl+g to open external editor for notes
  useKeybinding(
    'chat:externalEditor',
    async () => {
      const currentValue = questionState?.textInputValue || '';
      const result = await editPromptInEditor(currentValue);
      if (result.content !== null && result.content !== currentValue) {
        onUpdateQuestionState(questionText, { textInputValue: result.content }, false);
      }
    },
    { context: 'Chat', isActive: isInNotesInput && !!editor },
  );

  // Handle left/right arrow and tab for question navigation.
  // This must be in the child component (not just the parent) because child useInput
  // handlers register first on the event emitter and fire before parent handlers.
  // Without this, the parent's useKeybindings may not fire reliably depending on
  // listener ordering in the event emitter.
  useKeybindings(
    {
      'tabs:previous': () => onTabPrev?.(),
      'tabs:next': () => onTabNext?.(),
    },
    { context: 'Tabs', isActive: !isInNotesInput && !isFooterFocused },
  );

  // Re-submit the answer (plain label) when exiting notes input.
  // Notes are stored in questionStates and collected at submit time via annotations.
  const handleNotesExit = useCallback(() => {
    setIsInNotesInput(false);
    onTextInputFocus(false);
    if (selectedValue) {
      onAnswer(questionText, selectedValue);
    }
  }, [selectedValue, questionText, onAnswer, onTextInputFocus]);

  const handleDownFromPreview = useCallback(() => {
    setIsFooterFocused(true);
  }, []);

  const handleUpFromFooter = useCallback(() => {
    setIsFooterFocused(false);
  }, []);

  // Handle keyboard input for option/footer/notes navigation.
  // Always active — the handler routes internally based on isFooterFocused/isInNotesInput.
  const handleKeyDown = useCallback(
    (e: KeyboardEvent) => {
      if (isFooterFocused) {
        if (e.key === 'up' || (e.ctrl && e.key === 'p')) {
          e.preventDefault();
          if (footerIndex === 0) {
            handleUpFromFooter();
          } else {
            setFooterIndex(0);
          }
          return;
        }

        if (e.key === 'down' || (e.ctrl && e.key === 'n')) {
          e.preventDefault();
          if (isInPlanMode && footerIndex === 0) {
            setFooterIndex(1);
          }
          return;
        }

        if (e.key === 'return') {
          e.preventDefault();
          if (footerIndex === 0) {
            onRespondToClaude();
          } else {
            onFinishPlanInterview();
          }
          return;
        }

        if (e.key === 'escape') {
          e.preventDefault();
          onCancel();
        }
        return;
      }

      if (isInNotesInput) {
        // In notes input mode, handle escape to exit back to option navigation
        if (e.key === 'escape') {
          e.preventDefault();
          handleNotesExit();
        }
        return;
      }

      // Handle option navigation (vertical)
      if (e.key === 'up' || (e.ctrl && e.key === 'p')) {
        e.preventDefault();
        if (focusedIndex > 0) {
          handleNavigate('up');
        }
      } else if (e.key === 'down' || (e.ctrl && e.key === 'n')) {
        e.preventDefault();
        if (focusedIndex === allOptions.length - 1) {
          // At bottom of options, go to footer
          handleDownFromPreview();
        } else {
          handleNavigate('down');
        }
      } else if (e.key === 'return') {
        e.preventDefault();
        handleSelectOption(focusedIndex);
      } else if (e.key === 'n' && !e.ctrl && !e.meta) {
        // Press 'n' to focus the notes input
        e.preventDefault();
        setIsInNotesInput(true);
        onTextInputFocus(true);
      } else if (e.key === 'escape') {
        e.preventDefault();
        onCancel();
      } else if (e.key.length === 1 && e.key >= '1' && e.key <= '9') {
        e.preventDefault();
        const idx = parseInt(e.key, 10) - 1;
        if (idx < allOptions.length) {
          handleNavigate(idx);
        }
      }
    },
    [
      isFooterFocused,
      footerIndex,
      isInPlanMode,
      isInNotesInput,
      focusedIndex,
      allOptions.length,
      handleUpFromFooter,
      handleDownFromPreview,
      handleNavigate,
      handleSelectOption,
      handleNotesExit,
      onRespondToClaude,
      onFinishPlanInterview,
      onCancel,
      onTextInputFocus,
    ],
  );

  const previewContent = focusedOption?.preview || null;

  // The right panel's available width is terminal minus the left panel and gap.
  const LEFT_PANEL_WIDTH = 30;
  const GAP = 4;
  const { columns } = useTerminalSize();
  const previewMaxWidth = columns - LEFT_PANEL_WIDTH - GAP;

  // Lines used within the content area that aren't preview content:
  // 1: marginTop on side-by-side box
  // 2: PreviewBox borders (top + bottom)
  // 2: notes section (marginTop=1 + text)
  // 2: footer section (marginTop=1 + divider)
  // 1: "Chat about this" line
  // 1: plan mode line (may or may not show)
  // 2: help text (marginTop=1 + text)
  const PREVIEW_OVERHEAD = 11;

  // Compute the max lines available for preview content from the parent's
  // height budget to prevent terminal overflow. We do NOT pad shorter options
  // to match the tallest — the outer box's minHeight handles cross-question
  // layout consistency, and within-question shifts are acceptable.
  const previewMaxLines = useMemo(() => {
    return minContentHeight ? Math.max(1, minContentHeight - PREVIEW_OVERHEAD) : undefined;
  }, [minContentHeight]);

  return (
    <Box flexDirection="column" marginTop={1} tabIndex={0} autoFocus onKeyDown={handleKeyDown}>
      <Divider color="inactive" />
      <Box flexDirection="column" paddingTop={0}>
        <QuestionNavigationBar
          questions={questions}
          currentQuestionIndex={currentQuestionIndex}
          answers={answers}
          hideSubmitTab={hideSubmitTab}
        />
        <PermissionRequestTitle title={question.question} color={'text'} />

        <Box flexDirection="column" minHeight={minContentHeight}>
          {/* Side-by-side layout: options on left, preview on right */}
          <Box marginTop={1} flexDirection="row" gap={4}>
            {/* Left panel: vertical option list */}
            <Box flexDirection="column" width={30}>
              {allOptions.map((option, index) => {
                const isFocused = focusedIndex === index;
                const isSelected = selectedValue === option.label;

                return (
                  <Box key={option.label} flexDirection="row">
                    {isFocused ? <Text color="suggestion">{figures.pointer}</Text> : <Text> </Text>}
                    <Text dimColor> {index + 1}.</Text>
                    <Text color={isSelected ? 'success' : isFocused ? 'suggestion' : undefined} bold={isFocused}>
                      {' '}
                      {option.label}
                    </Text>
                    {isSelected && <Text color="success"> {figures.tick}</Text>}
                  </Box>
                );
              })}
            </Box>

            {/* Right panel: preview + notes */}
            <Box flexDirection="column" flexGrow={1}>
              <PreviewBox
                content={previewContent || 'No preview available'}
                maxLines={previewMaxLines}
                minWidth={minContentWidth}
                maxWidth={previewMaxWidth}
              />
              <Box marginTop={1} flexDirection="row" gap={1}>
                <Text color="suggestion">Notes:</Text>
                {isInNotesInput ? (
                  <TextInput
                    value={notesValue}
                    placeholder="Add notes on this design…"
                    onChange={value => {
                      onUpdateQuestionState(questionText, { textInputValue: value }, false);
                    }}
                    onSubmit={handleNotesExit}
                    onExit={handleNotesExit}
                    focus={true}
                    showCursor={true}
                    columns={60}
                    cursorOffset={cursorOffset}
                    onChangeCursorOffset={setCursorOffset}
                  />
                ) : (
                  <Text dimColor italic>
                    {notesValue || 'press n to add notes'}
                  </Text>
                )}
              </Box>
            </Box>
          </Box>

          {/* Footer section */}
          <Box flexDirection="column" marginTop={1}>
            <Divider color="inactive" />
            <Box flexDirection="row" gap={1}>
              {isFooterFocused && footerIndex === 0 ? (
                <Text color="suggestion">{figures.pointer}</Text>
              ) : (
                <Text> </Text>
              )}
              <Text color={isFooterFocused && footerIndex === 0 ? 'suggestion' : undefined}>Chat about this</Text>
            </Box>
            {isInPlanMode && (
              <Box flexDirection="row" gap={1}>
                {isFooterFocused && footerIndex === 1 ? (
                  <Text color="suggestion">{figures.pointer}</Text>
                ) : (
                  <Text> </Text>
                )}
                <Text color={isFooterFocused && footerIndex === 1 ? 'suggestion' : undefined}>
                  Skip interview and plan immediately
                </Text>
              </Box>
            )}
          </Box>
          <Box marginTop={1}>
            <Text color="inactive" dimColor>
              Enter to select · {figures.arrowUp}/{figures.arrowDown} to navigate · n to add notes
              {questions.length > 1 && <> · Tab to switch questions</>}
              {isInNotesInput && editorName && <> · ctrl+g to edit in {editorName}</>} · Esc to cancel
            </Text>
          </Box>
        </Box>
      </Box>
    </Box>
  );
}
