import "server-only";
import { asc, eq } from "drizzle-orm";
import staticData from "@/data/frcs-bank.json";
import { db } from "@/lib/db/client";
import { orthoprepQuestions } from "@/lib/db/schema";

/**
 * OrthoPrep question bank reader.
 *
 * Primary source is the `orthoprep_questions` table — admins add / edit
 * questions via /admin/orthoprep and changes go live immediately. When the
 * DB is unreachable or empty we fall back to the static JSON seed so
 * marketing pages and the quiz never 500.
 */

export type FrcsQuestion = {
  number: number;
  stem: string;
  options: string[];
  answer: string;
  explanation: string;
  distractors?: string;
  takeHome?: string;
  difficulty?: string;
  tags?: string[];
};

export type FrcsChapter = {
  title: string;
  range: string;
  questions: FrcsQuestion[];
};

export type FrcsPart = {
  title: string;
  chapters: FrcsChapter[];
};

export type FrcsBank = {
  title: string;
  subtitle: string;
  author: string;
  parts: FrcsPart[];
};

export type FrcsQuestionWithContext = FrcsQuestion & {
  partSlug: string;
  partTitle: string;
  chapterSlug: string;
  chapterTitle: string;
};

export type PartSummary = {
  slug: string;
  title: string;
  chapterCount: number;
  questionCount: number;
  chapters: {
    slug: string;
    title: string;
    range: string;
    questionCount: number;
  }[];
};

export function slugifyFrcs(input: string): string {
  return input
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/(^-|-$)/g, "");
}

/**
 * Build a FrcsBank shape from DB rows, grouping by part → chapter and
 * preserving the original numeric ordering.
 */
async function buildBankFromDb(): Promise<FrcsBank | null> {
  try {
    const rows = await db
      .select()
      .from(orthoprepQuestions)
      .where(eq(orthoprepQuestions.status, "published"))
      .orderBy(asc(orthoprepQuestions.number));
    if (rows.length === 0) return null;

    const partOrder: string[] = [];
    const parts = new Map<string, Map<string, FrcsQuestion[]>>();

    for (const r of rows) {
      if (!parts.has(r.part)) {
        parts.set(r.part, new Map());
        partOrder.push(r.part);
      }
      const chapters = parts.get(r.part)!;
      if (!chapters.has(r.chapter)) chapters.set(r.chapter, []);
      chapters.get(r.chapter)!.push({
        number: r.number,
        stem: r.stem,
        options: r.options,
        answer: r.answer,
        explanation: r.explanation,
        distractors: r.distractors || undefined,
        takeHome: r.takeHome || undefined,
        difficulty: r.difficulty,
        tags: r.tags.length > 0 ? r.tags : undefined,
      });
    }

    return {
      title: staticData.title ?? "OrthoPrep FRCS Basic Sciences",
      subtitle: staticData.subtitle ?? "",
      author: staticData.author ?? "",
      parts: partOrder.map((partTitle) => {
        const chapters = Array.from(parts.get(partTitle)!.entries()).map(
          ([chapterTitle, questions]) => ({
            title: chapterTitle || "General",
            range: rangeOf(questions),
            questions,
          }),
        );
        return { title: partTitle, chapters };
      }),
    };
  } catch {
    return null;
  }
}

function rangeOf(questions: FrcsQuestion[]): string {
  if (questions.length === 0) return "";
  const nums = questions.map((q) => q.number).sort((a, b) => a - b);
  return nums[0] === nums[nums.length - 1]
    ? `Q${nums[0]}`
    : `Q${nums[0]}–Q${nums[nums.length - 1]}`;
}

export async function getFrcsBank(): Promise<FrcsBank> {
  const fromDb = await buildBankFromDb();
  return fromDb ?? (staticData as FrcsBank);
}

export async function getAllQuestionsWithContext(): Promise<FrcsQuestionWithContext[]> {
  const bank = await getFrcsBank();
  const out: FrcsQuestionWithContext[] = [];
  for (const part of bank.parts) {
    const partSlug = slugifyFrcs(part.title);
    for (const chapter of part.chapters) {
      const chapterSlug = slugifyFrcs(chapter.title);
      for (const q of chapter.questions) {
        out.push({
          ...q,
          partSlug,
          partTitle: part.title,
          chapterSlug,
          chapterTitle: chapter.title,
        });
      }
    }
  }
  return out;
}

function shuffleInPlace<T>(arr: T[]): T[] {
  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  return arr;
}

export async function getRandomQuestions(
  count: number,
): Promise<FrcsQuestionWithContext[]> {
  const all = await getAllQuestionsWithContext();
  const shuffled = shuffleInPlace(all.slice());
  return shuffled.slice(0, Math.min(count, shuffled.length));
}

export async function getQuestionsByPart(
  partSlug: string,
): Promise<FrcsQuestionWithContext[]> {
  const all = await getAllQuestionsWithContext();
  return all.filter((q) => q.partSlug === partSlug);
}

export async function getAllPartsWithCounts(): Promise<PartSummary[]> {
  const bank = await getFrcsBank();
  return bank.parts.map((part) => {
    const partSlug = slugifyFrcs(part.title);
    const chapters = part.chapters.map((ch) => ({
      slug: slugifyFrcs(ch.title),
      title: ch.title,
      range: ch.range,
      questionCount: ch.questions.length,
    }));
    return {
      slug: partSlug,
      title: part.title,
      chapterCount: chapters.length,
      questionCount: chapters.reduce((s, c) => s + c.questionCount, 0),
      chapters,
    };
  });
}

export async function getPartBySlug(
  partSlug: string,
): Promise<PartSummary | undefined> {
  const all = await getAllPartsWithCounts();
  return all.find((p) => p.slug === partSlug);
}
