import { Router } from "express";
import { getAuth } from "@clerk/express";
import { db } from "@workspace/db";
import {
  sessionPlansTable, sessionRowsTable, plansTable, documentsTable, institutionsTable,
} from "@workspace/db";
import { eq } from "drizzle-orm";
import { openai } from "@workspace/integrations-openai-ai-server";
import {
  Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
  AlignmentType, BorderStyle, WidthType, VerticalAlign, convertInchesToTwip,
} from "docx";

const router = Router();

// ── Types ────────────────────────────────────────────────────────────────────

interface ParticipationStage {
  title: string;
  duration: string;
  trainerActivity: string;
  traineeActivity: string;
  teachingMethod: string;
  learningCheck: string;
  notes?: string | null;
}

interface SessionPlanContent {
  bridge_in?: {
    attentionGrabber?: string;
    scenario?: string;
    priorKnowledgeActivity?: string;
  };
  learning_outcomes?: string[];
  resources?: string[];
  safety_requirements?: string[];
  pre_assessment?: string[];
  participatory_stages?: ParticipationStage[];
  post_assessment?: string[];
  summary?: {
    recap?: string;
    keyTakeaways?: string[];
    assignment?: string;
    nextLessonPrep?: string;
  };
  reflection_prompts?: string[];
}

function mapSessionPlan(sp: typeof sessionPlansTable.$inferSelect) {
  return {
    id: sp.id,
    sessionRowId: sp.sessionRowId,
    planId: sp.planId,
    scheduledDate: sp.scheduledDate,
    scheduledStartTime: sp.scheduledStartTime,
    scheduledEndTime: sp.scheduledEndTime,
    duration: sp.duration,
    venue: sp.venue,
    status: sp.status,
    content: sp.content as SessionPlanContent | null,
    createdAt: sp.createdAt.toISOString(),
    updatedAt: sp.updatedAt.toISOString(),
  };
}

// ── AI Generation ─────────────────────────────────────────────────────────────

