import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { db, shoppingListItems, eq, and } from "@epicure/db";
import { getOptionalSession } from "@/lib/api-auth";
import { getShoppingListAccess, canWriteShoppingList } from "@/lib/shopping-list-access";
import { guessAisle } from "@/lib/grocery-categories";
import { notifyShoppingListMembers } from "@/lib/shopping-list-notify";

// Polled by the list view (components/meal-plan/shopping-list-view.tsx) so
// collaborators see each other's checked/added/reordered items without a
// manual refresh — no push/websocket infra in this app, so a short poll on
// the same shape the page's initial server-render already uses. No
// requireSession here: a public-editable list's own visitors (no account)
// need this too — getShoppingListAccess resolves that from the link itself.
export async function GET(_req: NextRequest, { params }: Params) {
  const { id } = await params;
  const session = await getOptionalSession();

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

  const items = await db.query.shoppingListItems.findMany({
    where: eq(shoppingListItems.listId, id),
    orderBy: (t, { asc }) => [asc(t.aisle), asc(t.sortOrder), asc(t.rawName)],
  });

  return NextResponse.json({
    items: items.map((i) => ({
      id: i.id,
      rawName: i.rawName,
      quantity: i.quantity,
      unit: i.unit,
      aisle: i.aisle,
      checked: i.checked,
      sortOrder: i.sortOrder,
    })),
  });
}

const AddItemsSchema = z.object({
  items: z.array(z.object({
    rawName: z.string().min(1),
    quantity: z.string().optional(),
    unit: z.string().optional(),
    aisle: z.string().optional(),
  })).min(1),
});

// Bulk-update a set of existing items (sortOrder and/or aisle) in one request —
// used for drag-and-drop reordering (avoids one PUT per item on every drag) and
// for the "auto-categorize" action on lists whose items predate aisle-guessing.
const BulkUpdateSchema = z.object({
  items: z.array(z.object({
    id: z.string().min(1),
    sortOrder: z.number().int().optional(),
    aisle: z.string().nullable().optional(),
  })).min(1),
});

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

export async function POST(req: NextRequest, { params }: Params) {
  const { id } = await params;
  const session = await getOptionalSession();

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

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

  await db.insert(shoppingListItems).values(
    parsed.data.items.map((item) => ({
      id: crypto.randomUUID(),
      listId: id,
      rawName: item.rawName,
      quantity: item.quantity,
      unit: item.unit,
      // Only guess when the caller didn't provide an explicit aisle — never override.
      aisle: item.aisle ?? guessAisle(item.rawName) ?? undefined,
      checked: false,
    }))
  );

  void notifyShoppingListMembers(id, session?.user.id ?? "", session?.user.name ?? null, {
    type: "itemsAdded",
    count: parsed.data.items.length,
    listName: access.list.name,
  }).catch(() => {});

  return NextResponse.json({ ok: true }, { status: 201 });
}

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

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

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

  await Promise.all(
    parsed.data.items.map((item) => {
      const patch: Partial<typeof shoppingListItems.$inferInsert> = {};
      if (item.sortOrder !== undefined) patch.sortOrder = item.sortOrder;
      if (item.aisle !== undefined) patch.aisle = item.aisle;
      if (Object.keys(patch).length === 0) return Promise.resolve();
      return db.update(shoppingListItems)
        .set(patch)
        .where(and(eq(shoppingListItems.id, item.id), eq(shoppingListItems.listId, id)));
    })
  );

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