import type { Hono } from "hono";
import { z } from "zod";
import * as cronStore from "../cron-store.js";
import type { CronScheduler } from "../cron-scheduler.js";
import { parseBody } from "../validation.js";

// ─── Zod Schemas ──────────────────────────────────────────────────

const cronJobCreateSchema = z.object({
  name: z.string().max(200).optional().default(""),
  prompt: z.string().max(50_000).optional().default(""),
  schedule: z.string().max(200).optional().default(""),
  recurring: z.boolean().optional().default(true),
  backendType: z.enum(["claude", "codex"]).optional().default("claude"),
  model: z.string().max(200).optional().default(""),
  cwd: z.string().max(1000).optional().default(""),
  envSlug: z.string().max(200).optional(),
  enabled: z.boolean().optional().default(true),
  permissionMode: z.string().max(100).optional().default("bypassPermissions"),
  codexInternetAccess: z.boolean().optional(),
});

const cronJobUpdateSchema = z.object({
  name: z.string().max(200).optional(),
  prompt: z.string().max(50_000).optional(),
  schedule: z.string().max(200).optional(),
  recurring: z.boolean().optional(),
  backendType: z.enum(["claude", "codex"]).optional(),
  model: z.string().max(200).optional(),
  cwd: z.string().max(1000).optional(),
  envSlug: z.string().max(200).optional(),
  enabled: z.boolean().optional(),
  permissionMode: z.string().max(100).optional(),
  codexInternetAccess: z.boolean().optional(),
});

export function registerCronRoutes(
  api: Hono,
  cronScheduler?: CronScheduler,
): void {
  api.get("/cron/jobs", (c) => {
    const jobs = cronStore.listJobs();
    const enriched = jobs.map((j) => ({
      ...j,
      nextRunAt: cronScheduler?.getNextRunTime(j.id)?.getTime() ?? null,
    }));
    return c.json(enriched);
  });

  api.get("/cron/jobs/:id", (c) => {
    const job = cronStore.getJob(c.req.param("id"));
    if (!job) return c.json({ error: "Job not found" }, 404);
    return c.json({
      ...job,
      nextRunAt: cronScheduler?.getNextRunTime(job.id)?.getTime() ?? null,
    });
  });

  api.post("/cron/jobs", async (c) => {
    const parsed = await parseBody(c, cronJobCreateSchema);
    if (!parsed.success) return parsed.response;
    const body = parsed.data;
    try {
      const job = cronStore.createJob({
        name: body.name,
        prompt: body.prompt,
        schedule: body.schedule,
        recurring: body.recurring,
        backendType: body.backendType,
        model: body.model,
        cwd: body.cwd,
        envSlug: body.envSlug,
        enabled: body.enabled,
        permissionMode: body.permissionMode,
        codexInternetAccess: body.codexInternetAccess,
      });
      if (job.enabled) cronScheduler?.scheduleJob(job);
      return c.json(job, 201);
    } catch (e: unknown) {
      return c.json({ error: e instanceof Error ? e.message : String(e) }, 400);
    }
  });

  api.put("/cron/jobs/:id", async (c) => {
    const id = c.req.param("id");
    const parsed = await parseBody(c, cronJobUpdateSchema);
    if (!parsed.success) return parsed.response;
    try {
      // Only allow user-editable fields — prevent tampering with internal tracking
      const allowed: Record<string, unknown> = {};
      for (const key of ["name", "prompt", "schedule", "recurring", "backendType", "model", "cwd", "envSlug", "enabled", "permissionMode", "codexInternetAccess"] as const) {
        if (key in parsed.data) allowed[key] = parsed.data[key as keyof typeof parsed.data];
      }
      const job = cronStore.updateJob(id, allowed);
      if (!job) return c.json({ error: "Job not found" }, 404);
      // Stop the old timer (id may differ from job.id after a rename)
      if (job.id !== id) cronScheduler?.stopJob(id);
      cronScheduler?.scheduleJob(job);
      return c.json(job);
    } catch (e: unknown) {
      return c.json({ error: e instanceof Error ? e.message : String(e) }, 400);
    }
  });

  api.delete("/cron/jobs/:id", (c) => {
    const id = c.req.param("id");
    cronScheduler?.stopJob(id);
    const deleted = cronStore.deleteJob(id);
    if (!deleted) return c.json({ error: "Job not found" }, 404);
    return c.json({ ok: true });
  });

  api.post("/cron/jobs/:id/toggle", (c) => {
    const id = c.req.param("id");
    const job = cronStore.getJob(id);
    if (!job) return c.json({ error: "Job not found" }, 404);
    const updated = cronStore.updateJob(id, { enabled: !job.enabled });
    if (updated?.enabled) {
      cronScheduler?.scheduleJob(updated);
    } else {
      cronScheduler?.stopJob(id);
    }
    return c.json(updated);
  });

  api.post("/cron/jobs/:id/run", (c) => {
    const id = c.req.param("id");
    const job = cronStore.getJob(id);
    if (!job) return c.json({ error: "Job not found" }, 404);
    cronScheduler?.executeJobManually(id);
    return c.json({ ok: true, message: "Job triggered" });
  });

  api.get("/cron/jobs/:id/executions", (c) => {
    const id = c.req.param("id");
    return c.json(cronScheduler?.getExecutions(id) ?? []);
  });
}