async function buildSessionPlanPrompt(
  plan: typeof plansTable.$inferSelect,
  row: typeof sessionRowsTable.$inferSelect,
  opts: {
    duration?: string;
    venue?: string;
    deliveryContext?: string;
    ragText?: string;
  },
): Promise<string> {
  const durationStr = opts.duration ?? row.duration ?? "2 hours";
  const venue = opts.venue ?? "Classroom / Workshop";
  const deliveryCtx = opts.deliveryContext ?? (row.deliveryMode ?? "classroom");

  return `You are an expert TVET curriculum specialist generating a detailed CDACC/CBET session plan for a Kenyan TVET institution.

PLAN CONTEXT:
- Unit Title: ${plan.unitTitle}
- Unit Code: ${plan.unitCode ?? "N/A"}
- Level: ${plan.level ?? "N/A"}
- Trainer: ${plan.trainerName ?? "N/A"}
- Class: ${plan.className ?? "N/A"}
- Trainees: ${plan.numberOfTrainees ?? "N/A"}

SESSION ROW TO PLAN:
- Week: ${row.weekNumber}, Session: ${row.sessionNumber}
- Topic: ${row.topic ?? "N/A"}
- Subtopic: ${row.subtopic ?? "N/A"}
- Learning Outcome: ${row.learningOutcome ?? "N/A"}
- Trainer Activity: ${row.trainerActivity ?? "N/A"}
- Trainee Activity: ${row.traineeActivity ?? "N/A"}
- Resources: ${row.resources ?? "N/A"}
- Assessment: ${row.assessmentMethod ?? "N/A"}
- Performance Criteria: ${row.performanceCriteria ?? "N/A"}
- Delivery Mode: ${deliveryCtx}

SESSION DETAILS:
- Duration: ${durationStr}
- Venue: ${venue}
${opts.ragText ? `\nCURRICULUM REFERENCE:\n${opts.ragText.slice(0, 3000)}` : ""}

REQUIREMENTS:
1. Use CBET/CDACC participatory delivery with measurable outcomes
2. Include realistic, specific trainer activities (not vague like "discuss X" — say exactly what to demonstrate, show, explain, supervise)
3. Scale depth to the session duration (${durationStr})
4. Use workplace-realistic examples relevant to ${plan.unitTitle}
5. Include safety emphasis where relevant to ${deliveryCtx} delivery
6. Break the participatory section into 4-7 stages (demonstration, guided practice, group work, practical, observation, reflection, etc.)
7. Each stage must have specific trainer AND trainee actions with time allocation
8. Use measurable CBET verbs: demonstrate, perform, identify, apply, calculate, construct, inspect, verify
9. Align assessment with outcomes

Generate a structured JSON session plan matching EXACTLY this schema:
{
  "bridge_in": {
    "attentionGrabber": "engaging real-world question or scenario to open the session",
    "scenario": "workplace-realistic problem or story relevant to the topic",
    "priorKnowledgeActivity": "specific activity to activate prior knowledge (think-pair-share, quick quiz, demonstration)"
  },
  "learning_outcomes": ["measurable outcome 1", "measurable outcome 2", "..."],
  "resources": ["specific tool/material/equipment 1", "PPE if needed", "worksheet name", "..."],
  "safety_requirements": ["specific safety precaution 1", "..."],
  "pre_assessment": [
    "specific diagnostic question 1",
    "guided question 2",
    "..."
  ],
  "participatory_stages": [
    {
      "title": "Stage name (e.g. Demonstration)",
      "duration": "X minutes",
      "trainerActivity": "Exactly what trainer does — be specific and actionable",
      "traineeActivity": "Exactly what trainees do — be specific and actionable",
      "teachingMethod": "methodology (demonstration, group work, guided practice, etc.)",
      "learningCheck": "how trainer checks understanding at this stage",
      "notes": "optional notes"
    }
  ],
  "post_assessment": [
    "specific reflection question 1",
    "practical competency check 2",
    "peer assessment prompt 3",
    "..."
  ],
  "summary": {
    "recap": "concise 2-3 sentence recap of key learning",
    "keyTakeaways": ["takeaway 1", "takeaway 2", "..."],
    "assignment": "specific assignment or task for independent practice",
    "nextLessonPrep": "what trainees should prepare/review for next session"
  },
  "reflection_prompts": [
    "What worked well today?",
    "Which trainees struggled and why?",
    "Was timing adequate for each stage?",
    "custom prompt relevant to topic..."
  ]
}

Respond ONLY with valid JSON. No markdown. No explanation.`;
}

async function generateSessionPlanContent(
  plan: typeof plansTable.$inferSelect,
  row: typeof sessionRowsTable.$inferSelect,
  opts: {
    duration?: string;
    venue?: string;
    deliveryContext?: string;
    documentIds?: number[];
  },
): Promise<SessionPlanContent> {
  let ragText = "";
  if (opts.documentIds && opts.documentIds.length > 0) {
    const docs = await db.select().from(documentsTable);
    const relevant = docs.filter((d) => opts.documentIds!.includes(d.id) && d.extractedText);
    ragText = relevant.map((d) => `[${d.title}]\n${d.extractedText}`).join("\n\n---\n\n");
  }

  const prompt = await buildSessionPlanPrompt(plan, row, {
    duration: opts.duration,
    venue: opts.venue,
    deliveryContext: opts.deliveryContext,
    ragText,
  });

  const completion = await openai.chat.completions.create({
    model: "gpt-4o",
    response_format: { type: "json_object" },
    temperature: 0.7,
    messages: [{ role: "user", content: prompt }],
  });

  const raw = completion.choices[0]?.message?.content ?? "{}";
  return JSON.parse(raw) as SessionPlanContent;
}

// ── DOCX Generator (CDACC Official Template) ─────────────────────────────────

function parseDurationMins(str: string): number {
  if (!str) return 0;
  const h = str.match(/(\d+(?:\.\d+)?)\s*h/i);
  const m = str.match(/(\d+)\s*min/i);
  return (h ? Math.round(parseFloat(h[1]) * 60) : 0) + (m ? parseInt(m[1]) : 0);
}

const THIN = { style: BorderStyle.SINGLE, size: 4, color: "000000" } as const;
const NO_BORDER = { style: BorderStyle.NIL, size: 0, color: "FFFFFF" } as const;
const TABLE_BORDERS = { top: THIN, bottom: THIN, left: THIN, right: THIN, insideHorizontal: THIN, insideVertical: THIN };
const _NO_BORDERS = { top: NO_BORDER, bottom: NO_BORDER, left: NO_BORDER, right: NO_BORDER, insideHorizontal: NO_BORDER, insideVertical: NO_BORDER };
void _NO_BORDERS;

