"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import type { FormEvent } from "react";
import { flushSync } from "react-dom";

import AnswerForm from "@/components/game/AnswerForm";
import type {
  ActiveAnswerInput,
  AnswerFormHandle,
} from "@/components/game/AnswerForm";
import FeedbackPanel from "@/components/game/FeedbackPanel";
import ScoreSummary from "@/components/game/ScoreSummary";
import StageIndicator from "@/components/game/StageIndicator";
import { DESKTOP_INPUT_DEVICE_QUERY } from "@/lib/input-device";
import KatexRenderer from "@/components/math/KatexRenderer";
import { useGameStore } from "@/store/gameStore";
import type { ProblemCount } from "@/store/gameStore";

const REAL_NUMBER_PATTERN = /^-?\d+(?:\.\d+)?$/;

function parseAnswer(value: string) {
  const trimmed = value.trim();

  if (!REAL_NUMBER_PATTERN.test(trimmed)) return null;

  return Number(trimmed);
}

interface NumberPlayClientProps {
  problemCount: ProblemCount;
}

export default function NumberPlayClient({
  problemCount,
}: NumberPlayClientProps) {
  const session = useGameStore((state) => state.session);
  const selectedProblemCount = useGameStore(
    (state) => state.selectedProblemCount,
  );
  const setProblemCount = useGameStore((state) => state.setProblemCount);
  const initSession = useGameStore((state) => state.initSession);
  const submitAnswer = useGameStore((state) => state.submitAnswer);
  const nextProblem = useGameStore((state) => state.nextProblem);
  const resetSession = useGameStore((state) => state.resetSession);

  const problemCardRef = useRef<HTMLDivElement>(null);
  const answerFormRef = useRef<AnswerFormHandle>(null);

  const [answerA, setAnswerA] = useState("");
  const [answerB, setAnswerB] = useState("");
  const [activeInput, setActiveInput] = useState<ActiveAnswerInput>(0);
  const [inputError, setInputError] = useState("");

  const sessionProblemCount = session?.problems.length ?? null;
  const currentProblem = session?.problems[session.currentIndex] ?? null;
  const currentResult = session?.results[session.currentIndex] ?? null;
  const isComplete = Boolean(
    session && session.currentIndex >= session.problems.length,
  );
  const sessionNeedsInit = sessionProblemCount !== problemCount;
  const correctCount =
    session?.results.filter((result) => result?.correct).length ?? 0;
  const totalCount = session?.problems.length ?? 0;

  useEffect(() => {
    if (selectedProblemCount !== problemCount) {
      setProblemCount(problemCount);
    }

    if (sessionNeedsInit) {
      initSession(problemCount);
    }
  }, [
    initSession,
    problemCount,
    selectedProblemCount,
    sessionProblemCount,
    sessionNeedsInit,
    setProblemCount,
  ]);

  useEffect(() => {
    setAnswerA("");
    setAnswerB("");
    setActiveInput(0);
    setInputError("");
  }, [currentProblem?.key]);

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    if (currentResult || !currentProblem) return;

    const parsedA = parseAnswer(answerA);
    const parsedB = parseAnswer(answerB);

    if (parsedA === null || parsedB === null) {
      setInputError("두 칸 모두 실수로 입력하세요.");
      return;
    }

    setInputError("");
    submitAnswer({ a: parsedA, b: parsedB });
  }

  function handleNextProblem() {
    flushSync(() => {
      nextProblem();
    });
    answerFormRef.current?.focusInput();
  }

  const scrollKatexToViewportTop = useCallback(() => {
    if (window.matchMedia(DESKTOP_INPUT_DEVICE_QUERY).matches) return;

    function alignKatex() {
      const katexElement =
        problemCardRef.current?.querySelector<HTMLElement>("span.katex");

      if (!katexElement) return;

      const rect = katexElement.getBoundingClientRect();
      const scrollElement =
        document.scrollingElement ?? document.documentElement;
      const elementDocumentTop = window.scrollY + rect.top;
      const maxScrollTop = Math.max(
        0,
        scrollElement.scrollHeight - window.innerHeight,
      );
      const scrollTop = Math.min(
        maxScrollTop,
        Math.max(0, elementDocumentTop),
      );

      if (Math.abs(window.scrollY - scrollTop) < 4) return;

      scrollElement.scrollTop = scrollTop;
      document.body.scrollTop = scrollTop;
      window.scrollTo(0, scrollTop);
    }

    window.requestAnimationFrame(() => {
      window.requestAnimationFrame(alignKatex);
    });
  }, []);

  if (!session || sessionNeedsInit) {
    return (
      <main className="flex min-h-screen items-center bg-slate-50 px-6 py-12 text-slate-950">
        <section className="mx-auto w-full max-w-3xl">
          <p className="text-lg font-semibold text-slate-700">
            문제를 준비하고 있습니다.
          </p>
        </section>
      </main>
    );
  }

  if (isComplete) {
    return (
      <ScoreSummary
        correctCount={correctCount}
        totalCount={totalCount}
        problems={session.problems}
        results={session.results}
        onRestart={resetSession}
      />
    );
  }

  if (!currentProblem) {
    return (
      <main className="flex min-h-screen items-center bg-slate-50 px-6 py-12 text-slate-950">
        <section className="mx-auto w-full max-w-3xl">
          <p className="text-lg font-semibold text-slate-700">
            현재 문제를 불러올 수 없습니다.
          </p>
        </section>
      </main>
    );
  }

  const submitted = currentResult !== null;
  const isLastProblem = session.currentIndex === session.problems.length - 1;

  return (
    <main className="min-h-screen bg-slate-50 px-6 py-10 text-slate-950">
      <section className="mx-auto flex w-full max-w-3xl flex-col gap-8">
        <div className="flex flex-wrap items-center justify-between gap-4">
          <div className="space-y-2">
            <p className="text-sm font-semibold text-emerald-700">
              괄호 음수 분배
            </p>
            <h1 className="text-3xl font-bold tracking-normal sm:text-4xl">
              문제 풀이
            </h1>
          </div>
        </div>

        <div
          ref={problemCardRef}
          className="rounded-md bg-white px-6 pt-6 pb-4 shadow-sm ring-1 ring-slate-200 sm:p-8"
        >
          <p className="mb-4 text-sm font-semibold text-slate-500">
            괄호를 풀어 두 항을 순서대로 입력하세요.
          </p>
          <div className="text-3xl font-semibold text-slate-950 sm:text-4xl">
            <KatexRenderer latex={currentProblem.latexBefore} displayMode />
          </div>
          <div className="mt-6">
            <StageIndicator
              currentIndex={session.currentIndex}
              results={session.results}
            />
          </div>
        </div>

        {!submitted ? (
          <AnswerForm
            ref={answerFormRef}
            answerA={answerA}
            answerB={answerB}
            activeInput={activeInput}
            error={inputError}
            onActiveInputChange={setActiveInput}
            onAnswerAChange={setAnswerA}
            onAnswerBChange={setAnswerB}
            onInputFocus={scrollKatexToViewportTop}
            onSubmit={handleSubmit}
          />
        ) : null}

        {currentResult ? (
          <FeedbackPanel
            result={currentResult}
            problem={currentProblem}
            isLastProblem={isLastProblem}
            onNext={handleNextProblem}
          />
        ) : null}
      </section>
    </main>
  );
}
