import { Hono } from "hono";
import { and, asc, eq } from "drizzle-orm";
import { z } from "zod";

import { diagram, project } from "@turbostarter/db/schema";
import { db } from "@turbostarter/db/server";
import { HttpStatusCode } from "@turbostarter/shared/constants";
import { HttpException } from "@turbostarter/shared/utils";

import { enforceAuth, validate } from "../../middleware";

export const createProjectSchema = z.object({
  name: z.string().min(1).max(100),
});

export const updateProjectSchema = z
  .object({
    name: z.string().min(1).max(100).optional(),
    sortOrder: z.number().int().optional(),
  })
  .refine((data) => data.name !== undefined || data.sortOrder !== undefined, {
    message: "At least one field (name or sortOrder) must be provided",
  });

export const projectRouter = new Hono()
  .get("/", enforceAuth, async (c) => {
    const projects = await db
      .select()
      .from(project)
      .where(eq(project.userId, c.var.user.id))
      .orderBy(asc(project.sortOrder));

    return c.json({ data: projects });
  })
  .post(
    "/",
    enforceAuth,
    validate("json", createProjectSchema),
    async (c) => {
      const input = c.req.valid("json");
      const [created] = await db
        .insert(project)
        .values({
          ...input,
          userId: c.var.user.id,
        })
        .returning();

      return c.json({ data: created });
    },
  )
  .patch(
    "/:id",
    enforceAuth,
    validate("json", updateProjectSchema),
    async (c) => {
      const [existing] = await db
        .select()
        .from(project)
        .where(
          and(
            eq(project.id, c.req.param("id")),
            eq(project.userId, c.var.user.id),
          ),
        );

      if (!existing) {
        throw new HttpException(HttpStatusCode.NOT_FOUND, {
          code: "error.notFound",
        });
      }

      const [updated] = await db
        .update(project)
        .set(c.req.valid("json"))
        .where(eq(project.id, c.req.param("id")))
        .returning();

      return c.json({ data: updated });
    },
  )
  .delete("/:id", enforceAuth, async (c) => {
    const [existing] = await db
      .select()
      .from(project)
      .where(
        and(
          eq(project.id, c.req.param("id")),
          eq(project.userId, c.var.user.id),
        ),
      );

    if (!existing) {
      throw new HttpException(HttpStatusCode.NOT_FOUND, {
        code: "error.notFound",
      });
    }

    await db.transaction(async (tx) => {
      // Move diagrams to Unorganized (null projectId)
      await tx
        .update(diagram)
        .set({ projectId: null })
        .where(eq(diagram.projectId, c.req.param("id")));

      // Delete the project
      await tx.delete(project).where(eq(project.id, c.req.param("id")));
    });

    return c.json({ data: { success: true } });
  });