function centeredBold(text: string, size = 22): Paragraph {
  return new Paragraph({
    alignment: AlignmentType.CENTER,
    spacing: { after: 0 },
    children: [new TextRun({ text, bold: true, size })],
  });
}

function centeredText(text: string, size = 20): Paragraph {
  return new Paragraph({
    alignment: AlignmentType.CENTER,
    spacing: { after: 0 },
    children: [new TextRun({ text, size })],
  });
}

function infoLine(label: string, value: string): Paragraph {
  return new Paragraph({
    spacing: { after: 40 },
    children: [
      new TextRun({ text: `${label}  `, bold: true, size: 20 }),
      new TextRun({ text: value, size: 20 }),
    ],
  });
}

function sectionHeading(label: string, timeSuffix = ""): Paragraph {
  return new Paragraph({
    spacing: { before: 160, after: 80 },
    children: [
      new TextRun({ text: label + (timeSuffix ? `   ${timeSuffix}` : ""), bold: true, underline: {}, size: 22 }),
    ],
  });
}

function bodyText(text: string, size = 20): Paragraph {
  return new Paragraph({
    spacing: { after: 60 },
    children: [new TextRun({ text: text ?? "", size })],
  });
}

function bulletItem(text: string): Paragraph {
  return new Paragraph({
    spacing: { after: 60 },
    children: [new TextRun({ text: `\u2022  ${text}`, size: 20 })],
    indent: { left: convertInchesToTwip(0.25) },
  });
}

function cell(text: string, bold = false, width?: number): TableCell {
  return new TableCell({
    verticalAlign: VerticalAlign.TOP,
    width: width ? { size: width, type: WidthType.DXA } : undefined,
    margins: { top: 80, bottom: 80, left: 100, right: 100 },
    children: [
      new Paragraph({
        children: [new TextRun({ text: text ?? "", bold, size: 18 })],
        spacing: { after: 0 },
      }),
    ],
  });
}

function hdrCell(text: string, width?: number): TableCell {
  return new TableCell({
    verticalAlign: VerticalAlign.CENTER,
    width: width ? { size: width, type: WidthType.DXA } : undefined,
    shading: { fill: "D9D9D9" },
    margins: { top: 80, bottom: 80, left: 100, right: 100 },
    children: [
      new Paragraph({
        alignment: AlignmentType.CENTER,
        children: [new TextRun({ text, bold: true, size: 18 })],
        spacing: { after: 0 },
      }),
    ],
  });
}

