import { zValidator } from "@hono/zod-validator";
import { db, eq, and, desc } from "@haushaltsApp/db";
import { shoppingLists, shoppingListItems } from "@haushaltsApp/db/schema";
import { Hono } from "hono";
import { z } from "zod";
import { authMiddleware, requireAuth, type AuthVariables } from "../middleware/auth.middleware";
import { tenantMiddleware, requireHousehold, type TenantVariables } from "../middleware/tenant.middleware";

type Variables = AuthVariables & TenantVariables;

export const shoppingListRoutes = new Hono<{ Variables: Variables }>();
shoppingListRoutes.use("/*", authMiddleware, requireAuth, tenantMiddleware, requireHousehold);

async function getOrCreateActiveList(householdId: string) {
  const existing = await db
    .select()
    .from(shoppingLists)
    .where(and(eq(shoppingLists.householdId, householdId), eq(shoppingLists.isActive, true)))
    .limit(1);
  if (existing[0]) return existing[0];
  const [list] = await db
    .insert(shoppingLists)
    .values({ householdId, name: "Einkaufsliste", isActive: true })
    .returning();
  return list!;
}

// GET /api/shopping-lists/items
shoppingListRoutes.get("/items", async (c) => {
  const householdId = c.get("householdId") as string;
  const list = await getOrCreateActiveList(householdId);
  const items = await db
    .select()
    .from(shoppingListItems)
    .where(eq(shoppingListItems.listId, list.id))
    .orderBy(desc(shoppingListItems.createdAt));
  return c.json({ items, listId: list.id });
});

// POST /api/shopping-lists/items
shoppingListRoutes.post(
  "/items",
  zValidator("json", z.object({ name: z.string().min(1).max(200) })),
  async (c) => {
    const householdId = c.get("householdId") as string;
    const user = c.get("user") as { id: string };
    const { name } = c.req.valid("json");
    const list = await getOrCreateActiveList(householdId);
    const [item] = await db
      .insert(shoppingListItems)
      .values({ listId: list.id, addedByUserId: user.id, name })
      .returning();
    return c.json({ item }, 201);
  },
);

// PATCH /api/shopping-lists/items/:id
shoppingListRoutes.patch(
  "/items/:id",
  zValidator(
    "json",
    z.object({
      name: z.string().min(1).max(200).optional(),
      isChecked: z.boolean().optional(),
    }),
  ),
  async (c) => {
    const householdId = c.get("householdId") as string;
    const user = c.get("user") as { id: string };
    const { id } = c.req.param();
    const input = c.req.valid("json");
    const list = await getOrCreateActiveList(householdId);
    const updateValues: Record<string, unknown> = {};
    if (input.name !== undefined) updateValues.name = input.name;
    if (input.isChecked !== undefined) {
      updateValues.isChecked = input.isChecked;
      updateValues.checkedByUserId = input.isChecked ? user.id : null;
      updateValues.checkedAt = input.isChecked ? new Date() : null;
    }
    const [item] = await db
      .update(shoppingListItems)
      .set(updateValues)
      .where(and(eq(shoppingListItems.id, id), eq(shoppingListItems.listId, list.id)))
      .returning();
    if (!item) return c.json({ error: "Not found" }, 404);
    return c.json({ item });
  },
);

// DELETE /api/shopping-lists/items/checked — delete all checked items
// IMPORTANT: must be registered BEFORE /items/:id to avoid being matched as an id
shoppingListRoutes.delete("/items/checked", async (c) => {
  const householdId = c.get("householdId") as string;
  const list = await getOrCreateActiveList(householdId);
  const deleted = await db
    .delete(shoppingListItems)
    .where(and(eq(shoppingListItems.listId, list.id), eq(shoppingListItems.isChecked, true)))
    .returning();
  return c.json({ deleted: deleted.length });
});

// DELETE /api/shopping-lists/items/:id
shoppingListRoutes.delete("/items/:id", async (c) => {
  const householdId = c.get("householdId") as string;
  const { id } = c.req.param();
  const list = await getOrCreateActiveList(householdId);
  const [item] = await db
    .delete(shoppingListItems)
    .where(and(eq(shoppingListItems.id, id), eq(shoppingListItems.listId, list.id)))
    .returning();
  if (!item) return c.json({ error: "Not found" }, 404);
  return c.json({ deleted: true });
});
