/**
 * Age-band × subject → graded levels.
 *
 * Content sources, in priority order:
 *   1. data/levels.generated.json  (AI-augmented, written by `npm run seed`)
 *   2. data/levels.json            (hand-curated overrides, optional)
 *   3. synthesised from the existing learn cards + question pool
 *
 * Synthesis guarantees the app always has multi-level progression even before
 * any curated/AI content exists. Server-only.
 */
import fs from 'node:fs';
import path from 'node:path';
import type { AgeBand, Level, LevelStatus, Question, LearnCardData } from './types';
import { getLearnCards, getLessonsForSubject } from './content';
import { bn } from './bn';

type CuratedMap = Record<string, Partial<Record<AgeBand, Level[]>>>;

let _curated: CuratedMap | null = null;

function loadCurated(): CuratedMap {
  if (_curated) return _curated;
  const merged: CuratedMap = {};
  for (const file of ['levels.json', 'levels.generated.json']) {
    try {
      const p = path.join(process.cwd(), 'data', file);
      if (!fs.existsSync(p)) continue;
      const data = JSON.parse(fs.readFileSync(p, 'utf-8')) as CuratedMap;
      for (const [subject, bands] of Object.entries(data)) {
        merged[subject] = { ...(merged[subject] ?? {}), ...bands };
      }
    } catch {
      /* ignore malformed content, fall back to synthesis */
    }
  }
  _curated = merged;
  return merged;
}

// How many levels / questions-per-level to synthesise when there's no
// generated/curated content. Kept in sync with the seed's LEVELS_PER_BAND.
const BAND_SHAPE: Record<AgeBand, { levels: number; perLevel: number }> = {
  '3-5': { levels: 30, perLevel: 3 },
  '6-8': { levels: 30, perLevel: 4 },
  '9-11': { levels: 30, perLevel: 5 },
  '12-14': { levels: 30, perLevel: 5 },
};

function pick<T>(pool: T[], start: number, count: number): T[] {
  if (pool.length === 0) return [];
  const out: T[] = [];
  for (let i = 0; i < count; i += 1) out.push(pool[(start + i) % pool.length]);
  return out;
}

function synthesise(subjectId: string, band: AgeBand): Level[] {
  const cards = getLearnCards(subjectId);
  const questionPool: Question[] = getLessonsForSubject(subjectId).flatMap((l) => l.questions);
  const { levels, perLevel } = BAND_SHAPE[band];
  if (questionPool.length === 0) return [];

  return Array.from({ length: levels }, (_, i): Level => {
    const learn: LearnCardData[] = cards.length
      ? pick(cards, i * 2, Math.min(3, Math.max(2, cards.length)))
      : [];
    return {
      id: `${subjectId}-${band}-${i + 1}`,
      index: i,
      title: `লেভেল ${bn(i + 1)}`,
      subtitle: i === 0 ? 'শুরু করো' : i === levels - 1 ? 'চ্যালেঞ্জ' : 'আরও শেখো',
      learnCards: learn,
      questions: pick(questionPool, i * perLevel, perLevel),
      rewardCoins: 10 + i * 5,
    };
  });
}

/** All levels for a subject in a band. */
export function getLevels(subjectId: string, band: AgeBand): Level[] {
  const curated = loadCurated()[subjectId]?.[band];
  if (curated && curated.length > 0) return curated;
  return synthesise(subjectId, band);
}

export function getLevel(subjectId: string, band: AgeBand, levelId: string): Level | undefined {
  return getLevels(subjectId, band).find((l) => l.id === levelId);
}

/**
 * Levels annotated with lock/complete/best-stars for a subscriber.
 * A level is unlocked when it's the first, or the previous level is completed.
 */
export function getLevelStatuses(
  levels: Level[],
  completedMap: Map<string, { bestStars: number }>,
): LevelStatus[] {
  const out: LevelStatus[] = [];
  levels.forEach((lvl, i) => {
    const prev = i === 0 ? null : levels[i - 1];
    const prevDone = prev ? completedMap.has(prev.id) : true;
    const mine = completedMap.get(lvl.id);
    out.push({
      ...lvl,
      completed: Boolean(mine),
      bestStars: mine?.bestStars ?? 0,
      locked: !prevDone,
    });
  });
  return out;
}
