/**
 * AI generation of full graded levels (learn cards + questions) for a
 * subject × age band. Used by `npm run seed` to build data/levels.generated.json.
 *
 * Large level counts (e.g. 30) are generated in CHUNK-sized model calls so each
 * response stays within token limits; difficulty position within the full track
 * is passed to each chunk so progression stays coherent. Falls back to chunking
 * the base question pool when AI is unavailable or a chunk fails.
 * Server-only.
 */
import { canUseAi } from '../env';
import type { AgeBand, Level, Question, LearnCardData } from '../types';
import { SUBJECT_NAME } from '../types';
import { getLearnCards, getLessonsForSubject } from '../content';
import { callModel, extractJsonArray } from './provider';
import { bn } from '../bn';

const CHUNK = 6; // levels generated per model call

const BAND_DESC: Record<AgeBand, string> = {
  '3-5': 'একদম ছোট বাচ্চা (৩-৫ বছর) — খুব সহজ, ছবি-ভাব, ছোট শব্দ ও ১-৫ সংখ্যা',
  '6-8': 'ছোট বাচ্চা (৬-৮ বছর) — সহজ বর্ণ, ১-২ অঙ্কের যোগ-বিয়োগ, সহজ পড়া',
  '9-11': 'বাচ্চা (৯-১১ বছর) — যুক্তবর্ণ, ২ অঙ্কের অঙ্ক, গুণ, বিজ্ঞান তথ্য',
  '12-14': 'কিশোর (১২-১৪ বছর) — কঠিন শব্দ, ভাগ/ভগ্নাংশ, বিজ্ঞান ধারণা, বোধশক্তি',
};

function buildPrompt(
  subjectId: string,
  band: AgeBand,
  offset: number,
  count: number,
  total: number,
  perLevel: number,
): string {
  const name = SUBJECT_NAME[subjectId] ?? subjectId;
  return [
    `তুমি বাংলাদেশি শিশুদের জন্য একটা শিক্ষামূলক অ্যাপের কনটেন্ট বানাও।`,
    `বিষয়: ${name}। শিক্ষার্থী: ${BAND_DESC[band]}।`,
    `এটা মোট ${total} লেভেলের একটা track, যেখানে লেভেল সহজ থেকে কঠিন হয়।`,
    `এখন লেভেল ${offset + 1} থেকে ${offset + count} বানাও (এই অংশের কঠিনতা track-এর ওই অবস্থান অনুযায়ী)।`,
    `প্রতিটা লেভেলে থাকবে:`,
    `- "subtitle": ছোট ফোকাস (২-৩ শব্দ)`,
    `- "learnCards": ৩টা কার্ড, প্রতিটায় {"big","sub","emoji","tip"}`,
    `- "questions": ${perLevel} টা MCQ, প্রতিটায় {"question","options" (ঠিক ৪টা),"correctIndex" (0-3),"explanation"}`,
    `নিয়ম: সহজ শুদ্ধ বাংলা, তথ্য সঠিক, শিশু-উপযোগী, প্রশ্ন পুনরাবৃত্তি নয়।`,
    `শুধু ${count}-length JSON array দাও, আর কিছু নয়:`,
    `[{"subtitle":"..","learnCards":[{"big":"..","sub":"..","emoji":"..","tip":".."}],"questions":[{"question":"..","options":["a","b","c","d"],"correctIndex":0,"explanation":".."}]}]`,
  ].join('\n');
}

function coerceQuestions(arr: any, prefix: string): Question[] {
  if (!Array.isArray(arr)) return [];
  const out: Question[] = [];
  arr.forEach((q: any, i: number) => {
    if (q && typeof q.question === 'string' && Array.isArray(q.options) && q.options.length === 4) {
      out.push({
        id: `${prefix}-q${i + 1}`,
        question: q.question,
        options: q.options.slice(0, 4).map(String) as [string, string, string, string],
        correctIndex: Math.max(0, Math.min(3, Number(q.correctIndex) || 0)),
        explanation: typeof q.explanation === 'string' ? q.explanation : 'দারুণ চেষ্টা!',
      });
    }
  });
  return out;
}