async function buildSessionPlanDocx(
  plan: typeof plansTable.$inferSelect,
  row: typeof sessionRowsTable.$inferSelect,
  sp: typeof sessionPlansTable.$inferSelect,
  institution: typeof institutionsTable.$inferSelect | null,
): Promise<Buffer> {
  const content = (sp.content ?? {}) as SessionPlanContent;
  const stages = content.participatory_stages ?? [];

  // ── Duration accounting ───────────────────────────────────────────────────
  const bridgeMins = 10;
  const preMins = 20;
  const postMins = 30;
  const summaryMins = 10;
  const partMins = stages.reduce((acc, s) => acc + parseDurationMins(s.duration), 0);
  const totalMins = bridgeMins + preMins + partMins + postMins + summaryMins;

  // ── Participatory table rows ───────────────────────────────────────────────
  const stageRows = stages.map((stage, i) =>
    new TableRow({
      children: [
        cell(String(i + 1), true, 400),
        cell(stage.duration, false, 900),
        cell(stage.trainerActivity),
        cell(stage.traineeActivity),
        cell(stage.learningCheck),
      ],
    }),
  );

  // ── Institution header ────────────────────────────────────────────────────
  const instName = institution?.name ?? plan.trainerName ?? "TVET INSTITUTION";
  const instAddress = institution?.address ?? "";
  const instHeader = institution?.defaultHeaderText ?? "";

  const children: (Paragraph | Table)[] = [
    // Gov header
    centeredBold("MINISTRY OF EDUCATION", 22),
    centeredBold("STATE DEPARTMENT FOR VOCATIONAL AND TECHNICAL TRAINING", 20),
    new Paragraph({ spacing: { after: 0 }, children: [] }),
    centeredBold(instName.toUpperCase(), 22),
    ...(instAddress ? [centeredText(instAddress, 18)] : []),
    ...(instHeader ? [centeredText(instHeader, 18)] : []),
    new Paragraph({ spacing: { after: 160 }, children: [] }),

    // "Session Plan" title
    new Paragraph({
      alignment: AlignmentType.CENTER,
      spacing: { after: 200 },
      children: [new TextRun({ text: "Session Plan", bold: true, underline: {}, size: 28 })],
    }),

    // Session info block
    infoLine("Date:", sp.scheduledDate ?? ""),
    infoLine("Time:", sp.scheduledStartTime && sp.scheduledEndTime ? `${sp.scheduledStartTime} – ${sp.scheduledEndTime}` : ""),
    new Paragraph({ spacing: { after: 40 }, children: [] }),
    infoLine("Trainer name:", plan.trainerName ?? ""),
    infoLine("Trainer Number:", ""),
    infoLine("Institution:", instName),
    infoLine("Level:", plan.level ?? ""),
    infoLine("Unit Code:", plan.unitCode ?? ""),
    new Paragraph({ spacing: { after: 40 }, children: [] }),
    infoLine("Class:", plan.className ?? ""),
    infoLine("Unit of Competency:", plan.unitTitle ?? ""),
    infoLine("Session title:", row.topic ?? ""),
    new Paragraph({ spacing: { after: 120 }, children: [] }),

    // LLN block
    new Paragraph({
      spacing: { after: 40 },
      children: [new TextRun({ text: "Language, Literacy or Numeracy needs (LLN) or Other Requirements of trainee (group) –", bold: true, size: 18, italics: true })],
    }),
    new Paragraph({
      spacing: { after: 200 },
      children: [new TextRun({ text: "If there is any trainee with special needs, make arrangements as appropriate", size: 18, italics: true })],
    }),

    // BRIDGE IN
    sectionHeading("BRIDGE IN", `${bridgeMins}min`),
    ...(content.bridge_in
      ? [
          bodyText(content.bridge_in.attentionGrabber ?? ""),
          ...(content.bridge_in.scenario ? [bodyText(content.bridge_in.scenario)] : []),
          ...(content.bridge_in.priorKnowledgeActivity ? [bodyText(content.bridge_in.priorKnowledgeActivity)] : []),
        ]
      : [bodyText("—")]),
    new Paragraph({ spacing: { after: 120 }, children: [] }),

    // Learning outcomes
    new Paragraph({
      spacing: { before: 120, after: 60 },
      children: [new TextRun({ text: "Learning outcome(s)", bold: true, underline: {}, size: 22 })],
    }),
    bodyText("By the end of the session, the trainee should be able to:"),
    ...(content.learning_outcomes ?? []).map((o) => bulletItem(o)),
    new Paragraph({ spacing: { after: 80 }, children: [] }),

    // Resources
    new Paragraph({
      spacing: { before: 100, after: 60 },
      children: [new TextRun({ text: "Resources (references, and learning aids)", bold: true, underline: {}, size: 22 })],
    }),
    ...(content.resources ?? []).map((r) => bulletItem(r)),
    new Paragraph({ spacing: { after: 80 }, children: [] }),

    // Safety
    ...(content.safety_requirements && content.safety_requirements.length > 0
      ? [
          new Paragraph({
            spacing: { before: 100, after: 60 },
            children: [new TextRun({ text: "Safety requirements", bold: true, underline: {}, size: 22 })],
          }),
          ...(content.safety_requirements.map((s) => bulletItem(s))),
          new Paragraph({ spacing: { after: 80 }, children: [] }),
        ]
      : []),

    // Pre-assessment
    sectionHeading("Pre assessment", `(${preMins}min)`),
    bodyText("Pose guiding questions to assess prior knowledge on the following topics:"),
    ...(content.pre_assessment ?? []).map((q) => bulletItem(q)),
    new Paragraph({ spacing: { after: 60 }, children: [] }),
    bodyText("Trainees to use a Think\u2013Pair\u2013Share strategy to discuss and share their understanding."),
    new Paragraph({ spacing: { after: 160 }, children: [] }),

    // Participatory section heading
    new Paragraph({
      spacing: { before: 120, after: 120 },
      children: [new TextRun({ text: "2.  Participatory ", bold: true, underline: {}, size: 22 })],
    }),

    // Participatory table
    ...(stageRows.length > 0
      ? [
          new Table({
            width: { size: 100, type: WidthType.PERCENTAGE },
            borders: TABLE_BORDERS,
            rows: [
              new TableRow({
                tableHeader: true,
                children: [
                  hdrCell("", 400),
                  hdrCell("Time\n(in minutes)", 900),
                  hdrCell("Trainer Activity"),
                  hdrCell("Trainee Activity"),
                  hdrCell("Learning Check/Assessment"),
                ],
              }),
              ...stageRows,
            ],
          }),
        ]
      : [bodyText("No participatory stages generated.")]),
    new Paragraph({ spacing: { after: 160 }, children: [] }),

    // POST ASSESSMENT
    sectionHeading("3.  POST ASSESSMENT:", `${postMins}min`),
    ...(content.post_assessment ?? []).map((q) => bodyText(q)),
    new Paragraph({ spacing: { after: 80 }, children: [] }),

    // Reflection (trainer)
    ...(content.reflection_prompts && content.reflection_prompts.length > 0
      ? content.reflection_prompts.map((p) => bodyText(`\u2022  ${p}`))
      : []),
    new Paragraph({ spacing: { after: 160 }, children: [] }),

    // SUMMARY
    new Paragraph({
      spacing: { before: 120, after: 80 },
      children: [new TextRun({ text: `SUMMARY    ${summaryMins}min`, bold: true, underline: {}, size: 22 })],
    }),
    ...(content.summary
      ? [
          bodyText(content.summary.recap ?? ""),
          ...(content.summary.assignment ? [bodyText(`Assignment: ${content.summary.assignment}`)] : []),
          ...(content.summary.nextLessonPrep ? [bodyText(`Next session: ${content.summary.nextLessonPrep}`)] : []),
        ]
      : []),
    new Paragraph({ spacing: { after: 200 }, children: [] }),

    // Signature line
    new Paragraph({
      spacing: { after: 200 },
      children: [new TextRun({ text: "Signature", bold: true, size: 20 })],
    }),

    // TOTAL TIME
    new Paragraph({
      spacing: { after: 80 },
      children: [
        new TextRun({ text: "TOTAL TIME:    ", bold: true, size: 22, allCaps: true }),
        new TextRun({ text: `${totalMins} min`, bold: true, size: 22 }),
      ],
    }),
  ];

  const doc = new Document({
    styles: {
      default: {
        document: {
          run: { font: "Times New Roman", size: 20 },
        },
      },
    },
    sections: [{
      properties: {
        page: {
          margin: {
            top: convertInchesToTwip(1),
            bottom: convertInchesToTwip(1),
            left: convertInchesToTwip(1.25),
            right: convertInchesToTwip(1.25),
          },
        },
      },
      children,
    }],
  });

  return Buffer.from(await Packer.toBuffer(doc));
}

