import { Router } from "express";
import { getAuth } from "@clerk/express";
import { db } from "@workspace/db";
import { plansTable, sessionRowsTable, approvalsTable, commentsTable, usersTable, documentsTable } from "@workspace/db";
import {
  CreatePlanBody, DeletePlanParams, GeneratePlanBody, GeneratePlanParams,
  DuplicatePlanParams, ExportPlanBody, ExportPlanParams, GetPlanParams,
  ListPlansQueryParams, UpdatePlanBody, UpdatePlanParams,
  CreateSessionRowBody, CreateSessionRowParams, UpdateSessionRowBody, UpdateSessionRowParams,
  CreateApprovalBody, CreateApprovalParams,
  CreateCommentBody, CreateCommentParams,
} from "@workspace/api-zod";
import { eq, desc, sql } from "drizzle-orm";
import { openai } from "@workspace/integrations-openai-ai-server";
import {
  Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
  AlignmentType, HeadingLevel, BorderStyle, WidthType, ShadingType,
} from "docx";

const router = Router();

function mapPlan(p: typeof plansTable.$inferSelect) {
  return {
    id: p.id,
    unitTitle: p.unitTitle,
    unitCode: p.unitCode,
    level: p.level,
    trainerName: p.trainerName,
    className: p.className,
    numberOfTrainees: p.numberOfTrainees,
    totalDuration: p.totalDuration,
    deliveryMode: p.deliveryMode,
    numberOfSessions: p.numberOfSessions,
    sessionDuration: p.sessionDuration,
    institutionId: p.institutionId,
    departmentId: p.departmentId,
    createdById: p.createdById,
    templateId: p.templateId,
    status: p.status,
    versionNumber: p.versionNumber,
    complianceFlags: p.complianceFlags,
    improvementNotes: p.improvementNotes,
    dateOfPreparation: p.dateOfPreparation,
    dateOfRevision: p.dateOfRevision,
    createdAt: p.createdAt.toISOString(),
    updatedAt: p.updatedAt.toISOString(),
  };
}

function mapSession(s: typeof sessionRowsTable.$inferSelect) {
  return {
    id: s.id,
    planId: s.planId,
    weekNumber: s.weekNumber,
    sessionNumber: s.sessionNumber,
    topic: s.topic,
    subtopic: s.subtopic,
    learningOutcome: s.learningOutcome,
    trainerActivity: s.trainerActivity,
    traineeActivity: s.traineeActivity,
    resources: s.resources,
    assessmentMethod: s.assessmentMethod,
    duration: s.duration,
    performanceCriteria: s.performanceCriteria,
    evidenceRequired: s.evidenceRequired,
    deliveryMode: s.deliveryMode,
    practicalExercise: s.practicalExercise,
    reflection: s.reflection,
    notes: s.notes,
    isLocked: s.isLocked,
    createdAt: s.createdAt.toISOString(),
    updatedAt: s.updatedAt.toISOString(),
  };
}

function mapApproval(a: typeof approvalsTable.$inferSelect) {
  return {
    id: a.id,
    planId: a.planId,
    stage: a.stage,
    status: a.status,
    approverUserId: a.approverUserId,
    approverName: a.approverName,
    comments: a.comments,
    signatureText: a.signatureText,
    createdAt: a.createdAt.toISOString(),
  };
}

function mapComment(c: typeof commentsTable.$inferSelect) {
  return {
    id: c.id,
    planId: c.planId,
    userId: c.userId,
    userName: c.userName,
    content: c.content,
    sessionRowId: c.sessionRowId,
    createdAt: c.createdAt.toISOString(),
  };
}

// ── Plans ───────────────────────────────────────────────────────────────────

