import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { db, mealPlanEntries, recipes, eq, and, or, ne, inArray } from "@epicure/db";
import { requireSessionOrApiKey } from "@/lib/api-auth";
import { getMealPlanAccessById, canWriteMealPlan } from "@/lib/meal-plan-access";
import { dispatchWebhook } from "@/lib/webhooks";

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

// Polled by SharedMealPlanView so every collaborator sees each other's
// changes, and the plan owner's own tab (a different component/URL, per
// meal-plans/[weekStart]/entries) picks up edits made from here too.
export async function GET(req: NextRequest, { params }: Params) {
  const { session, response } = await requireSessionOrApiKey(req);
  if (response) return response;
  const { mealPlanId } = await params;

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

  const entries = await db.query.mealPlanEntries.findMany({
    where: eq(mealPlanEntries.mealPlanId, mealPlanId),
    with: { recipe: { columns: { id: true, title: true } } },
  });

  return NextResponse.json({
    entries: entries.map((e) => ({
      id: e.id,
      day: e.day,
      mealType: e.mealType,
      servings: e.servings,
      recipe: e.recipe,
    })),
  });
}

const Schema = z.object({
  day: z.enum(["mon", "tue", "wed", "thu", "fri", "sat", "sun"]),
  mealType: z.enum(["breakfast", "lunch", "dinner", "snack"]),
  recipeId: z.string().optional(),
  servings: z.number().int().min(1).max(100).default(2),
  note: z.string().max(500).optional(),
});

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

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

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

  if (parsed.data.recipeId) {
    const recipe = await db.query.recipes.findFirst({
      where: and(
        eq(recipes.id, parsed.data.recipeId),
        or(eq(recipes.authorId, session!.user.id), ne(recipes.visibility, "private"))
      ),
    });
    if (!recipe) return NextResponse.json({ error: "Recipe not found" }, { status: 404 });
  }

  await db.delete(mealPlanEntries).where(
    and(
      eq(mealPlanEntries.mealPlanId, mealPlanId),
      eq(mealPlanEntries.day, parsed.data.day),
      eq(mealPlanEntries.mealType, parsed.data.mealType)
    )
  );

  const entryId = crypto.randomUUID();
  await db.insert(mealPlanEntries).values({
    id: entryId,
    mealPlanId,
    day: parsed.data.day,
    mealType: parsed.data.mealType,
    recipeId: parsed.data.recipeId,
    servings: parsed.data.servings,
    note: parsed.data.note,
  });

  void dispatchWebhook(access.plan.userId, "meal_plan.updated", { mealPlanId, day: parsed.data.day, mealType: parsed.data.mealType });
  return NextResponse.json({ id: entryId }, { status: 201 });
}

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

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

  // ?entryId=x for a single delete (existing behavior), or ?ids=a,b,c for a
  // "clear this day"/"clear this week" bulk delete — same access check either way.
  const entryId = req.nextUrl.searchParams.get("entryId");
  const idsParam = req.nextUrl.searchParams.get("ids");
  const ids = idsParam ? idsParam.split(",").filter(Boolean) : entryId ? [entryId] : [];
  if (ids.length === 0) return NextResponse.json({ error: "entryId or ids required" }, { status: 400 });

  await db.delete(mealPlanEntries).where(
    and(inArray(mealPlanEntries.id, ids), eq(mealPlanEntries.mealPlanId, mealPlanId))
  );

  return new NextResponse(null, { status: 204 });
}
