"use client";

import Link from "next/link";
import { useEffect, useMemo, useRef, useState } from "react";
import { motion, AnimatePresence } from "motion/react";

import type { FrcsQuestionWithContext } from "@/lib/frcs";
import {
  formatDuration,
  makeQuizId,
  saveQuizResult,
  type AnswerDetail,
  type QuizResult,
} from "./quiz-storage";

type Mode = "random" | "part" | "review";

type Props = {
  questions: FrcsQuestionWithContext[];
  mode: Mode;
  partSlug?: string;
  partTitle?: string;
  retryHref: string;
};

type AnswerRecord = {
  chosen: string | null; // letter A-E
  correct: string;
  timeMs: number;
};

/**
 * Map an option's *index* (0..4) to its letter label (A..E).
 *
 * The bank stores option bodies without "A." / "B." prefixes, so the letter
 * is purely positional. Deriving it from the index (rather than the option
 * text) avoids two bugs: options whose bodies happen to start with the same
 * character colliding under the same React key, and the letter displayed to
 * the user not matching the `answer` field.
 */
const LETTERS = ["A", "B", "C", "D", "E"] as const;

function stripLetter(option: string): string {
  // Defensive: if an option DOES happen to have an "A. " prefix (e.g. from
  // a legacy import), strip it so we don't render "A. A. Foo".
  return option.replace(/^[A-E][\.\)]\s*/, "");
}

