import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { createJob, getJob } from '../jobs/queue.js';
import { applyVersion } from '../versioning/apply.js';
import { getPocketBase } from '../lib/pocketbase.js';

const GenerateBodySchema = z.object({
  triggeredBy: z.string(),
  reason: z.enum(['new_topics', 'manual']),
});

const PatchWeekBodySchema = z.object({
  theme: z.string().optional(),
  topics: z.array(z.string()).optional(),
  topic_order: z.array(z.number()).optional(),
  admin_notes: z.string().optional(),
  estimated_duration_minutes: z.number().min(15).max(45).optional(),
});

export async function curriculumRoutes(app: FastifyInstance): Promise<void> {
  // POST /generate
  app.post('/generate', async (request, reply) => {
    const body = GenerateBodySchema.safeParse(request.body);
    if (!body.success) {
      return reply.status(400).send({ error: 'Invalid request body', details: body.error.issues });
    }

    const job = createJob(body.data.triggeredBy, body.data.reason);
    return reply.status(202).send({ jobId: job.id, status: job.status });
  });

  // GET /status/:jobId
  app.get<{ Params: { jobId: string } }>('/status/:jobId', async (request, reply) => {
    const job = getJob(request.params.jobId);
    if (!job) {
      return reply.status(404).send({ error: 'Job not found' });
    }
    return reply.send(job);
  });

  // GET /current
  app.get('/current', async (_request, reply) => {
    const pb = await getPocketBase();

    const versions = await pb.collection('curriculum_versions').getFullList({
      filter: 'status = "active"',
    });

    if (versions.length === 0) {
      return reply.status(404).send({ error: 'No active curriculum version' });
    }

    const version = versions[0]!;
    const weeks = await pb.collection('curriculum_weeks').getFullList({
      filter: `curriculum_version = "${version.id}"`,
      sort: 'week_number',
      expand: 'theme,topics',
    });

    return reply.send({ version, weeks });
  });

  // GET /preview
  app.get('/preview', async (_request, reply) => {
    const pb = await getPocketBase();

    const versions = await pb.collection('curriculum_versions').getFullList({
      filter: 'status = "draft"',
      sort: '-version',
      perPage: 1,
    });

    if (versions.length === 0) {
      return reply.status(404).send({ error: 'No draft curriculum version' });
    }

    const version = versions[0]!;
    const weeks = await pb.collection('curriculum_weeks').getFullList({
      filter: `curriculum_version = "${version.id}"`,
      sort: 'week_number',
      expand: 'theme,topics',
    });

    const themeIds = new Set(weeks.map(w => w['theme'] as string));
    const topicIds = new Set(weeks.flatMap(w => (w['topics'] as string[]) ?? []));

    const coverageStats = {
      themesTotal: themeIds.size,
      themesCovered: themeIds.size,
      topicsTotal: topicIds.size,
      topicsCovered: topicIds.size,
    };

    return reply.send({
      version: version['version'],
      weeks: weeks.map(w => {
        const expand = (w['expand'] as Record<string, unknown>) ?? {};
        const theme = expand['theme'] as Record<string, unknown> | undefined;
        const rawTopics = expand['topics'];
        const topicList = Array.isArray(rawTopics) ? rawTopics : [];

        return {
          weekNumber: w['week_number'],
          theme: theme ? { id: theme['id'], title: theme['title'] } : null,
          topics: topicList.map((t: unknown) => {
            const tr = t as Record<string, unknown>;
            return {
              id: tr['id'],
              title: tr['title'],
              complexityWeight: tr['complexity_weight'],
            };
          }),
          estimatedDurationMinutes: w['estimated_duration_minutes'],
        };
      }),
      coverageStats,
    });
  });

  // PATCH /weeks/:weekId
  app.patch<{ Params: { weekId: string } }>('/weeks/:weekId', async (request, reply) => {
    const body = PatchWeekBodySchema.safeParse(request.body);
    if (!body.success) {
      return reply.status(400).send({ error: 'Invalid request body', details: body.error.issues });
    }

    const pb = await getPocketBase();

    let week;
    try {
      week = await pb.collection('curriculum_weeks').getOne(request.params.weekId);
    } catch {
      return reply.status(404).send({ error: 'Week not found' });
    }

    // Only allow patching draft versions
    const version = await pb.collection('curriculum_versions').getOne(week['curriculum_version'] as string);
    if (version['status'] !== 'draft') {
      return reply.status(409).send({ error: 'Can only edit weeks belonging to a draft version' });
    }

    const updates: Record<string, unknown> = {};
    if (body.data.theme !== undefined) updates['theme'] = body.data.theme;
    if (body.data.topics !== undefined) updates['topics'] = body.data.topics;
    if (body.data.topic_order !== undefined) updates['topic_order'] = body.data.topic_order;
    if (body.data.admin_notes !== undefined) updates['admin_notes'] = body.data.admin_notes;
    if (body.data.estimated_duration_minutes !== undefined) {
      updates['estimated_duration_minutes'] = body.data.estimated_duration_minutes;
    }

    const updated = await pb.collection('curriculum_weeks').update(request.params.weekId, updates);
    return reply.send(updated);
  });

  // POST /confirm
  app.post('/confirm', async (_request, reply) => {
    const pb = await getPocketBase();

    const drafts = await pb.collection('curriculum_versions').getFullList({
      filter: 'status = "draft"',
      sort: '-version',
      perPage: 1,
    });

    if (drafts.length === 0) {
      return reply.status(404).send({ error: 'No draft curriculum to confirm' });
    }

    const draftId = drafts[0]!.id;
    await applyVersion(draftId);

    return reply.send({ applied: draftId, status: 'active' });
  });
}
