import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { db, recipes, recipeIngredients, recipeSteps, collections, collectionRecipes, eq, and } from "@epicure/db";
import { requireSessionOrApiKey } from "@/lib/api-auth";
import { applyRateLimit } from "@/lib/rate-limit";
import { getDefaultProviderWithKey } from "@/lib/ai/resolve-user-key";
import { withAiQuota, resolveAiConfigOrError } from "@/lib/ai/ai-error";
import { checkTierLimitInTransaction, TierLimitError } from "@/lib/tiers";
import { generateMeal, MEAL_COURSES } from "@/lib/ai/features/generate-meal";
import { getUserPrivateBio } from "@/lib/ai/user-bio";
import { getMessages } from "@/lib/i18n/server";

const Schema = z.object({
  collectionId: z.string().min(1),
  theme: z.string().min(1).max(200),
  courses: z.array(z.enum(MEAL_COURSES)).min(2).max(6).refine((c) => new Set(c).size === c.length, "Duplicate courses"),
  servings: z.number().int().min(1).max(20).default(4),
  dietaryPrefs: z.string().max(200).optional(),
  difficulty: z.enum(["easy", "medium", "hard"]).optional(),
});

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

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

  const userId = session!.user.id;
  const tier = session!.user.tier as "free" | "pro" | "family";
  const { collectionId, theme, courses, servings, dietaryPrefs, difficulty } = parsed.data;

  const collection = await db.query.collections.findFirst({
    where: and(eq(collections.id, collectionId), eq(collections.userId, userId)),
  });
  if (!collection) return NextResponse.json({ error: "Not found" }, { status: 404 });

  const limited = await applyRateLimit(`rl:ai:${userId}`, 3, 60);
  if (limited) return limited;

  const locale = (session!.user as { locale?: string }).locale ?? "en";
  const [configResult, privateBio] = await Promise.all([
    resolveAiConfigOrError(() => getDefaultProviderWithKey(userId, "mealPlan")),
    getUserPrivateBio(userId),
  ]);
  if (!configResult.ok) return configResult.response;
  const config = configResult.data;

  const result = await withAiQuota(userId, tier, () =>
    generateMeal(
      { theme, courses, servings, dietaryPrefs, difficulty },
      { ...config, userContext: privateBio ?? undefined },
      locale
    ), { skipQuota: config.isByok }
  );
  if (!result.ok) return result.response;
  const meal = result.data;

  const created: Array<{ id: string; title: string; course: string }> = [];

  try {
    await db.transaction(async (tx) => {
    // Each recipe in the meal creates a real recipe row — check the recipe
    // limit covers all of them before inserting anything.
    await checkTierLimitInTransaction(tx, userId, tier, "recipe", meal.recipes.length);
    for (const recipe of meal.recipes) {
      const recipeId = crypto.randomUUID();
      await tx.insert(recipes).values({
        id: recipeId,
        authorId: userId,
        title: recipe.title,
        description: recipe.description,
        baseServings: recipe.baseServings,
        recipeType: recipe.recipeType,
        visibility: "private",
        language: locale,
        aiGenerated: true,
        difficulty: recipe.difficulty ?? null,
        prepMins: recipe.prepMins ?? null,
        cookMins: recipe.cookMins ?? null,
        dietaryTags: recipe.dietaryTags ?? {},
        tags: [getMessages(locale).collections.course[recipe.course]],
      });

      if (recipe.ingredients.length > 0) {
        await tx.insert(recipeIngredients).values(
          recipe.ingredients.map((ing, i) => ({
            id: crypto.randomUUID(),
            recipeId,
            rawName: ing.rawName,
            quantity: ing.quantity != null ? String(ing.quantity) : null,
            unit: ing.unit ?? null,
            note: ing.note ?? null,
            order: i,
          }))
        );
      }

      if (recipe.steps.length > 0) {
        await tx.insert(recipeSteps).values(
          recipe.steps.map((step, i) => ({
            id: crypto.randomUUID(),
            recipeId,
            instruction: step.instruction,
            timerSeconds: step.timerSeconds ?? null,
            order: i,
          }))
        );
      }

      await tx.insert(collectionRecipes).values({ collectionId, recipeId }).onConflictDoNothing();

      created.push({ id: recipeId, title: recipe.title, course: recipe.course });
    }
    });
  } catch (err) {
    if (err instanceof TierLimitError) {
      return NextResponse.json({ error: "Recipe limit reached for your tier" }, { status: 403 });
    }
    throw err;
  }

  return NextResponse.json({ collectionId, recipes: created });
}