function coerceCards(arr: any, prefix: string): LearnCardData[] {
  if (!Array.isArray(arr)) return [];
  const out: LearnCardData[] = [];
  arr.forEach((c: any, i: number) => {
    if (c && (c.big || c.sub)) {
      out.push({
        id: `${prefix}-c${i + 1}`,
        big: String(c.big ?? c.sub ?? '★'),
        sub: String(c.sub ?? ''),
        emoji: String(c.emoji ?? '✨'),
        tip: String(c.tip ?? ''),
      });
    }
  });
  return out;
}

/** One fallback level at a given absolute index, chunked from the base pool. */
function fallbackLevel(subjectId: string, band: AgeBand, index: number, total: number, perLevel: number): Level {
  const cards = getLearnCards(subjectId);
  const pool = getLessonsForSubject(subjectId).flatMap((l) => l.questions);
  const c = Math.max(1, cards.length);
  return {
    id: `${subjectId}-${band}-${index + 1}`,
    index,
    title: `লেভেল ${bn(index + 1)}`,
    subtitle: index === 0 ? 'শুরু' : index === total - 1 ? 'চ্যালেঞ্জ' : 'আরও শেখো',
    learnCards: cards.slice((index * 2) % c, ((index * 2) % c) + 3),
    questions: Array.from({ length: perLevel }, (_, j) => pool[(index * perLevel + j) % pool.length]).filter(Boolean),
    rewardCoins: 10 + Math.min(index, 20) * 2,
  };
}

function fallbackRange(subjectId: string, band: AgeBand, offset: number, count: number, total: number, perLevel: number): Level[] {
  return Array.from({ length: count }, (_, j) => fallbackLevel(subjectId, band, offset + j, total, perLevel));
}

async function generateChunk(
  subjectId: string,
  band: AgeBand,
  offset: number,
  count: number,
  total: number,
  perLevel: number,
): Promise<Level[]> {
  try {
    const text = await callModel(buildPrompt(subjectId, band, offset, count, total, perLevel), {
      maxTokens: 3500,
      temperature: 0.7,
    });
    const parsed = extractJsonArray(text);
    if (!Array.isArray(parsed) || parsed.length === 0) {
      return fallbackRange(subjectId, band, offset, count, total, perLevel);
    }
    const out: Level[] = [];
    for (let j = 0; j < count; j += 1) {
      const lvl = parsed[j];
      const index = offset + j;
      const prefix = `${subjectId}-${band}-${index + 1}`;
      const questions = coerceQuestions(lvl?.questions, prefix);
      if (questions.length === 0) {
        out.push(fallbackLevel(subjectId, band, index, total, perLevel));
        continue;
      }
      out.push({
        id: prefix,
        index,
        title: `লেভেল ${bn(index + 1)}`,
        subtitle: typeof lvl?.subtitle === 'string' ? lvl.subtitle : `লেভেল ${bn(index + 1)}`,
        learnCards: coerceCards(lvl?.learnCards, prefix),
        questions,
        rewardCoins: 10 + Math.min(index, 20) * 2,
      });
    }
    return out;
  } catch (err) {
    console.error(`[ai/levelgen] ${subjectId}/${band} chunk@${offset} failed:`, err);
    return fallbackRange(subjectId, band, offset, count, total, perLevel);
  }
}

export async function generateBandLevels(
  subjectId: string,
  band: AgeBand,
  total = 30,
  perLevel = 5,
  onProgress?: (done: number) => void,
): Promise<Level[]> {
  if (!canUseAi()) {
    return fallbackRange(subjectId, band, 0, total, total, perLevel);
  }
  const all: Level[] = [];
  for (let offset = 0; offset < total; offset += CHUNK) {
    const count = Math.min(CHUNK, total - offset);
    const chunk = await generateChunk(subjectId, band, offset, count, total, perLevel);
    all.push(...chunk);
    onProgress?.(all.length);
  }
  return all;
}
