import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { db, shoppingLists, shoppingListItems, eq } from "@epicure/db";
import { requireSessionOrApiKey } from "@/lib/api-auth";
import { dispatchWebhook } from "@/lib/webhooks";
import { getShoppingListAccess, canWriteShoppingList } from "@/lib/shopping-list-access";

type Params = { params: Promise<{ id: string }> };

export async function GET(req: NextRequest, { params }: Params) {
  const { session, response } = await requireSessionOrApiKey(req);
  if (response) return response;
  const { id } = await params;

  const access = await getShoppingListAccess(id, session!.user.id);
  if (!access) return NextResponse.json({ error: "Not found" }, { status: 404 });

  const list = await db.query.shoppingLists.findFirst({
    where: eq(shoppingLists.id, id),
    with: { items: { orderBy: (t, { asc }) => [asc(t.aisle), asc(t.rawName)] } },
  });

  return NextResponse.json(list);
}

const PatchSchema = z.object({
  completed: z.boolean().optional(),
  name: z.string().min(1).max(100).optional(),
  isPublic: z.boolean().optional(),
  publicEditable: z.boolean().optional(),
});

export async function PATCH(req: NextRequest, { params }: Params) {
  const { session, response } = await requireSessionOrApiKey(req);
  if (response) return response;
  const { id } = await params;

  const access = await getShoppingListAccess(id, session!.user.id);
  if (!access) return NextResponse.json({ error: "Not found" }, { status: 404 });

  const body = PatchSchema.safeParse(await req.json());
  if (!body.success) return NextResponse.json({ error: "Validation error" }, { status: 400 });

  if (body.data.name !== undefined) {
    // Renaming is owner-only, same as delete
    if (access.role !== "owner") return NextResponse.json({ error: "Forbidden" }, { status: 403 });
    await db.update(shoppingLists).set({ name: body.data.name }).where(eq(shoppingLists.id, id));
  }

  if (body.data.isPublic !== undefined) {
    if (access.role !== "owner") return NextResponse.json({ error: "Forbidden" }, { status: 403 });
    // Turning the public link off also revokes public editing — otherwise
    // re-enabling the link later would silently restore write access without
    // the owner having re-opted into it.
    await db.update(shoppingLists)
      .set({ isPublic: body.data.isPublic, ...(body.data.isPublic ? {} : { publicEditable: false }) })
      .where(eq(shoppingLists.id, id));
  }

  if (body.data.publicEditable !== undefined) {
    if (access.role !== "owner") return NextResponse.json({ error: "Forbidden" }, { status: 403 });
    await db.update(shoppingLists).set({ publicEditable: body.data.publicEditable }).where(eq(shoppingLists.id, id));
  }

  if (body.data.completed) {
    if (!canWriteShoppingList(access.role)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
    // Mark all items as checked
    await db.update(shoppingListItems).set({ checked: true }).where(eq(shoppingListItems.listId, id));
    void dispatchWebhook(session!.user.id, "shopping_list.completed", { id, name: access.list.name });
  }

  return NextResponse.json({ updated: true });
}

export async function DELETE(req: NextRequest, { params }: Params) {
  const { session, response } = await requireSessionOrApiKey(req);
  if (response) return response;
  const { id } = await params;

  const access = await getShoppingListAccess(id, session!.user.id);
  if (!access) return NextResponse.json({ error: "Not found" }, { status: 404 });
  if (access.role !== "owner") return NextResponse.json({ error: "Forbidden" }, { status: 403 });

  await db.delete(shoppingLists).where(eq(shoppingLists.id, id));
  return new NextResponse(null, { status: 204 });
}
