import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { db, mealPlans, mealPlanEntries, recipes, recipeBatchDishes, eq, and, or, ne } from "@epicure/db";
import { requireSessionOrApiKey } from "@/lib/api-auth";
import { dispatchWebhook } from "@/lib/webhooks";

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

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(),
  batchDishId: z.string().optional(),
  servings: z.number().int().min(1).max(100).default(2),
  note: z.string().max(500).optional(),
});

async function getOrCreatePlan(userId: string, weekStart: string) {
  const existing = await db.query.mealPlans.findFirst({
    where: and(eq(mealPlans.userId, userId), eq(mealPlans.weekStart, weekStart)),
  });
  if (existing) return existing;

  const id = crypto.randomUUID();
  await db.insert(mealPlans).values({ id, userId, weekStart });
  return { id, userId, weekStart };
}

// Polled by MealPlanner (components/meal-plan/meal-planner.tsx) so entries
// added/removed by a collaborator via the shared view show up here too — a
// lean shape (no photos join) unlike the full plan GET at
// meal-plans/[weekStart]/route.ts, since this runs every few seconds.
export async function GET(req: NextRequest, { params }: Params) {
  const { session, response } = await requireSessionOrApiKey(req);
  if (response) return response;
  const { weekStart } = await params;

  const plan = await db.query.mealPlans.findFirst({
    where: and(eq(mealPlans.userId, session!.user.id), eq(mealPlans.weekStart, weekStart)),
  });
  if (!plan) return NextResponse.json({ entries: [] });

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

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

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

  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 });
  }

  if (parsed.data.batchDishId) {
    if (!parsed.data.recipeId) return NextResponse.json({ error: "batchDishId requires recipeId" }, { status: 400 });
    const dish = await db.query.recipeBatchDishes.findFirst({
      where: and(eq(recipeBatchDishes.id, parsed.data.batchDishId), eq(recipeBatchDishes.recipeId, parsed.data.recipeId)),
    });
    if (!dish) return NextResponse.json({ error: "Dish not found" }, { status: 404 });
  }

  const plan = await getOrCreatePlan(session!.user.id, weekStart);

  // Remove existing entry for same day+mealType before inserting
  await db.delete(mealPlanEntries).where(
    and(
      eq(mealPlanEntries.mealPlanId, plan.id),
      eq(mealPlanEntries.day, parsed.data.day),
      eq(mealPlanEntries.mealType, parsed.data.mealType)
    )
  );

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

  void dispatchWebhook(session!.user.id, "meal_plan.updated", { weekStart, day: parsed.data.day, mealType: parsed.data.mealType });
  return NextResponse.json({ id: entryId }, { status: 201 });
}
