"use client";

/**
 * QuizResult schema.
 *
 * v1 (legacy): aggregate-only — correctCount / questionNumbers.
 * v2 (current): adds per-question `answers` so we can derive "wrong questions"
 *               and per-category accuracy. The optional `v` field identifies
 *               records written by the v2 code path; anything missing is
 *               treated as v1 (aggregate-only) during migration.
 */
export type AnswerDetail = {
  /**
   * Stable identifier for a question. We use the FRCS bank `number` field
   * (the only stable id the bank exposes today). Named `pmid` because that
   * is the generic "question primary id" the rest of the app refers to.
   */
  pmid: number;
  correct: boolean;
};

export type QuizResult = {
  id: string;
  takenAt: string; // ISO
  mode: "random" | "part" | "review";
  partSlug?: string;
  partTitle?: string;
  questionCount: number;
  correctCount: number;
  durationMs: number;
  questionNumbers: number[];
  /** v2 schema marker. Absent on legacy records. */
  v?: 2;
  /** v2 per-question correctness. Absent on legacy records. */
  answers?: AnswerDetail[];
};

export const QUIZ_HISTORY_KEY = "orthoprep.quiz.history";
const MAX_HISTORY = 50;

export function loadQuizHistory(): QuizResult[] {
  if (typeof window === "undefined") return [];
  try {
    const raw = window.localStorage.getItem(QUIZ_HISTORY_KEY);
    if (!raw) return [];
    const parsed = JSON.parse(raw);
    if (!Array.isArray(parsed)) return [];
    return parsed as QuizResult[];
  } catch {
    return [];
  }
}

export function saveQuizResult(result: QuizResult): QuizResult[] {
  if (typeof window === "undefined") return [];
  const prior = loadQuizHistory();
  const next = [result, ...prior].slice(0, MAX_HISTORY);
  try {
    window.localStorage.setItem(QUIZ_HISTORY_KEY, JSON.stringify(next));
  } catch {
    /* ignore quota errors */
  }
  return next;
}

export function clearQuizHistory(): void {
  if (typeof window === "undefined") return;
  window.localStorage.removeItem(QUIZ_HISTORY_KEY);
}

export function makeQuizId(): string {
  // Cheap collision-resistant id without bringing in uuid.
  return `q_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
}

export function formatDuration(ms: number): string {
  const totalSec = Math.max(0, Math.round(ms / 1000));
  const m = Math.floor(totalSec / 60);
  const s = totalSec % 60;
  if (m === 0) return `${s}s`;
  return `${m}m ${s.toString().padStart(2, "0")}s`;
}

export function formatTakenAt(iso: string): string {
  try {
    const d = new Date(iso);
    return d.toLocaleDateString(undefined, {
      year: "numeric",
      month: "short",
      day: "numeric",
      hour: "2-digit",
      minute: "2-digit",
    });
  } catch {
    return iso;
  }
}

/**
 * Derive the set of question ids the learner has answered incorrectly.
 *
 * v2 records contribute their per-question `answers` directly. v1 records
 * (no `answers` array) are treated as aggregate-only and cannot surface
 * individual wrong ids, so they are skipped here — but they still count
 * in `getCategoryStats`.
 *
 * If the learner later answered the same question correctly, it is removed
 * from the wrong set. Order is "most recently wrong first".
 */
export function getWrongQuestions(): number[] {
  const history = loadQuizHistory();
  // Walk oldest → newest so later correct answers override earlier wrongs.
  const state = new Map<number, boolean>(); // pmid -> lastCorrect
  const order: number[] = []; // insertion order of most-recently-wrong
  for (let i = history.length - 1; i >= 0; i--) {
    const rec = history[i];
    if (!rec.answers || !Array.isArray(rec.answers)) continue;
    for (const a of rec.answers) {
      state.set(a.pmid, a.correct);
      if (!a.correct) {
        // promote to front (most recently wrong)
        const idx = order.indexOf(a.pmid);
        if (idx >= 0) order.splice(idx, 1);
        order.unshift(a.pmid);
      }
    }
  }
  return order.filter((pmid) => state.get(pmid) === false);
}

export type CategoryStat = {
  partSlug: string;
  partTitle: string;
  attempted: number;
  correct: number;
  accuracy: number; // 0..1
};

/**
 * Aggregate attempted / correct counts per part. Works on both v1 and v2
 * records: v1 contributes its aggregate questionCount/correctCount (the
 * whole session counts toward the recorded partSlug, or the synthetic
 * "random" bucket for random-mode sessions).
 */
export function getCategoryStats(): CategoryStat[] {
  const history = loadQuizHistory();
  const buckets = new Map<
    string,
    { partSlug: string; partTitle: string; attempted: number; correct: number }
  >();
  for (const rec of history) {
    const partSlug = rec.partSlug ?? "__mixed__";
    const partTitle = rec.partTitle ?? "Mixed / random";
    const bucket = buckets.get(partSlug) ?? {
      partSlug,
      partTitle,
      attempted: 0,
      correct: 0,
    };
    bucket.attempted += rec.questionCount;
    bucket.correct += rec.correctCount;
    buckets.set(partSlug, bucket);
  }
  return Array.from(buckets.values())
    .map((b) => ({
      ...b,
      accuracy: b.attempted > 0 ? b.correct / b.attempted : 0,
    }))
    .sort((a, b) => b.attempted - a.attempted);
}

/**
 * Global totals across all sessions — used by the stats page.
 */
export function getOverallStats(): {
  sessions: number;
  questions: number;
  correct: number;
  accuracy: number;
  totalDurationMs: number;
} {
  const history = loadQuizHistory();
  const sessions = history.length;
  const questions = history.reduce((s, r) => s + r.questionCount, 0);
  const correct = history.reduce((s, r) => s + r.correctCount, 0);
  const totalDurationMs = history.reduce((s, r) => s + r.durationMs, 0);
  return {
    sessions,
    questions,
    correct,
    accuracy: questions > 0 ? correct / questions : 0,
    totalDurationMs,
  };
}
