"use client";

import {
  forwardRef,
  useCallback,
  useEffect,
  useId,
  useImperativeHandle,
  useRef,
  useState,
} from "react";
import type { FocusEvent, FormEvent } from "react";

import { DESKTOP_INPUT_DEVICE_QUERY } from "@/lib/input-device";
import KatexRenderer from "@/components/math/KatexRenderer";

export type ActiveAnswerInput = number;

const ANSWER_INPUT_PATTERN = /^-?\d*(?:\.\d*)?$/;
const KEYPAD_KEYS = [
  "1",
  "2",
  "3",
  "4",
  "5",
  "6",
  "7",
  "8",
  "9",
  "+/-",
  "0",
];

export interface NumberAnswerFormHandle {
  focusInput: () => void;
}

interface NumberAnswerFormProps {
  answers: string[];
  activeInput: ActiveAnswerInput;
  error: string;
  pendingAnswerLabels: string[];
  onAnswerChange: (index: ActiveAnswerInput, value: string) => void;
  onActiveInputChange: (input: ActiveAnswerInput) => void;
  onInputBlur?: () => void;
  onInputFocus?: () => void;
  onSubmit: (event: FormEvent<HTMLFormElement>) => void;
}

const NumberAnswerForm = forwardRef<
  NumberAnswerFormHandle,
  NumberAnswerFormProps