export function QuizRunner({
  questions,
  mode,
  partSlug,
  partTitle,
  retryHref,
}: Props) {
  const total = questions.length;
  const [index, setIndex] = useState(0);
  const [selected, setSelected] = useState<string | null>(null);
  const [revealed, setRevealed] = useState(false);
  const [answers, setAnswers] = useState<AnswerRecord[]>([]);
  const [finished, setFinished] = useState(false);
  const [savedId, setSavedId] = useState<string | null>(null);

  const startRef = useRef<number>(Date.now());
  const questionStartRef = useRef<number>(Date.now());
  // Scrolled to the top of the current question on every advance so the user
  // doesn't end up stuck at the bottom of the previous explanation on mobile.
  const questionTopRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    startRef.current = Date.now();
    questionStartRef.current = Date.now();
  }, []);

  useEffect(() => {
    // Runs after each question change. `block: "start"` aligns the top of the
    // question card with the viewport top; smooth behaviour keeps the
    // transition feeling continuous with the AnimatePresence slide.
    if (questionTopRef.current) {
      questionTopRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
    }
  }, [index]);

  const current = questions[index];
  const currentCorrect = useMemo(
    () => (current ? current.answer.trim().toUpperCase().slice(0, 1) : ""),
    [current],
  );

  if (total === 0) {
    return (
      <div className="rounded-xl border border-[var(--color-line)] bg-white p-8 text-center">
        <p className="text-[15px] text-[var(--color-slate)]">
          No questions available for this selection.
        </p>
        <Link
          href="/products/orthoprep/quiz"
          className="mt-5 inline-block font-semibold text-[var(--color-blue)] underline"
        >
          ← Back to quiz hub
        </Link>
      </div>
    );
  }

  const correctCount = answers.filter((a) => a.chosen === a.correct).length;

  function handleSelect(letter: string) {
    if (revealed) return;
    setSelected(letter);
  }

  function handleSubmit() {
    if (!selected || revealed) return;
    setRevealed(true);
  }

  function handleNext() {
    if (!current) return;
    const timeMs = Date.now() - questionStartRef.current;
    const rec: AnswerRecord = {
      chosen: selected,
      correct: currentCorrect,
      timeMs,
    };
    const nextAnswers = [...answers, rec];
    setAnswers(nextAnswers);

    if (index + 1 >= total) {
      // finalise
      const durationMs = Date.now() - startRef.current;
      const correct = nextAnswers.filter((a) => a.chosen === a.correct).length;
      const answerDetails: AnswerDetail[] = questions.map((q, i) => ({
        pmid: q.number,
        correct: nextAnswers[i]?.chosen === nextAnswers[i]?.correct,
      }));
      const result: QuizResult = {
        id: makeQuizId(),
        takenAt: new Date().toISOString(),
        mode,
        partSlug,
        partTitle,
        questionCount: total,
        correctCount: correct,
        durationMs,
        questionNumbers: questions.map((q) => q.number),
        v: 2,
        answers: answerDetails,
      };
      saveQuizResult(result);
      setSavedId(result.id);
      setFinished(true);
    } else {
      setIndex(index + 1);
      setSelected(null);
      setRevealed(false);
      questionStartRef.current = Date.now();
    }
  }

  if (finished) {
    const durationMs = Date.now() - startRef.current;
    const pct = Math.round((correctCount / total) * 100);
    return (
      <ResultScreen
        correctCount={correctCount}
        total={total}
        pct={pct}
        durationMs={durationMs}
        questions={questions}
        answers={answers}
        retryHref={retryHref}
        saved={savedId !== null}
      />
    );
  }

  if (!current) return null;

  const progressPct = Math.round(((index + (revealed ? 1 : 0)) / total) * 100);

  return (
    <div>
      {/* Scroll anchor — placed above the progress bar so advancing a question
          brings the progress + question into view on mobile. Has `scroll-mt`
          to clear the sticky header on the question-bank → session route. */}
      <div ref={questionTopRef} aria-hidden className="scroll-mt-24" />
      {/* Progress + meta */}
      <div className="mb-6">
        <div className="flex items-center justify-between text-[12px] font-semibold text-[var(--color-slate)]">
          <span>
            Question {index + 1} of {total}
          </span>
          <span>
            Score so far: {correctCount}/{index}
          </span>
        </div>
        <div className="mt-2 h-1.5 overflow-hidden rounded-full bg-[var(--color-mist)]">
          <motion.div
            className="h-full bg-[var(--color-blue)]"
            initial={false}
            animate={{ width: `${progressPct}%` }}
            transition={{ duration: 0.35, ease: "easeOut" }}
          />
        </div>
      </div>

      <AnimatePresence mode="wait">
        <motion.article
          key={current.number}
          initial={{ opacity: 0, y: 8 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -8 }}
          transition={{ duration: 0.25 }}
          className="rounded-xl border border-[var(--color-line)] bg-white p-7 shadow-[0_1px_0_var(--color-line)]"
        >
          <div className="flex flex-wrap items-center gap-2 text-[11px] font-bold uppercase tracking-wider">
            <span className="text-[var(--color-blue)]">
              Q{current.number}
            </span>
            <span className="text-[var(--color-line)]">•</span>
            <span className="text-[var(--color-slate)]">
              {current.partTitle}
            </span>
            <span className="text-[var(--color-line)]">•</span>
            <span className="text-[var(--color-slate)]">
              {current.chapterTitle}
            </span>
          </div>

          <p className="mt-4 text-[17px] leading-relaxed text-[var(--color-ink)]">
            {current.stem}
          </p>

          <ul className="mt-6 space-y-2.5">
            {current.options.slice(0, 5).map((opt, idx) => {
              const letter = LETTERS[idx];
              const isSelected = selected === letter;
              const isCorrect = currentCorrect === letter;

              let state = "idle";
              if (revealed) {
                if (isCorrect) state = "correct";
                else if (isSelected) state = "wrong";
                else state = "muted";
              } else if (isSelected) {
                state = "selected";
              }

              const classes: Record<string, string> = {
                idle: "border-[var(--color-line)] bg-white hover:border-[var(--color-blue)] hover:bg-[var(--color-mist)]/40",
                selected:
                  "border-[var(--color-blue)] bg-[var(--color-mist)]/60 ring-1 ring-[var(--color-blue)]",
                correct:
                  "border-emerald-500 bg-emerald-50 text-emerald-900 ring-1 ring-emerald-500",
                wrong:
                  "border-red-400 bg-red-50 text-red-900 ring-1 ring-red-400",
                muted:
                  "border-[var(--color-line)] bg-white opacity-60",
              };

              return (
                <li key={`${current.number}-${idx}`}>
                  <button
                    type="button"
                    onClick={() => handleSelect(letter)}
                    disabled={revealed}
                    className={`flex w-full items-start gap-3 rounded-lg border px-4 py-3 text-left text-[15px] leading-relaxed transition-all ${classes[state]}`}
                  >
                    <span className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full border border-current text-[12px] font-bold">
                      {letter}
                    </span>
                    <span>{stripLetter(opt)}</span>
                  </button>
                </li>
              );
            })}
          </ul>

          {revealed && (
            <motion.div
              initial={{ opacity: 0, y: 6 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.25 }}
              className="mt-6 rounded-lg border border-[var(--color-line)] bg-[var(--color-mist)]/40 p-5"
            >
              <div
                className={`text-[12px] font-bold uppercase tracking-wider ${
                  selected === currentCorrect
                    ? "text-emerald-700"
                    : "text-red-700"
                }`}
              >
                {selected === currentCorrect ? "Correct" : "Incorrect"}
              </div>
              <p className="mt-1 text-[14px] font-bold text-[var(--color-ink)]">
                Answer: {currentCorrect}
              </p>
              {current.explanation && (
                <p className="mt-3 text-[14px] leading-relaxed text-[var(--color-slate)]">
                  {current.explanation}
                </p>
              )}
            </motion.div>
          )}

          <div className="mt-7 flex items-center justify-between gap-4">
            <Link
              href="/products/orthoprep/quiz"
              className="text-[13px] font-semibold text-[var(--color-slate)] hover:text-[var(--color-blue)]"
            >
              ← Exit quiz
            </Link>
            {!revealed ? (
              <button
                type="button"
                onClick={handleSubmit}
                disabled={!selected}
                className="inline-flex items-center gap-2 rounded-lg bg-[var(--color-blue)] px-5 py-2.5 text-[14px] font-semibold text-white transition-colors hover:bg-[var(--color-blue-deep)] disabled:cursor-not-allowed disabled:opacity-40"
              >
                Submit answer
              </button>
            ) : (
              <button
                type="button"
                onClick={handleNext}
                className="inline-flex items-center gap-2 rounded-lg bg-[var(--color-blue)] px-5 py-2.5 text-[14px] font-semibold text-white transition-colors hover:bg-[var(--color-blue-deep)]"
              >
                {index + 1 >= total ? "Finish" : "Next question"} →
              </button>
            )}
          </div>
        </motion.article>
      </AnimatePresence>
    </div>
  );
}

