import { Box, Text, useInput } from "ink";
import { Fragment, memo, useMemo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { colors } from "./colors";

interface QuestionOption {
  label: string;
  description: string;
}

interface Question {
  question: string;
  header: string;
  options: QuestionOption[];
  multiSelect: boolean;
}

type Props = {
  questions: Question[];
  onSubmit: (answers: Record<string, string>) => void;
  onCancel?: () => void;
  isFocused?: boolean;
};

// Horizontal line character for Claude Code style
const SOLID_LINE = "─";

export const InlineQuestionApproval = memo(
  ({ questions, onSubmit, onCancel, isFocused = true }: Props) => {
    const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
    const [answers, setAnswers] = useState<Record<string, string>>({});
    const [selectedOption, setSelectedOption] = useState(0);
    const {
      text: customText,
      setText: setCustomText,
      cursorPos,
      setCursorPos,
      handleKey,
      clear: clearCustomText,
    } = useTextInputCursor();
    const [selectedMulti, setSelectedMulti] = useState<Set<number>>(new Set());
    const columns = useTerminalWidth();
    useProgressIndicator();

    const currentQuestion = questions[currentQuestionIndex];

    // Build options list: regular options + "Type something"
    // For multi-select, we also track a separate "Submit" action
    const baseOptions = currentQuestion
      ? [
          ...currentQuestion.options,
          { label: "Type something.", description: "" },
        ]
      : [];

    // For multi-select, add Submit as a separate selectable item
    const optionsWithOther = currentQuestion?.multiSelect
      ? [...baseOptions, { label: "Submit", description: "" }]
      : baseOptions;

    const customOptionIndex = baseOptions.length - 1; // "Type something" index
    const submitOptionIndex = currentQuestion?.multiSelect
      ? optionsWithOther.length - 1
      : -1; // Submit index (only for multi-select)

    const isOnCustomOption = selectedOption === customOptionIndex;
    const isOnSubmitOption = selectedOption === submitOptionIndex;

    const handleSubmitAnswer = (answer: string) => {
      if (!currentQuestion) return;
      const newAnswers = {
        ...answers,
        [currentQuestion.question]: answer,
      };
      setAnswers(newAnswers);

      if (currentQuestionIndex < questions.length - 1) {
        setCurrentQuestionIndex(currentQuestionIndex + 1);
        setSelectedOption(0);
        clearCustomText();
        setSelectedMulti(new Set());
      } else {
        onSubmit(newAnswers);
      }
    };

    useInput(
      (input, key) => {
        if (!isFocused || !currentQuestion) return;

        // CTRL-C: cancel
        if (key.ctrl && input === "c") {
          onCancel?.();
          return;
        }

        // Arrow navigation always works
        if (key.upArrow) {
          setSelectedOption((prev) => Math.max(0, prev - 1));
          return;
        }
        if (key.downArrow || key.tab) {
          setSelectedOption((prev) =>
            Math.min(optionsWithOther.length - 1, prev + 1),
          );
          return;
        }

        // When on custom input option ("Type something")
        if (isOnCustomOption) {
          if (key.return) {
            // Enter toggles the checkbox (same as other options)
            if (currentQuestion.multiSelect) {
              setSelectedMulti((prev) => {
                const newSet = new Set(prev);
                if (newSet.has(customOptionIndex)) {
                  newSet.delete(customOptionIndex);
                } else {
                  newSet.add(customOptionIndex);
                }
                return newSet;
              });
            } else {
              // Single-select: submit the custom text if any
              if (customText.trim()) {
                handleSubmitAnswer(customText.trim());
              }
            }
            return;
          }
          if (input === " " && currentQuestion.multiSelect) {
            // Space in multi-select: toggle checkbox if not checked, then insert space
            if (!selectedMulti.has(customOptionIndex)) {
              setSelectedMulti((prev) => {
                const newSet = new Set(prev);
                newSet.add(customOptionIndex);
                return newSet;
              });
            }
            // Insert space at cursor position
            setCustomText(
              (prev) => `${prev.slice(0, cursorPos)} ${prev.slice(cursorPos)}`,
            );
            setCursorPos((prev) => prev + 1);
            return;
          }
          if (key.escape) {
            if (customText) {
              clearCustomText();
            } else {
              onCancel?.();
            }
            return;
          }
          // Handle text input (arrows, backspace, typing)
          if (handleKey(input, key)) return;
        }

        // When on Submit option (multi-select only)
        if (isOnSubmitOption) {
          if (key.return) {
            // Submit the selected options + custom text if "Type something" is checked
            const selectedLabels: string[] = [];
            for (const i of selectedMulti) {
              if (i === customOptionIndex) {
                // Include custom text if checkbox is checked and text was entered
                if (customText.trim()) {
                  selectedLabels.push(customText.trim());
                }
              } else {
                const label = baseOptions[i]?.label;
                if (label) {
                  selectedLabels.push(label);
                }
              }
            }
            if (selectedLabels.length > 0) {
              handleSubmitAnswer(selectedLabels.join(", "));
            }
            return;
          }
          if (key.escape) {
            onCancel?.();
            return;
          }
          return;
        }

        // ESC on regular options: cancel
        if (key.escape) {
          onCancel?.();
          return;
        }

        // Enter behavior depends on single vs multi-select
        if (key.return) {
          if (currentQuestion.multiSelect) {
            // Multi-select: Enter toggles the checkbox (only for regular options, not custom)
            if (selectedOption < customOptionIndex) {
              setSelectedMulti((prev) => {
                const newSet = new Set(prev);
                if (newSet.has(selectedOption)) {
                  newSet.delete(selectedOption);
                } else {
                  newSet.add(selectedOption);
                }
                return newSet;
              });
            }
          } else {
            // Single-select: Enter selects and submits
            handleSubmitAnswer(optionsWithOther[selectedOption]?.label || "");
          }
          return;
        }

        // Space also toggles for multi-select (like Claude Code) - only regular options
        if (input === " " && currentQuestion.multiSelect) {
          if (selectedOption < customOptionIndex) {
            setSelectedMulti((prev) => {
              const newSet = new Set(prev);
              if (newSet.has(selectedOption)) {
                newSet.delete(selectedOption);
              } else {
                newSet.add(selectedOption);
              }
              return newSet;
            });
          }
          return;
        }

        // Number keys for quick selection
        if (input >= "1" && input <= "9") {
          const optionIndex = Number.parseInt(input, 10) - 1;
          if (optionIndex < optionsWithOther.length - 1) {
            if (currentQuestion.multiSelect) {
              setSelectedMulti((prev) => {
                const newSet = new Set(prev);
                if (newSet.has(optionIndex)) {
                  newSet.delete(optionIndex);
                } else {
                  newSet.add(optionIndex);
                }
                return newSet;
              });
            } else {
              handleSubmitAnswer(optionsWithOther[optionIndex]?.label || "");
            }
          }
        }
      },
      { isActive: isFocused },
    );

    // Generate horizontal line
    const solidLine = SOLID_LINE.repeat(Math.max(columns - 2, 10));

    // Memoize the static header content so it doesn't re-render on keystroke
    // This prevents flicker when typing in the custom input field
    const memoizedHeaderContent = useMemo(
      () => (
        <>
          {/* Top solid line */}
          <Text dimColor>{solidLine}</Text>

          {/* Header label */}
          <Text>{currentQuestion?.header}</Text>

          <Box height={1} />

          {/* Question */}
          <Text bold>{currentQuestion?.question}</Text>

          <Box height={1} />

          {/* Progress indicator for multiple questions */}
          {questions.length > 1 && (
            <Box marginBottom={1}>
              <Text dimColor>
                Question {currentQuestionIndex + 1} of {questions.length}
              </Text>
            </Box>
          )}
        </>
      ),
      [
        currentQuestion?.header,
        currentQuestion?.question,
        currentQuestionIndex,
        questions.length,
        solidLine,
      ],
    );

    // Hint text based on state - keep consistent to avoid jarring changes
    const hintText = currentQuestion?.multiSelect
      ? "Enter to toggle · Arrow to navigate · Esc to cancel"
      : "Enter to select · Arrow to navigate · Esc to cancel";

    if (!currentQuestion) return null;

    return (
      <Box flexDirection="column">
        {/* Static header content - memoized to prevent re-render on keystroke */}
        {memoizedHeaderContent}

        {/* Options - Format: ❯ N. [ ] Label (selector, number, checkbox, label) */}
        <Box flexDirection="column">
          {optionsWithOther.map((option, index) => {
            const isSelected = index === selectedOption;
            const isChecked = selectedMulti.has(index);
            const color = isSelected ? colors.approval.header : undefined;
            const isCustomOption = index === customOptionIndex;
            const isSubmitOption = index === submitOptionIndex;

            // Calculate prefix width: "❯ N. " = 5 chars, "[ ] " = 4 chars for multi-select
            const selectorAndNumber = 5; // "❯ N. " or "  N. "
            const checkboxWidth = currentQuestion.multiSelect ? 4 : 0; // "[ ] " or nothing
            const prefixWidth = selectorAndNumber + checkboxWidth;

            // Submit option renders differently (selector + always bold "Submit")
            if (isSubmitOption) {
              return (
                <Box key="submit" flexDirection="column">
                  {/* Extra newline above Submit */}
                  <Box height={1} />
                  <Box flexDirection="row">
                    <Box width={selectorAndNumber} flexShrink={0}>
                      <Text color={color}>
                        {isSelected ? "❯" : " "}
                        {"    "}
                      </Text>
                    </Box>
                    <Box flexGrow={1}>
                      <Text bold color={color}>
                        Submit
                      </Text>
                    </Box>
                  </Box>
                </Box>
              );
            }

            const hasDescription = option.description && !isCustomOption;

            // Use Fragment to avoid column Box wrapper - render row and description as siblings
            // Note: Can't use <> shorthand with key, so we import Fragment
            return (
              <Fragment key={`${option.label}-${index}`}>
                <Box flexDirection="row">
                  {/* Selector and number */}
                  <Box width={selectorAndNumber} flexShrink={0}>
                    <Text color={color}>
                      {isSelected ? "❯" : " "} {index + 1}.
                    </Text>
                  </Box>
                  {/* Checkbox (for multi-select) - single Text element to avoid re-mount */}
                  {currentQuestion.multiSelect && (
                    <Box width={checkboxWidth} flexShrink={0}>
                      <Text color={isChecked ? "green" : color}>
                        [{isChecked ? "✓" : " "}]{" "}
                      </Text>
                    </Box>
                  )}
                  {/* Label */}
                  <Box flexGrow={1} width={Math.max(0, columns - prefixWidth)}>
                    {isCustomOption ? (
                      // Custom input option ("Type something")
                      customText ? (
                        <Text wrap="wrap">
                          {customText.slice(0, cursorPos)}
                          {isSelected && "█"}
                          {customText.slice(cursorPos)}
                        </Text>
                      ) : (
                        <Text wrap="wrap" dimColor>
                          {option.label}
                          {isSelected && "█"}
                        </Text>
                      )
                    ) : (
                      <Text wrap="wrap" color={color} bold={isSelected}>
                        {option.label}
                      </Text>
                    )}
                  </Box>
                </Box>
                {/* Description - rendered as sibling row */}
                {hasDescription && (
                  <Box paddingLeft={prefixWidth}>
                    <Text dimColor>{option.description}</Text>
                  </Box>
                )}
              </Fragment>
            );
          })}

          <Box marginTop={1}>
            <Text dimColor>{hintText}</Text>
          </Box>
        </Box>
      </Box>
    );
  },
);

InlineQuestionApproval.displayName = "InlineQuestionApproval";