// ── Routes ────────────────────────────────────────────────────────────────────

// GET /api/plans/:id/sessions/:sessionId/session-plan
router.get("/plans/:id/sessions/:sessionId/session-plan", async (req, res) => {
  try {
    const sessionId = parseInt(req.params.sessionId);
    const sp = await db.query.sessionPlansTable.findFirst({
      where: eq(sessionPlansTable.sessionRowId, sessionId),
    });
    if (!sp) return res.status(404).json({ error: "Not found" });
    return res.json(mapSessionPlan(sp));
  } catch (err) {
    req.log.error({ err }, "Error getting session plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/plans/:id/sessions/:sessionId/session-plan (AI generate)
router.post("/plans/:id/sessions/:sessionId/session-plan", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });

    const planId = parseInt(req.params.id);
    const sessionId = parseInt(req.params.sessionId);

    const [plan, row] = await Promise.all([
      db.query.plansTable.findFirst({ where: eq(plansTable.id, planId) }),
      db.query.sessionRowsTable.findFirst({ where: eq(sessionRowsTable.id, sessionId) }),
    ]);
    if (!plan || !row) return res.status(404).json({ error: "Plan or session not found" });

    const { duration, venue, scheduledDate, scheduledStartTime, scheduledEndTime, deliveryContext, documentIds } = req.body;

    const content = await generateSessionPlanContent(plan, row, {
      duration, venue, deliveryContext, documentIds,
    });

    // Upsert (delete existing, insert new)
    await db.delete(sessionPlansTable).where(eq(sessionPlansTable.sessionRowId, sessionId));

    const [sp] = await db.insert(sessionPlansTable).values({
      sessionRowId: sessionId,
      planId,
      scheduledDate: scheduledDate ?? null,
      scheduledStartTime: scheduledStartTime ?? null,
      scheduledEndTime: scheduledEndTime ?? null,
      duration: duration ?? row.duration ?? null,
      venue: venue ?? null,
      status: "draft",
      content,
      createdById: userId,
    }).returning();

    return res.status(201).json(mapSessionPlan(sp));
  } catch (err) {
    req.log.error({ err }, "Error generating session plan");
    return res.status(500).json({ error: "Failed to generate session plan" });
  }
});