>(function NumberAnswerForm(
  {
    answers,
    activeInput,
    error,
    pendingAnswerLabels,
    onAnswerChange,
    onActiveInputChange,
    onInputBlur,
    onInputFocus,
    onSubmit,
  },
  ref,
) {
  const inputId = useId();
  const formRef = useRef<HTMLFormElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const blurTimeoutRef = useRef<number | null>(null);
  const [keypadOpen, setKeypadOpen] = useState(false);
  const [desktopInputDevice, setDesktopInputDevice] = useState(false);

  const activeAnswer = answers[activeInput] ?? "";
  const activeLabel = `${activeInput + 1}항`;
  const activeDescription = `${activeLabel}의 답을 입력하세요.`;
  const lastInputIndex = answers.length - 1;
  const activeEnterKeyHint = activeInput < lastInputIndex ? "next" : "done";

  function updateAnswer(value: string) {
    if (!ANSWER_INPUT_PATTERN.test(value)) return;

    onAnswerChange(activeInput, value);
  }

  function handleInputFocus() {
    if (blurTimeoutRef.current !== null) {
      window.clearTimeout(blurTimeoutRef.current);
      blurTimeoutRef.current = null;
    }

    setKeypadOpen(true);
    onInputFocus?.();
  }

  function handleInputBlur(event: FocusEvent<HTMLInputElement>) {
    const nextFocusedElement = event.relatedTarget;

    if (
      nextFocusedElement instanceof Node &&
      formRef.current?.contains(nextFocusedElement)
    ) {
      return;
    }

    blurTimeoutRef.current = window.setTimeout(() => {
      blurTimeoutRef.current = null;

      if (formRef.current?.contains(document.activeElement)) {
        setKeypadOpen(true);
        return;
      }

      setKeypadOpen(false);
      onInputBlur?.();
    }, 0);
  }

  const focusInput = useCallback(() => {
    const input = inputRef.current;

    if (!input) return;

    const wasFocused = document.activeElement === input;

    input.focus({ preventScroll: true });

    if (wasFocused) {
      onInputFocus?.();
    }
  }, [onInputFocus]);

  useImperativeHandle(ref, () => ({ focusInput }), [focusInput]);

  useEffect(() => {
    const mediaQuery = window.matchMedia(DESKTOP_INPUT_DEVICE_QUERY);
    const updateInputDevice = () => {
      setDesktopInputDevice(mediaQuery.matches);
    };

    updateInputDevice();
    mediaQuery.addEventListener("change", updateInputDevice);

    return () => {
      if (blurTimeoutRef.current !== null) {
        window.clearTimeout(blurTimeoutRef.current);
      }
      mediaQuery.removeEventListener("change", updateInputDevice);
    };
  }, []);

  function toggleMinus() {
    onAnswerChange(
      activeInput,
      activeAnswer.startsWith("-") ? activeAnswer.slice(1) : `-${activeAnswer}`,
    );
    focusInput();
  }

  function appendKeypadValue(value: string) {
    updateAnswer(`${activeAnswer}${value}`);
    focusInput();
  }

  function deleteLastCharacter() {
    updateAnswer(activeAnswer.slice(0, -1));
    focusInput();
  }

  function moveToInput(input: ActiveAnswerInput, reset = false) {
    onActiveInputChange(input);
    if (reset) {
      onAnswerChange(input, "");
    }
    focusInput();
  }

  function handleEnter() {
    if (activeInput < lastInputIndex) {
      moveToInput(activeInput + 1);
      return;
    }

    formRef.current?.requestSubmit();
    focusInput();
  }

  const answerButtons = answers.map((value, index) => ({
    index,
    label: `${index + 1}항`,
    value,
  }));

  return (
    <form
      ref={formRef}
      onSubmit={onSubmit}
      className="rounded-md bg-white p-6 shadow-sm ring-1 ring-slate-200 sm:p-8"
    >
      <div className="flex flex-col gap-4">
        <div className="flex flex-wrap items-center justify-between gap-3">
          <label
            htmlFor={inputId}
            className="text-sm font-semibold text-slate-500"
          >
            {activeDescription}
          </label>
          <ol
            className="flex min-w-40 items-center gap-1"
            aria-label="입력 항 진행 상태"
          >
            {answerButtons.map((button) => {
              const selected = activeInput === button.index;
              const completed = button.value !== "";
              const displayValue =
                button.value || pendingAnswerLabels[button.index];

              return (
                <li
                  key={button.index}
                  className="min-w-0 flex-1"
                  aria-label={`${button.label} ${
                    completed
                      ? `입력값 ${button.value}`
                      : `원래 항 ${displayValue}, 미입력`
                  }${selected ? ", 현재 입력 중" : ""}`}
                >
                  <span
                    className={`block truncate text-center text-xs font-bold ${
                      selected || completed ? "text-emerald-800" : "text-slate-500"
                    }`}
                  >
                    <KatexRenderer latex={displayValue} />
                  </span>
                  <span
                    className={`mt-1 block h-1.5 rounded-full ${
                      completed
                        ? "bg-emerald-700"
                        : selected
                          ? "bg-emerald-300 ring-1 ring-emerald-700"
                          : "bg-slate-200"
                    }`}
                  />
                </li>
              );
            })}
          </ol>
        </div>

        <input
          id={inputId}
          ref={inputRef}
          type="text"
          value={activeAnswer}
          onFocus={handleInputFocus}
          onBlur={handleInputBlur}
          onChange={(event) => updateAnswer(event.target.value)}
          onKeyDown={(event) => {
            if (event.key === "Enter") {
              event.preventDefault();
              handleEnter();
            }
          }}
          autoComplete="off"
          enterKeyHint={activeEnterKeyHint}
          inputMode="none"
          pattern="-?[0-9]*(\.[0-9]*)?"
          className="h-12 w-full min-w-0 rounded-md border border-slate-300 bg-white px-3 text-lg font-semibold text-slate-950 outline-none transition focus:border-emerald-600 focus:ring-2 focus:ring-emerald-200 sm:px-4"
        />

        {keypadOpen && !desktopInputDevice ? (
          <div
            className="grid grid-cols-3 gap-2"
            role="group"
            aria-label={`${activeLabel} 숫자 입력 키패드`}
          >
            {KEYPAD_KEYS.map((key) => (
              <button
                key={key}
                type="button"
                onPointerDown={(event) => event.preventDefault()}
                onClick={() =>
                  key === "+/-" ? toggleMinus() : appendKeypadValue(key)
                }
                aria-label={
                  key === "+/-"
                    ? `${activeLabel} 양수 음수 기호 전환`
                    : `${activeLabel} 숫자 ${key} 입력`
                }
                className="inline-flex h-12 items-center justify-center rounded-md border border-slate-300 bg-white text-lg font-semibold text-slate-900 shadow-sm transition hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2"
              >
                {key}
              </button>
            ))}
            <button
              type="button"
              onPointerDown={(event) => event.preventDefault()}
              onClick={() => appendKeypadValue(".")}
              aria-label={`${activeLabel} 소수점 입력`}
              className="inline-flex h-12 items-center justify-center rounded-md border border-slate-300 bg-white text-lg font-semibold text-slate-900 shadow-sm transition hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2"
            >
              .
            </button>
            <button
              type="button"
              onPointerDown={(event) => event.preventDefault()}
              onClick={deleteLastCharacter}
              aria-label={`${activeLabel} 마지막 글자 삭제`}
              className="inline-flex h-12 items-center justify-center rounded-md border border-slate-300 bg-white text-xl font-semibold text-slate-800 shadow-sm transition hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2"
            >
              ⌫
            </button>
            <button
              type="button"
              onPointerDown={(event) => event.preventDefault()}
              onClick={handleEnter}
              aria-label={
                activeInput < lastInputIndex
                  ? "현재 항 입력 완료 후 다음 항으로 이동"
                  : "마지막 항 입력 완료 후 제출"
              }
              className="col-span-2 inline-flex h-12 items-center justify-center rounded-md bg-emerald-700 text-base font-semibold text-white shadow-sm transition hover:bg-emerald-800 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2"
            >
              ↵
            </button>
          </div>
        ) : null}
      </div>

      {error ? (
        <p className="mt-3 text-sm font-semibold text-red-700">{error}</p>
      ) : null}
    </form>
  );
});

export default NumberAnswerForm;
