import type { FastifyInstance } from 'fastify';
import { requireJwtOrApiKey } from '../../lib/api-key-auth.js';
import { BadRequestError } from '../../lib/errors.js';
import { TriggerJobSchema, UpdateJobSchema } from './types.js';
import * as repo from './repository.js';
import { getJobHandler } from './registry.js';
import { executeJob } from './runner.js';

export async function jobRoutes(app: FastifyInstance) {
  async function requireJobsRead(req: import('fastify').FastifyRequest): Promise<string> {
    const access = await requireJwtOrApiKey(req, {
      allowJwt: true,
      apiKeyScopes: ['jobs:read'],
      apiKeyTokenTypes: ['service_api'],
      rateLimitKey: 'jobs:read',
    });
    return access.productId;
  }

  async function requireJobsWrite(req: import('fastify').FastifyRequest): Promise<string> {
    const access = await requireJwtOrApiKey(req, {
      jwtRoles: ['super_admin', 'admin'],
      apiKeyScopes: ['jobs:write'],
      apiKeyTokenTypes: ['service_api'],
      rateLimitKey: 'jobs:write',
    });
    return access.productId;
  }

  // List all job definitions
  app.get('/jobs', async req => {
    const productId = await requireJobsRead(req);
    return repo.listJobDefinitions(productId);
  });

  // Get a specific job definition
  app.get('/jobs/:id', async req => {
    const productId = await requireJobsRead(req);
    const { id } = req.params as { id: string };
    return repo.getJobDefinition(id, productId);
  });

  // Update job (enable/disable, change cron, etc.)
  app.put('/jobs/:id', async req => {
    const productId = await requireJobsWrite(req);
    const { id } = req.params as { id: string };
    const parsed = UpdateJobSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }
    return repo.updateJobDefinition(id, productId, parsed.data);
  });

  // Manually trigger a job
  app.post('/jobs/trigger', async req => {
    const productId = await requireJobsWrite(req);
    const parsed = TriggerJobSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    const { jobName } = parsed.data;
    const handler = getJobHandler(jobName);
    if (!handler) {
      throw new BadRequestError(`No handler registered for job '${jobName}'`);
    }

    const def = await repo.getJobDefinition(`job_${jobName}`, productId);
    const run = await executeJob(def, 'manual', req.log);
    return run;
  });

  // List recent runs for a job
  app.get('/jobs/:name/runs', async req => {
    const productId = await requireJobsRead(req);
    const { name } = req.params as { name: string };
    const limit = parseInt((req.query as Record<string, string>).limit || '20', 10);
    return repo.listJobRuns(productId, name, Math.min(limit, 100));
  });
}