// GET /api/session-plans/:id
router.get("/session-plans/:id", async (req, res) => {
  try {
    const id = parseInt(req.params.id);
    const sp = await db.query.sessionPlansTable.findFirst({ where: eq(sessionPlansTable.id, id) });
    if (!sp) return res.status(404).json({ error: "Not found" });
    return res.json(mapSessionPlan(sp));
  } catch (err) {
    req.log.error({ err }, "Error getting session plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// PATCH /api/session-plans/:id
router.patch("/session-plans/:id", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const id = parseInt(req.params.id);
    const { scheduledDate, scheduledStartTime, scheduledEndTime, duration, venue, status, content } = req.body;
    const [sp] = await db.update(sessionPlansTable)
      .set({
        ...(scheduledDate !== undefined && { scheduledDate }),
        ...(scheduledStartTime !== undefined && { scheduledStartTime }),
        ...(scheduledEndTime !== undefined && { scheduledEndTime }),
        ...(duration !== undefined && { duration }),
        ...(venue !== undefined && { venue }),
        ...(status !== undefined && { status }),
        ...(content !== undefined && { content }),
      })
      .where(eq(sessionPlansTable.id, id))
      .returning();
    if (!sp) return res.status(404).json({ error: "Not found" });
    return res.json(mapSessionPlan(sp));
  } catch (err) {
    req.log.error({ err }, "Error updating session plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/session-plans/:id/regenerate-section
router.post("/session-plans/:id/regenerate-section", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });

    const id = parseInt(req.params.id);
    const { section, extraInstructions } = req.body as { section: string; extraInstructions?: string };

    const sp = await db.query.sessionPlansTable.findFirst({ where: eq(sessionPlansTable.id, id) });
    if (!sp) return res.status(404).json({ error: "Not found" });

    const [plan, row] = await Promise.all([
      db.query.plansTable.findFirst({ where: eq(plansTable.id, sp.planId) }),
      db.query.sessionRowsTable.findFirst({ where: eq(sessionRowsTable.id, sp.sessionRowId) }),
    ]);
    if (!plan || !row) return res.status(404).json({ error: "Plan or session not found" });

    const existingContent = (sp.content ?? {}) as SessionPlanContent;

    const prompt = `You are a TVET curriculum specialist. Regenerate ONLY the "${section}" section of this session plan.

CONTEXT:
- Unit: ${plan.unitTitle} (${plan.unitCode ?? "N/A"})
- Session: Week ${row.weekNumber}, Session ${row.sessionNumber}
- Topic: ${row.topic ?? "N/A"}
- Learning Outcome: ${row.learningOutcome ?? "N/A"}
- Duration: ${sp.duration ?? "2 hours"}
- Delivery Mode: ${row.deliveryMode ?? "classroom"}
${extraInstructions ? `\nEXTRA INSTRUCTIONS: ${extraInstructions}` : ""}

EXISTING CONTENT (for context):
${JSON.stringify(existingContent, null, 2).slice(0, 2000)}

Generate ONLY the "${section}" section value. Match the expected type exactly:
- bridge_in → { attentionGrabber, scenario, priorKnowledgeActivity }
- pre_assessment → string[]
- participatory_stages → Array of { title, duration, trainerActivity, traineeActivity, teachingMethod, learningCheck }
- post_assessment → string[]
- summary → { recap, keyTakeaways: string[], assignment, nextLessonPrep }
- reflection_prompts → string[]

Respond with JSON: { "${section}": <regenerated_value> }`;

    const completion = await openai.chat.completions.create({
      model: "gpt-4o",
      response_format: { type: "json_object" },
      temperature: 0.7,
      messages: [{ role: "user", content: prompt }],
    });

    const raw = JSON.parse(completion.choices[0]?.message?.content ?? "{}");
    const updatedContent = { ...existingContent, ...raw };

    const [updated] = await db.update(sessionPlansTable)
      .set({ content: updatedContent })
      .where(eq(sessionPlansTable.id, id))
      .returning();

    return res.json(mapSessionPlan(updated));
  } catch (err) {
    req.log.error({ err }, "Error regenerating section");
    return res.status(500).json({ error: "Failed to regenerate section" });
  }
});

// POST /api/plans/:id/generate-all-session-plans (batch)
router.post("/plans/:id/generate-all-session-plans", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });

    const planId = parseInt(req.params.id);
    const { duration, venue, deliveryContext, documentIds } = req.body;

    const [plan, rows] = await Promise.all([
      db.query.plansTable.findFirst({ where: eq(plansTable.id, planId) }),
      db.select().from(sessionRowsTable).where(eq(sessionRowsTable.planId, planId)),
    ]);
    if (!plan) return res.status(404).json({ error: "Plan not found" });
    if (rows.length === 0) return res.json({ success: true, generated: 0, failed: 0, sessionPlanIds: [] });

    // Limit batch to avoid timeout
    const toProcess = rows.slice(0, 20);
    let generated = 0;
    let failed = 0;
    const ids: number[] = [];

    for (const row of toProcess) {
      try {
        const content = await generateSessionPlanContent(plan, row, {
          duration, venue, deliveryContext, documentIds,
        });
        await db.delete(sessionPlansTable).where(eq(sessionPlansTable.sessionRowId, row.id));
        const [sp] = await db.insert(sessionPlansTable).values({
          sessionRowId: row.id,
          planId,
          duration: duration ?? row.duration ?? null,
          venue: venue ?? null,
          status: "draft",
          content,
          createdById: userId,
        }).returning();
        ids.push(sp.id);
        generated++;
      } catch {
        failed++;
      }
    }

    return res.json({ success: true, generated, failed, sessionPlanIds: ids });
  } catch (err) {
    req.log.error({ err }, "Error batch generating session plans");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// GET /api/session-plans/:id/download (DOCX)
router.get("/session-plans/:id/download", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });

    const id = parseInt(req.params.id);
    const sp = await db.query.sessionPlansTable.findFirst({ where: eq(sessionPlansTable.id, id) });
    if (!sp) return res.status(404).json({ error: "Not found" });

    const [plan, row] = await Promise.all([
      db.query.plansTable.findFirst({ where: eq(plansTable.id, sp.planId) }),
      db.query.sessionRowsTable.findFirst({ where: eq(sessionRowsTable.id, sp.sessionRowId) }),
    ]);
    if (!plan || !row) return res.status(404).json({ error: "Plan or session not found" });

    const institution = plan.institutionId
      ? (await db.query.institutionsTable.findFirst({ where: eq(institutionsTable.id, plan.institutionId) }) ?? null)
      : null;

    const buf = await buildSessionPlanDocx(plan, row, sp, institution);
    const filename = `session-plan-w${row.weekNumber}s${row.sessionNumber}-${(row.topic ?? "plan").replace(/[^a-z0-9]/gi, "_")}.docx`;

    res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
    res.setHeader("Content-Length", buf.length);
    return res.send(buf);
  } catch (err) {
    req.log.error({ err }, "Error downloading session plan");
    return res.status(500).json({ error: "Failed to generate document" });
  }
});

export default router;
