"use client";

import { useState, useCallback, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import type { MathProblem, Difficulty } from "@/lib/problems/types";
import {
  checkFractionAnswer,
  checkDecimalAnswer,
  checkRatioAnswer,
  checkIntegerAnswer,
  parseStrictDecimal,
  parseStrictSignedDecimal,
  parseStrictSignedInt,
  wrong,
  SOLVED,
  type AnswerResult,
} from "@/lib/math/validation";
import { FractionInput, DecimalInput, RatioInput } from "./fraction-input";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { MathDisplay } from "@/components/math/math-display";
import katex from "katex";

interface PracticeSectionProps {
  title: string;
  generator: (difficulty: Difficulty) => MathProblem;
  unitColor?: "unit-1" | "unit-2" | "unit-3" | "unit-4" | "unit-5" | "unit-6" | "unit-8";
}

type UnitColor = NonNullable<PracticeSectionProps["unitColor"]>;

const activeDifficultyStyle: Record<UnitColor, string> = {
  "unit-1": "bg-unit-1 text-white",
  "unit-2": "bg-unit-2 text-white",
  "unit-3": "bg-unit-3 text-white",
  "unit-4": "bg-unit-4 text-white",
  "unit-5": "bg-unit-5 text-white",
  "unit-6": "bg-unit-6 text-white",
  "unit-8": "bg-unit-8 text-white",
};

const RECENT_PROBLEM_LIMIT = 8;
const MAX_GENERATION_RETRIES = 12;

function getProblemKey(problem: MathProblem): string {
  return JSON.stringify({
    prompt: problem.prompt,
    answer: problem.answer,
  });
}

export function PracticeSection({ title, generator, unitColor = "unit-1" }: PracticeSectionProps) {
  const recentProblemKeysRef = useRef<string[] | null>(null);
  const [difficulty, setDifficulty] = useState<Difficulty>(1);
  const [problem, setProblem] = useState<MathProblem>(() => generator(1));
  const [userAnswer, setUserAnswer] = useState<Record<string, string>>({});
  const [result, setResult] = useState<AnswerResult | null>(null);
  const [showHint, setShowHint] = useState(false);
  const [hintIndex, setHintIndex] = useState(0);
  const [showSolution, setShowSolution] = useState(false);
  const [score, setScore] = useState({ correct: 0, total: 0 });
  const isFullyCorrect = result?.status === "solved";
  // Per-problem scoring guards: a problem is counted at most once, when solved,
  // and only counts as "correct" if solved with no earlier wrong attempt or peek.
  const missedCurrentRef = useRef(false);
  const scoredCurrentRef = useRef(false);

  const generateUniqueProblem = useCallback((diff: Difficulty): MathProblem => {
    const recentProblemKeys = recentProblemKeysRef.current ?? [getProblemKey(problem)];
    let nextProblem = generator(diff);
    let nextKey = getProblemKey(nextProblem);
    let retries = 0;

    while (
      recentProblemKeys.includes(nextKey) &&
      retries < MAX_GENERATION_RETRIES
    ) {
      nextProblem = generator(diff);
      nextKey = getProblemKey(nextProblem);
      retries += 1;
    }

    recentProblemKeysRef.current = [
      nextKey,
      ...recentProblemKeys.filter((key) => key !== nextKey),
    ].slice(0, RECENT_PROBLEM_LIMIT);

    return nextProblem;
  }, [generator, problem]);

  const generateNew = useCallback((diff: Difficulty) => {
    setProblem(generateUniqueProblem(diff));
    setUserAnswer({});
    setResult(null);
    setShowHint(false);
    setHintIndex(0);
    setShowSolution(false);
    missedCurrentRef.current = false;
    scoredCurrentRef.current = false;
  }, [generateUniqueProblem]);

  function checkAnswer() {
    const answer = problem.answer;
    let res: AnswerResult;

    switch (answer.kind) {
      case "fraction": {
        const num = parseStrictSignedInt(userAnswer.numerator || "");
        const den = parseStrictSignedInt(userAnswer.denominator || "");
        if (num === null || den === null) {
          res = wrong("Enter valid numbers");
        } else {
          res = checkFractionAnswer(num, den, answer.numerator, answer.denominator);
        }
        break;
      }
      case "decimal": {
        const val = parseStrictSignedDecimal(userAnswer.value || "");
        if (val === null) {
          res = wrong("Enter a valid number");
        } else {
          res = checkDecimalAnswer(val, answer.value);
        }
        break;
      }
      case "integer": {
        const val = parseStrictSignedInt(userAnswer.value || "");
        if (val === null) {
          res = wrong("Enter a valid number");
        } else {
          res = checkIntegerAnswer(val, answer.value);
        }
        break;
      }
      case "ratio": {
        const parts = (userAnswer.ratio || "").split(":").map((p) => parseInt(p.trim()));
        if (parts.some(isNaN)) {
          res = wrong("Enter valid ratio parts");
        } else {
          res = checkRatioAnswer(parts, answer.parts);
        }
        break;
      }
      case "standardForm": {
        const coeff = parseStrictDecimal(userAnswer.coefficient || "");
        const exp = parseStrictSignedInt(userAnswer.exponent || "");
        if (coeff === null || exp === null) {
          res = wrong("Enter valid numbers");
        } else if (
          Math.abs(coeff - answer.coefficient) < 0.01 &&
          exp === answer.exponent
        ) {
          res = SOLVED;
        } else {
          res = wrong("That's not quite right. Try again!");
        }
        break;
      }
    }

    setResult(res);

    // Score each problem exactly once, when it is solved. A wrong attempt (or an
    // "unsimplified" nudge that never gets finished) never scores on its own; it
    // just marks the current problem as missed so the eventual solve counts as
    // incorrect. This makes the score "problems solved / first-time right" and
    // makes double-counting impossible.
    if (res.status === "wrong") {
      missedCurrentRef.current = true;
    } else if (res.status === "solved" && !scoredCurrentRef.current) {
      scoredCurrentRef.current = true;
      const gotFirstTime = !missedCurrentRef.current;
      setScore((s) => ({
        correct: s.correct + (gotFirstTime ? 1 : 0),
        total: s.total + 1,
      }));
    }
  }

  // Enter key: check the answer, or move to the next problem once it's solved.
  function submit() {
    if (isFullyCorrect) generateNew(difficulty);
    else checkAnswer();
  }

  function nextHint() {
    if (!showHint) {
      setShowHint(true);
      return;
    }
    if (hintIndex < problem.hints.length - 1) {
      setHintIndex((i) => i + 1);
    }
  }

  const promptHtml = katex.renderToString(problem.prompt, { throwOnError: false, displayMode: true });

  return (
    <Card className="mt-8 border-2">
      <div className="mb-4 flex items-center justify-between">
        <h3 className="text-lg font-extrabold">{title}</h3>
        <div className="flex items-center gap-2 text-sm">
          <span className="rounded-full bg-background px-2.5 py-1 font-bold text-muted">
            {score.correct}/{score.total}
          </span>
          <div className="flex gap-1">
            {([1, 2, 3] as Difficulty[]).map((d) => (
              <button
                key={d}
                onClick={() => {
                  setDifficulty(d);
                  generateNew(d);
                }}
                className={`rounded-lg px-2.5 py-1 text-xs font-extrabold transition-colors ${
                  difficulty === d
                    ? activeDifficultyStyle[unitColor]
                    : "bg-border/70 text-muted hover:bg-border hover:text-foreground"
                }`}
              >
                L{d}
              </button>
            ))}
          </div>
        </div>
      </div>

      {/* Problem */}
      <div
        className="mb-6 text-center text-xl"
        dangerouslySetInnerHTML={{ __html: promptHtml }}
      />

      {/* Input */}
      <div className="mb-4 flex flex-wrap items-center justify-center gap-4">
        {problem.answer.kind === "fraction" && (
          <FractionInput
            numerator={userAnswer.numerator || ""}
            denominator={userAnswer.denominator || ""}
            onNumeratorChange={(v) => setUserAnswer((a) => ({ ...a, numerator: v }))}
            onDenominatorChange={(v) => setUserAnswer((a) => ({ ...a, denominator: v }))}
            onSubmit={submit}
            disabled={isFullyCorrect}
          />
        )}

        {(problem.answer.kind === "decimal" || problem.answer.kind === "integer") && (
          <DecimalInput
            value={userAnswer.value || ""}
            onChange={(v) => setUserAnswer((a) => ({ ...a, value: v }))}
            onSubmit={submit}
            disabled={isFullyCorrect}
          />
        )}

        {problem.answer.kind === "ratio" && (() => {
          const ratioParts = (problem.answer as { kind: "ratio"; parts: number[] }).parts;
          return (
            <RatioInput
              parts={
                userAnswer.ratioParts
                  ? JSON.parse(userAnswer.ratioParts)
                  : ratioParts.map(() => "")
              }
              onChange={(i, v) => {
                const current = userAnswer.ratioParts
                  ? JSON.parse(userAnswer.ratioParts)
                  : ratioParts.map(() => "");
                current[i] = v;
                setUserAnswer((a) => ({
                  ...a,
                  ratioParts: JSON.stringify(current),
                  ratio: current.join(":"),
                }));
              }}
              disabled={isFullyCorrect}
            />
          );
        })()}

        {problem.answer.kind === "standardForm" && (
          <div className="flex items-center gap-1">
            <input
              type="text"
              inputMode="decimal"
              value={userAnswer.coefficient || ""}
              onChange={(e) => setUserAnswer((a) => ({ ...a, coefficient: e.target.value }))}
              disabled={isFullyCorrect}
              className="w-16 rounded-lg border border-border bg-surface px-2 py-2 text-center font-bold focus:border-unit-2 focus:outline-none"
              placeholder="?"
              aria-label="Coefficient"
            />
            <span className="text-lg font-bold">x 10</span>
            <input
              type="text"
              inputMode="numeric"
              value={userAnswer.exponent || ""}
              onChange={(e) => setUserAnswer((a) => ({ ...a, exponent: e.target.value }))}
              disabled={isFullyCorrect}
              className="w-12 -translate-y-2 rounded-lg border border-border bg-surface px-2 py-1 text-center text-sm font-bold focus:border-unit-2 focus:outline-none"
              placeholder="?"
              aria-label="Exponent"
            />
          </div>
        )}
      </div>

      {/* Actions */}
      <div className="mb-4 flex flex-wrap justify-center gap-3">
        {!isFullyCorrect && (
          <>
            <Button variant={unitColor} size="sm" onClick={checkAnswer}>
              Check Answer
            </Button>
            <Button variant="secondary" size="sm" onClick={nextHint}>
              Hint
            </Button>
            <Button
              variant="ghost"
              size="sm"
              onClick={() => {
                missedCurrentRef.current = true;
                setShowSolution(true);
              }}
            >
              Show Solution
            </Button>
          </>
        )}
        {isFullyCorrect && (
          <Button variant={unitColor} size="sm" onClick={() => generateNew(difficulty)}>
            Next Problem
          </Button>
        )}
      </div>

      {/* Feedback */}
      <AnimatePresence>
        {result && (
          <motion.div
            initial={{ opacity: 0, y: -10 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0 }}
            className={`rounded-xl px-4 py-3 text-center text-sm font-medium ${
              result.status === "solved"
                ? "bg-correct-light text-correct"
                : result.status === "unsimplified"
                  ? "bg-hint-light text-hint"
                  : "bg-incorrect-light text-incorrect"
            }`}
          >
            {result.status === "solved"
              ? "Correct!"
              : result.status === "unsimplified"
                ? "Correct, but can you simplify further?"
                : result.message}
          </motion.div>
        )}
      </AnimatePresence>

      {/* Hint */}
      <AnimatePresence>
        {showHint && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            className="mt-3 rounded-xl bg-hint-light px-4 py-3 text-sm text-hint"
          >
            {problem.hints[hintIndex]}
          </motion.div>
        )}
      </AnimatePresence>

      {/* Solution */}
      <AnimatePresence>
        {showSolution && (
          <motion.div
            initial={{ opacity: 0, height: 0 }}
            animate={{ opacity: 1, height: "auto" }}
            className="mt-3 space-y-2 rounded-xl bg-background p-4"
          >
            <p className="text-xs font-semibold uppercase text-muted">Solution</p>
            {problem.steps.map((step, i) => (
              <div key={i} className="flex gap-2 text-sm">
                <span className="shrink-0 font-bold text-muted">{i + 1}.</span>
                <div className="min-w-0 flex-1 space-y-1">
                  <div>{step.explanation}</div>
                  {step.math && <MathDisplay math={step.math} className="text-lg" />}
                </div>
              </div>
            ))}
          </motion.div>
        )}
      </AnimatePresence>
    </Card>
  );
}