function ResultScreen({
  correctCount,
  total,
  pct,
  durationMs,
  questions,
  answers,
  retryHref,
  saved,
}: {
  correctCount: number;
  total: number;
  pct: number;
  durationMs: number;
  questions: FrcsQuestionWithContext[];
  answers: AnswerRecord[];
  retryHref: string;
  saved: boolean;
}) {
  const band =
    pct >= 80
      ? "Strong"
      : pct >= 60
        ? "Passing"
        : pct >= 40
          ? "Borderline"
          : "Needs work";

  return (
    <div>
      <motion.div
        initial={{ opacity: 0, y: 10 }}
        animate={{ opacity: 1, y: 0 }}
        className="rounded-xl border border-[var(--color-line)] bg-white p-8 shadow-[0_1px_0_var(--color-line)]"
      >
        <div className="text-[11px] font-bold uppercase tracking-wider text-[var(--color-blue)]">
          Quiz complete
        </div>
        <div className="mt-3 flex flex-wrap items-end gap-x-6 gap-y-3">
          <div className="font-display text-[64px] font-extrabold leading-none text-[var(--color-blue)]">
            {pct}%
          </div>
          <div>
            <div className="font-display text-[22px] font-bold text-[var(--color-ink)]">
              {correctCount} of {total} correct
            </div>
            <div className="text-[13px] text-[var(--color-slate)]">
              {band} · {formatDuration(durationMs)} total ·{" "}
              {saved ? "Saved to history" : "Not saved"}
            </div>
          </div>
        </div>

        <div className="mt-6 grid grid-cols-2 gap-3 sm:grid-cols-4">
          <Stat label="Correct" value={correctCount} tone="good" />
          <Stat label="Incorrect" value={total - correctCount} tone="bad" />
          <Stat
            label="Avg time"
            value={`${Math.round(durationMs / total / 1000)}s`}
          />
          <Stat label="Total time" value={formatDuration(durationMs)} />
        </div>

        <div className="mt-8 flex flex-wrap items-center gap-3">
          <Link
            href={retryHref}
            className="inline-flex items-center gap-2 rounded-lg bg-[var(--color-blue)] px-5 py-2.5 text-[14px] font-semibold text-white transition-colors hover:bg-[var(--color-blue-deep)]"
          >
            Try another
          </Link>
          <Link
            href="/products/orthoprep/quiz"
            className="inline-flex items-center gap-2 rounded-lg border border-[var(--color-line)] px-5 py-2.5 text-[14px] font-semibold text-[var(--color-ink)] transition-colors hover:border-[var(--color-blue)] hover:text-[var(--color-blue)]"
          >
            Quiz hub
          </Link>
          <Link
            href="/products/orthoprep/quiz/history"
            className="inline-flex items-center gap-2 rounded-lg border border-[var(--color-line)] px-5 py-2.5 text-[14px] font-semibold text-[var(--color-ink)] transition-colors hover:border-[var(--color-blue)] hover:text-[var(--color-blue)]"
          >
            View history
          </Link>
        </div>
      </motion.div>

      <details className="group mt-8 rounded-xl border border-[var(--color-line)] bg-white p-6">
        <summary className="flex cursor-pointer list-none items-center justify-between text-[14px] font-semibold text-[var(--color-ink)] [&::-webkit-details-marker]:hidden">
          <span>Review all answers ({total})</span>
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            strokeWidth="2"
            strokeLinecap="round"
            className="h-4 w-4 transition-transform group-open:rotate-45 text-[var(--color-blue)]"
          >
            <path d="M12 5v14M5 12h14" />
          </svg>
        </summary>
        <ol className="mt-6 space-y-5">
          {questions.map((q, i) => {
            const a = answers[i];
            const wasCorrect = a?.chosen === a?.correct;
            return (
              <li
                key={q.number}
                className="rounded-lg border border-[var(--color-line)] p-5"
              >
                <div className="flex flex-wrap items-center gap-2 text-[11px] font-bold uppercase tracking-wider">
                  <span className="text-[var(--color-blue)]">
                    Q{q.number}
                  </span>
                  <span className="text-[var(--color-line)]">•</span>
                  <span className="text-[var(--color-slate)]">
                    {q.chapterTitle}
                  </span>
                  <span className="text-[var(--color-line)]">•</span>
                  <span
                    className={
                      wasCorrect ? "text-emerald-700" : "text-red-700"
                    }
                  >
                    {wasCorrect ? "Correct" : "Incorrect"}
                  </span>
                </div>
                <p className="mt-2 text-[15px] leading-relaxed text-[var(--color-ink)]">
                  {q.stem}
                </p>
                <div className="mt-3 text-[13px] text-[var(--color-slate)]">
                  Your answer:{" "}
                  <strong className="text-[var(--color-ink)]">
                    {a?.chosen ?? "—"}
                  </strong>{" "}
                  · Correct:{" "}
                  <strong className="text-[var(--color-ink)]">
                    {a?.correct}
                  </strong>
                </div>
                {q.explanation && (
                  <p className="mt-3 text-[13px] leading-relaxed text-[var(--color-slate)]">
                    {q.explanation}
                  </p>
                )}
              </li>
            );
          })}
        </ol>
      </details>
    </div>
  );
}

function Stat({
  label,
  value,
  tone,
}: {
  label: string;
  value: string | number;
  tone?: "good" | "bad";
}) {
  const valueClass =
    tone === "good"
      ? "text-emerald-700"
      : tone === "bad"
        ? "text-red-700"
        : "text-[var(--color-ink)]";
  return (
    <div className="rounded-lg border border-[var(--color-line)] bg-[var(--color-mist)]/30 p-4">
      <div className="text-[11px] font-bold uppercase tracking-wider text-[var(--color-slate)]">
        {label}
      </div>
      <div
        className={`mt-1 font-display text-[22px] font-extrabold leading-none ${valueClass}`}
      >
        {value}
      </div>
    </div>
  );
}
