import { db, schema } from "@superlog/db";
import { eq } from "drizzle-orm";
import type { Context, Hono } from "hono";
import { HTTPException } from "hono/http-exception";
import { z } from "zod";
import {
  addDashboardWidget,
  createDashboard,
  dashboardCreateSchema,
  dashboardUpdateSchema,
  dashboardWidgetCreateSchema,
  dashboardWidgetLayoutSchema,
  dashboardWidgetUpdateSchema,
  deleteDashboard,
  deleteDashboardWidget,
  getDashboardWithWidgets,
  listDashboardsForProject,
  updateDashboard,
  updateDashboardLayout,
  updateDashboardWidget,
} from "./dashboards-service.js";
import { resolveActiveOrgContext } from "./org-context.js";

type Vars = { userId: string; orgId: string | null };

export function mountDashboards(app: Hono<{ Variables: Vars }>) {
  const requireAccess = async (c: Context<{ Variables: Vars }>, projectId: string) => {
    const project = await db.query.projects.findFirst({
      where: eq(schema.projects.id, projectId),
    });
    if (!project) throw new HTTPException(404, { message: "project not found" });
    const ctx = await resolveActiveOrgContext({
      userId: c.var.userId,
      preferredOrgId: c.var.orgId,
    });
    if (project.orgId !== ctx.org.id) throw new HTTPException(403, { message: "forbidden" });
    return { project, user: ctx.user };
  };

  app.get("/api/projects/:projectId/dashboards", async (c) => {
    const projectId = c.req.param("projectId");
    await requireAccess(c, projectId);
    return c.json(await listDashboardsForProject(projectId));
  });

  app.post("/api/projects/:projectId/dashboards", async (c) => {
    const projectId = c.req.param("projectId");
    const { user } = await requireAccess(c, projectId);
    const body = await c.req.json().catch(() => ({}));
    const parsed = dashboardCreateSchema.safeParse(body);
    if (!parsed.success) throw new HTTPException(400, { message: "invalid body" });
    return c.json(await createDashboard(projectId, user.id, parsed.data));
  });

  app.get("/api/projects/:projectId/dashboards/:id", async (c) => {
    const projectId = c.req.param("projectId");
    const id = c.req.param("id");
    await requireAccess(c, projectId);
    const dashboard = await getDashboardWithWidgets(projectId, id);
    if (!dashboard) throw new HTTPException(404, { message: "dashboard not found" });
    return c.json(dashboard);
  });

  app.patch("/api/projects/:projectId/dashboards/:id", async (c) => {
    const projectId = c.req.param("projectId");
    const id = c.req.param("id");
    await requireAccess(c, projectId);
    const body = await c.req.json().catch(() => ({}));
    const parsed = dashboardUpdateSchema.safeParse(body);
    if (!parsed.success) throw new HTTPException(400, { message: "invalid body" });
    const updated = await updateDashboard(projectId, id, parsed.data);
    if (!updated) throw new HTTPException(404, { message: "dashboard not found" });
    return c.json(updated);
  });

  app.delete("/api/projects/:projectId/dashboards/:id", async (c) => {
    const projectId = c.req.param("projectId");
    const id = c.req.param("id");
    await requireAccess(c, projectId);
    await deleteDashboard(projectId, id);
    return c.json({ ok: true });
  });

  app.post("/api/projects/:projectId/dashboards/:id/widgets", async (c) => {
    const projectId = c.req.param("projectId");
    const id = c.req.param("id");
    await requireAccess(c, projectId);
    const body = await c.req.json().catch(() => ({}));
    const parsed = dashboardWidgetCreateSchema.safeParse(body);
    if (!parsed.success) throw new HTTPException(400, { message: "invalid body" });
    const widget = await addDashboardWidget(projectId, id, parsed.data);
    if (!widget) throw new HTTPException(404, { message: "dashboard not found" });
    return c.json(widget);
  });

  app.patch("/api/projects/:projectId/dashboards/:id/widgets/:widgetId", async (c) => {
    const projectId = c.req.param("projectId");
    const id = c.req.param("id");
    const widgetId = c.req.param("widgetId");
    await requireAccess(c, projectId);
    const body = await c.req.json().catch(() => ({}));
    const parsed = dashboardWidgetUpdateSchema.safeParse(body);
    if (!parsed.success) throw new HTTPException(400, { message: "invalid body" });
    const widget = await updateDashboardWidget(projectId, id, widgetId, parsed.data);
    if (!widget) throw new HTTPException(404, { message: "widget not found" });
    return c.json(widget);
  });

  app.patch("/api/projects/:projectId/dashboards/:id/layout", async (c) => {
    const projectId = c.req.param("projectId");
    const id = c.req.param("id");
    await requireAccess(c, projectId);
    const body = await c.req.json().catch(() => ({}));
    const parsed = z
      .object({
        widgets: z
          .array(z.object({ id: z.string().uuid(), layout: dashboardWidgetLayoutSchema }))
          .max(200),
      })
      .safeParse(body);
    if (!parsed.success) throw new HTTPException(400, { message: "invalid body" });
    const ok = await updateDashboardLayout(projectId, id, parsed.data.widgets);
    if (!ok) throw new HTTPException(404, { message: "dashboard not found" });
    return c.json({ ok: true });
  });

  app.delete("/api/projects/:projectId/dashboards/:id/widgets/:widgetId", async (c) => {
    const projectId = c.req.param("projectId");
    const id = c.req.param("id");
    const widgetId = c.req.param("widgetId");
    await requireAccess(c, projectId);
    const ok = await deleteDashboardWidget(projectId, id, widgetId);
    if (!ok) throw new HTTPException(404, { message: "dashboard not found" });
    return c.json({ ok: true });
  });
}