router.get("/plans", async (req, res) => {
  try {
    const query = ListPlansQueryParams.parse(req.query);
    let plans = await db.select().from(plansTable).orderBy(desc(plansTable.createdAt));
    if (query.status) plans = plans.filter(p => p.status === query.status);
    if (query.institutionId) plans = plans.filter(p => p.institutionId === query.institutionId);
    if (query.createdById) plans = plans.filter(p => p.createdById === query.createdById);
    return res.json(plans.map(mapPlan));
  } catch (err) {
    req.log.error({ err }, "Error listing plans");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.post("/plans", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const body = CreatePlanBody.parse(req.body);
    const user = await db.query.usersTable.findFirst({ where: eq(usersTable.clerkUserId, userId) });
    const [plan] = await db.insert(plansTable).values({
      ...body,
      createdById: userId,
      institutionId: body.institutionId ?? (user?.institutionId ?? null),
      departmentId: body.departmentId ?? (user?.departmentId ?? null),
    }).returning();
    return res.status(201).json(mapPlan(plan));
  } catch (err) {
    req.log.error({ err }, "Error creating plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.get("/plans/:id", async (req, res) => {
  try {
    const { id } = GetPlanParams.parse({ id: parseInt(req.params.id) });
    const plan = await db.query.plansTable.findFirst({ where: eq(plansTable.id, id) });
    if (!plan) return res.status(404).json({ error: "Not found" });

    const [sessions, approvals, comments] = await Promise.all([
      db.select().from(sessionRowsTable).where(eq(sessionRowsTable.planId, id)),
      db.select().from(approvalsTable).where(eq(approvalsTable.planId, id)),
      db.select().from(commentsTable).where(eq(commentsTable.planId, id)),
    ]);

    return res.json({
      ...mapPlan(plan),
      sessions: sessions.map(mapSession),
      approvals: approvals.map(mapApproval),
      comments: comments.map(mapComment),
    });
  } catch (err) {
    req.log.error({ err }, "Error getting plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.patch("/plans/:id", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = UpdatePlanParams.parse({ id: parseInt(req.params.id) });
    const body = UpdatePlanBody.parse(req.body);
    const [plan] = await db.update(plansTable).set(body).where(eq(plansTable.id, id)).returning();
    if (!plan) return res.status(404).json({ error: "Not found" });
    return res.json(mapPlan(plan));
  } catch (err) {
    req.log.error({ err }, "Error updating plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.delete("/plans/:id", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = DeletePlanParams.parse({ id: parseInt(req.params.id) });
    await db.delete(plansTable).where(eq(plansTable.id, id));
    return res.status(204).send();
  } catch (err) {
    req.log.error({ err }, "Error deleting plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// ── AI Generate ─────────────────────────────────────────────────────────────

router.post("/plans/:id/generate", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = GeneratePlanParams.parse({ id: parseInt(req.params.id) });
    const body = GeneratePlanBody.parse(req.body);

    const plan = await db.query.plansTable.findFirst({ where: eq(plansTable.id, id) });
    if (!plan) return res.status(404).json({ error: "Plan not found" });

    let documentContext = "";
    if (body.documentIds && body.documentIds.length > 0) {
      const docs = await db.select().from(documentsTable)
        .where(sql`${documentsTable.id} = ANY(ARRAY[${sql.join(body.documentIds.map(did => sql`${did}`), sql`, `)}]::int[])`);
      documentContext = docs
        .filter(d => d.extractedText)
        .map(d => `[Document: ${d.title}]\n${d.extractedText}`)
        .join("\n\n");
    }

    const sessionCount = plan.numberOfSessions ?? 12;
    const systemPrompt = `You are an expert CDACC/CBET curriculum developer for Kenyan TVET institutions.
Generate a detailed, structured session plan following CDACC standards.
Return a JSON object with a "sessions" array. Each session must have these fields:
weekNumber (int), sessionNumber (int), topic, subtopic, learningOutcome,
trainerActivity, traineeActivity, resources, assessmentMethod, duration,
performanceCriteria, evidenceRequired, deliveryMode, practicalExercise, notes.

Focus on competency-based education (CBE) principles. Ensure:
- Clear learning outcomes aligned to performance criteria
- Practical exercises for hands-on competency development
- Assessment methods that evaluate competency attainment
- Industry-relevant resources and activities`;

    const userPrompt = `Generate ${sessionCount} sessions for a CDACC learning/session plan:
Unit Title: ${plan.unitTitle}
Unit Code: ${plan.unitCode ?? "N/A"}
Level: ${plan.level ?? "TVET Level 4"}
Delivery Mode: ${plan.deliveryMode ?? "Face-to-face"}
Session Duration: ${plan.sessionDuration ?? "2 hours"}
Total Duration: ${plan.totalDuration ?? "12 weeks"}
${body.specificOutcomes ? `Specific Outcomes: ${body.specificOutcomes}` : ""}
${body.practicalEmphasis ? `Practical Emphasis: ${body.practicalEmphasis}` : ""}
${body.assessmentStyle ? `Assessment Style: ${body.assessmentStyle}` : ""}
${body.weeklyStructure ? `Weekly Structure: ${body.weeklyStructure}` : ""}
${documentContext ? `\nReference Documents:\n${documentContext}` : ""}

Return a JSON object: { "sessions": [...] }`;

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

    const rawContent = completion.choices[0]?.message?.content ?? "{}";
    let sessions: Array<Record<string, unknown>> = [];
    try {
      const parsed = JSON.parse(rawContent);
      sessions = Array.isArray(parsed) ? parsed : (Array.isArray(parsed.sessions) ? parsed.sessions : []);
    } catch {
      sessions = [];
    }

    await db.delete(sessionRowsTable)
      .where(sql`${sessionRowsTable.planId} = ${id} AND ${sessionRowsTable.isLocked} = false`);

    const insertedSessions = sessions.length > 0
      ? await db.insert(sessionRowsTable).values(
          sessions.map((s, i) => ({
            planId: id,
            weekNumber: typeof s.weekNumber === "number" ? s.weekNumber : Math.floor(i / 2) + 1,
            sessionNumber: typeof s.sessionNumber === "number" ? s.sessionNumber : (i % 2) + 1,
            topic: typeof s.topic === "string" ? s.topic : null,
            subtopic: typeof s.subtopic === "string" ? s.subtopic : null,
            learningOutcome: typeof s.learningOutcome === "string" ? s.learningOutcome : null,
            trainerActivity: typeof s.trainerActivity === "string" ? s.trainerActivity : null,
            traineeActivity: typeof s.traineeActivity === "string" ? s.traineeActivity : null,
            resources: typeof s.resources === "string" ? s.resources : null,
            assessmentMethod: typeof s.assessmentMethod === "string" ? s.assessmentMethod : null,
            duration: typeof s.duration === "string" ? s.duration : null,
            performanceCriteria: typeof s.performanceCriteria === "string" ? s.performanceCriteria : null,
            evidenceRequired: typeof s.evidenceRequired === "string" ? s.evidenceRequired : null,
            deliveryMode: typeof s.deliveryMode === "string" ? s.deliveryMode : (plan.deliveryMode ?? null),
            practicalExercise: typeof s.practicalExercise === "string" ? s.practicalExercise : null,
            reflection: typeof s.reflection === "string" ? s.reflection : null,
            notes: typeof s.notes === "string" ? s.notes : null,
          }))
        ).returning()
      : [];

    await db.update(plansTable).set({
      complianceFlags: sessions.map(s => s.performanceCriteria).filter(Boolean).join("; ").slice(0, 2000),
    }).where(eq(plansTable.id, id));

    return res.json({
      success: true,
      sessions: insertedSessions.map(mapSession),
      complianceFlags: [],
      improvementNotes: [],
      sourcedDocuments: body.documentIds ? body.documentIds.map(String) : [],
    });
  } catch (err) {
    req.log.error({ err }, "Error generating plan");
    return res.status(500).json({ error: "Failed to generate plan" });
  }
});

// ── Duplicate ────────────────────────────────────────────────────────────────

router.post("/plans/:id/duplicate", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = DuplicatePlanParams.parse({ id: parseInt(req.params.id) });

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

    const { id: _id, createdAt: _ca, updatedAt: _ua, ...rest } = original;
    const [newPlan] = await db.insert(plansTable).values({
      ...rest,
      unitTitle: `${original.unitTitle} (Copy)`,
      status: "draft",
      versionNumber: 1,
      createdById: userId,
    }).returning();

    const sessions = await db.select().from(sessionRowsTable).where(eq(sessionRowsTable.planId, id));
    if (sessions.length > 0) {
      await db.insert(sessionRowsTable).values(
        sessions.map(s => {
          const { id: _sid, createdAt: _sca, updatedAt: _sua, ...sRest } = s;
          return { ...sRest, planId: newPlan.id };
        })
      );
    }

    return res.status(201).json(mapPlan(newPlan));
  } catch (err) {
    req.log.error({ err }, "Error duplicating plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// ── Export ───────────────────────────────────────────────────────────────────

function cell(text: string, bold = false, shade = false) {
  return new TableCell({
    shading: shade ? { type: ShadingType.CLEAR, fill: "2D6A4F", color: "FFFFFF" } : undefined,
    margins: { top: 80, bottom: 80, left: 100, right: 100 },
    children: [
      new Paragraph({
        children: [
          new TextRun({
            text: text ?? "",
            bold,
            color: shade ? "FFFFFF" : "000000",
            size: 18,
          }),
        ],
      }),
    ],
  });
}

async function buildDocx(
  plan: typeof plansTable.$inferSelect,
  sessions: (typeof sessionRowsTable.$inferSelect)[],
): Promise<Buffer> {
  const headerRow = new TableRow({
    tableHeader: true,
    children: [
      "Week", "Session", "Topic / Subtopic", "Learning Outcome",
      "Trainer Activity", "Trainee Activity", "Resources",
      "Assessment Method", "Performance Criteria",
    ].map((h) => cell(h, true, true)),
  });

  const dataRows = sessions.map(
    (s) =>
      new TableRow({
        children: [
          cell(s.weekNumber?.toString() ?? ""),
          cell(s.sessionNumber?.toString() ?? ""),
          cell([s.topic, s.subtopic].filter(Boolean).join("\n")),
          cell(s.learningOutcome ?? ""),
          cell(s.trainerActivity ?? ""),
          cell(s.traineeActivity ?? ""),
          cell(s.resources ?? ""),
          cell(s.assessmentMethod ?? ""),
          cell(s.performanceCriteria ?? ""),
        ],
      }),
  );

  const metaRows = (pairs: [string, string | null | undefined][]) =>
    pairs.map(
      ([label, value]) =>
        new Paragraph({
          children: [
            new TextRun({ text: `${label}: `, bold: true, size: 20 }),
            new TextRun({ text: value ?? "—", size: 20 }),
          ],
          spacing: { after: 60 },
        }),
    );

  const doc = new Document({
    sections: [
      {
        properties: {
          page: { margin: { top: 720, bottom: 720, left: 900, right: 900 } },
        },
        children: [
          // Title block
          new Paragraph({
            text: "CDACC / CBET SESSION LEARNING PLAN",
            heading: HeadingLevel.HEADING_1,
            alignment: AlignmentType.CENTER,
            spacing: { after: 120 },
          }),
          new Paragraph({
            text: plan.unitTitle ?? "Untitled Plan",
            heading: HeadingLevel.HEADING_2,
            alignment: AlignmentType.CENTER,
            spacing: { after: 240 },
          }),

          // Plan metadata
          ...metaRows([
            ["Unit Code", plan.unitCode],
            ["Level", plan.level],
            ["Trainer", plan.trainerName],
            ["Class", plan.className],
            ["Number of Trainees", plan.numberOfTrainees?.toString()],
            ["Total Duration", plan.totalDuration],
            ["Session Duration", plan.sessionDuration],
            ["Number of Sessions", plan.numberOfSessions?.toString()],
            ["Delivery Mode", plan.deliveryMode],
            ["Date of Preparation", plan.dateOfPreparation],
            ["Date of Revision", plan.dateOfRevision],
          ]),

          new Paragraph({ text: "", spacing: { after: 200 } }),

          // Session table
          ...(sessions.length > 0
            ? [
                new Paragraph({
                  text: "Session Plan",
                  heading: HeadingLevel.HEADING_2,
                  spacing: { after: 160 },
                }),
                new Table({
                  width: { size: 100, type: WidthType.PERCENTAGE },
                  columnWidths: [500, 500, 1200, 1200, 1200, 1200, 1000, 1100, 1100],
                  borders: {
                    top: { style: BorderStyle.SINGLE, size: 4, color: "2D6A4F" },
                    bottom: { style: BorderStyle.SINGLE, size: 4, color: "2D6A4F" },
                    left: { style: BorderStyle.SINGLE, size: 4, color: "2D6A4F" },
                    right: { style: BorderStyle.SINGLE, size: 4, color: "2D6A4F" },
                    insideHorizontal: { style: BorderStyle.SINGLE, size: 2, color: "CCCCCC" },
                    insideVertical: { style: BorderStyle.SINGLE, size: 2, color: "CCCCCC" },
                  },
                  rows: [headerRow, ...dataRows],
                }),
              ]
            : [
                new Paragraph({
                  text: "No sessions have been generated yet.",
                  spacing: { after: 200 },
                }),
              ]),

          // Compliance notes
          ...(plan.complianceFlags
            ? [
                new Paragraph({ text: "", spacing: { after: 200 } }),
                new Paragraph({
                  text: "Performance Criteria / Compliance Notes",
                  heading: HeadingLevel.HEADING_2,
                  spacing: { after: 120 },
                }),
                new Paragraph({
                  children: [new TextRun({ text: plan.complianceFlags, size: 18 })],
                }),
              ]
            : []),

          // Signature block
          new Paragraph({ text: "", spacing: { after: 400 } }),
          ...metaRows([
            ["Trainer Signature", ""],
            ["HOD Signature", ""],
            ["IQA Signature", ""],
            ["Date", new Date().toLocaleDateString("en-KE")],
          ]),
        ],
      },
    ],
  });

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

router.post("/plans/:id/export", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = ExportPlanParams.parse({ id: parseInt(req.params.id) });
    const body = ExportPlanBody.parse(req.body);

    const plan = await db.query.plansTable.findFirst({ where: eq(plansTable.id, id) });
    if (!plan) return res.status(404).json({ error: "Plan not found" });

    const downloadUrl = `/api/plans/${id}/download?format=${body.format}&t=${Date.now()}`;
    return res.json({ success: true, downloadUrl, format: body.format });
  } catch (err) {
    req.log.error({ err }, "Error exporting plan");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// Real DOCX download — streams the generated file directly
router.get("/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);
    if (isNaN(id)) return res.status(400).json({ error: "Invalid plan id" });

    const [plan, sessions] = await Promise.all([
      db.query.plansTable.findFirst({ where: eq(plansTable.id, id) }),
      db.select().from(sessionRowsTable).where(eq(sessionRowsTable.planId, id)),
    ]);

    if (!plan) return res.status(404).json({ error: "Plan not found" });

    const buf = await buildDocx(plan, sessions);
    const filename = `${(plan.unitTitle ?? "learning-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 generating DOCX");
    return res.status(500).json({ error: "Failed to generate document" });
  }
});

// ── Session Rows ─────────────────────────────────────────────────────────────

router.get("/plans/:id/sessions", async (req, res) => {
  try {
    const { id } = CreateSessionRowParams.parse({ id: parseInt(req.params.id) });
    const sessions = await db.select().from(sessionRowsTable).where(eq(sessionRowsTable.planId, id));
    return res.json(sessions.map(mapSession));
  } catch (err) {
    req.log.error({ err }, "Error listing sessions");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.post("/plans/:id/sessions", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = CreateSessionRowParams.parse({ id: parseInt(req.params.id) });
    const body = CreateSessionRowBody.parse(req.body);
    const [session] = await db.insert(sessionRowsTable).values({ ...body, planId: id }).returning();
    return res.status(201).json(mapSession(session));
  } catch (err) {
    req.log.error({ err }, "Error creating session");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.patch("/plans/:id/sessions/:sessionId", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { sessionId } = UpdateSessionRowParams.parse({
      id: parseInt(req.params.id),
      sessionId: parseInt(req.params.sessionId),
    });
    const body = UpdateSessionRowBody.parse(req.body);
    const [session] = await db.update(sessionRowsTable)
      .set(body)
      .where(eq(sessionRowsTable.id, sessionId))
      .returning();
    if (!session) return res.status(404).json({ error: "Not found" });
    return res.json(mapSession(session));
  } catch (err) {
    req.log.error({ err }, "Error updating session");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.delete("/plans/:id/sessions/:sessionId", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { sessionId } = UpdateSessionRowParams.parse({
      id: parseInt(req.params.id),
      sessionId: parseInt(req.params.sessionId),
    });
    await db.delete(sessionRowsTable).where(eq(sessionRowsTable.id, sessionId));
    return res.status(204).send();
  } catch (err) {
    req.log.error({ err }, "Error deleting session");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.post("/plans/:id/sessions/:sessionId/regenerate", 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, session] = await Promise.all([
      db.query.plansTable.findFirst({ where: eq(plansTable.id, planId) }),
      db.query.sessionRowsTable.findFirst({ where: eq(sessionRowsTable.id, sessionId) }),
    ]);

    if (!plan || !session) return res.status(404).json({ error: "Not found" });

    const completion = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [
        {
          role: "system",
          content: "You are a CDACC/CBET curriculum expert. Regenerate a single session row. Return a JSON object with fields: topic, subtopic, learningOutcome, trainerActivity, traineeActivity, resources, assessmentMethod, duration, performanceCriteria, evidenceRequired, practicalExercise, notes.",
        },
        {
          role: "user",
          content: `Regenerate session ${session.sessionNumber} of week ${session.weekNumber} for unit: ${plan.unitTitle} (${plan.unitCode ?? "N/A"}).
Current topic: ${session.topic ?? "N/A"}. Make it more aligned to CDACC competency standards.
Return ONLY a JSON object, no extra text.`,
        },
      ],
      response_format: { type: "json_object" },
      temperature: 0.8,
    });

    const raw = JSON.parse(completion.choices[0]?.message?.content ?? "{}") as Record<string, unknown>;
    const [updated] = await db.update(sessionRowsTable)
      .set({
        topic: typeof raw.topic === "string" ? raw.topic : session.topic,
        subtopic: typeof raw.subtopic === "string" ? raw.subtopic : session.subtopic,
        learningOutcome: typeof raw.learningOutcome === "string" ? raw.learningOutcome : session.learningOutcome,
        trainerActivity: typeof raw.trainerActivity === "string" ? raw.trainerActivity : session.trainerActivity,
        traineeActivity: typeof raw.traineeActivity === "string" ? raw.traineeActivity : session.traineeActivity,
        resources: typeof raw.resources === "string" ? raw.resources : session.resources,
        assessmentMethod: typeof raw.assessmentMethod === "string" ? raw.assessmentMethod : session.assessmentMethod,
        duration: typeof raw.duration === "string" ? raw.duration : session.duration,
        performanceCriteria: typeof raw.performanceCriteria === "string" ? raw.performanceCriteria : session.performanceCriteria,
        evidenceRequired: typeof raw.evidenceRequired === "string" ? raw.evidenceRequired : session.evidenceRequired,
        practicalExercise: typeof raw.practicalExercise === "string" ? raw.practicalExercise : session.practicalExercise,
        notes: typeof raw.notes === "string" ? raw.notes : session.notes,
      })
      .where(eq(sessionRowsTable.id, sessionId))
      .returning();

    return res.json(mapSession(updated));
  } catch (err) {
    req.log.error({ err }, "Error regenerating session");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// ── Approvals ────────────────────────────────────────────────────────────────

router.get("/plans/:id/approvals", async (req, res) => {
  try {
    const { id } = CreateApprovalParams.parse({ id: parseInt(req.params.id) });
    const approvals = await db.select().from(approvalsTable).where(eq(approvalsTable.planId, id));
    return res.json(approvals.map(mapApproval));
  } catch (err) {
    req.log.error({ err }, "Error listing approvals");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.post("/plans/:id/approvals", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = CreateApprovalParams.parse({ id: parseInt(req.params.id) });
    const body = CreateApprovalBody.parse(req.body);

    const [approval] = await db.insert(approvalsTable).values({
      planId: id,
      stage: body.stage,
      status: body.status,
      approverUserId: userId,
      approverName: body.approverName,
      comments: body.comments,
      signatureText: body.signatureText,
    }).returning();

    const statusMap: Record<string, string> = {
      "trainer_approved": "hod_review",
      "hod_approved": "iqa_review",
      "iqa_approved": "approved",
      "trainer_rejected": "rejected",
      "hod_rejected": "rejected",
      "iqa_rejected": "rejected",
    };
    const newStatus = statusMap[`${body.stage}_${body.status}`];
    if (newStatus) {
      await db.update(plansTable).set({ status: newStatus }).where(eq(plansTable.id, id));
    }

    return res.status(201).json(mapApproval(approval));
  } catch (err) {
    req.log.error({ err }, "Error creating approval");
    return res.status(500).json({ error: "Internal server error" });
  }
});

// ── Comments ─────────────────────────────────────────────────────────────────

router.get("/plans/:id/comments", async (req, res) => {
  try {
    const { id } = CreateCommentParams.parse({ id: parseInt(req.params.id) });
    const comments = await db.select().from(commentsTable).where(eq(commentsTable.planId, id));
    return res.json(comments.map(mapComment));
  } catch (err) {
    req.log.error({ err }, "Error listing comments");
    return res.status(500).json({ error: "Internal server error" });
  }
});

router.post("/plans/:id/comments", async (req, res) => {
  try {
    const { userId } = getAuth(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    const { id } = CreateCommentParams.parse({ id: parseInt(req.params.id) });
    const body = CreateCommentBody.parse(req.body);

    const user = await db.query.usersTable.findFirst({ where: eq(usersTable.clerkUserId, userId) });
    const fullName = [user?.firstName, user?.lastName].filter(Boolean).join(" ") || "Anonymous";

    const [comment] = await db.insert(commentsTable).values({
      planId: id,
      userId,
      userName: body.userName ?? fullName,
      content: body.content,
      sessionRowId: body.sessionRowId,
    }).returning();

    return res.status(201).json(mapComment(comment));
  } catch (err) {
    req.log.error({ err }, "Error creating comment");
    return res.status(500).json({ error: "Internal server error" });
  }
});

export default router;
