import { generateObject } from "ai";
import { z } from "zod";
import { resolveModel, type AiConfig } from "../factory";

const BatchCookSchema = z.object({
  title: z.string().max(150),
  description: z.string().max(300).describe("Plain prose only — no markdown formatting (no **bold**, no bullet points, no headers)."),
  prepMins: z.number().int().min(0).max(300).describe("Total hands-on prep time for the whole session, in minutes."),
  cookMins: z.number().int().min(0).max(300).describe("Total passive cook/bake/simmer time for the whole session, in minutes."),
  dishes: z.array(z.object({
    name: z.string().max(100),
    description: z.string().max(200).describe("Plain prose only — no markdown formatting."),
    fridgeDays: z.number().int().min(1).max(7),
    freezerFriendly: z.boolean(),
    freezerNote: z.string().max(150).optional(),
    dayOfInstructions: z.string().max(300).describe("Plain prose only — no markdown formatting."),
  })).min(1).max(6),
  ingredients: z.array(z.object({
    rawName: z.string().describe("Ingredient name only — never include the quantity or unit here."),
    quantity: z.number().optional().describe("A number only — never combined with the unit."),
    unit: z.string().optional().describe("The unit only, e.g. 'cup', 'g' — never combined with the quantity or name."),
  })).max(50),
  steps: z.array(z.object({
    appliesTo: z.array(z.string()).describe("Which dish(es) this step contributes to, using exact names from dishes[]. Empty array = shared mise-en-place step for everyone (Préparation phase). One name = a step specific to that dish's assembly. Two or more names = a step that simultaneously advances multiple dishes."),
    instruction: z.string(),
  })).max(50),
});

export type GeneratedBatchCook = z.infer<typeof BatchCookSchema>;

const LANG: Record<string, string> = { en: "English", fr: "French" };

export async function generateBatchCook(
  options: {
    dinners?: number;
    lunches?: number;
    servings?: number;
    dietaryPrefs?: string;
    difficulty?: "easy" | "medium" | "hard";
    description?: string;
  },
  config?: AiConfig & { userContext?: string },
  locale?: string
): Promise<GeneratedBatchCook> {
  const model = resolveModel(config);
  const lang = LANG[locale ?? "en"] ?? "English";
  const dinners = options.dinners ?? 4;
  const lunches = options.lunches ?? 0;
  const servings = options.servings ?? 4;
  const difficulty = options.difficulty;

  const mealParts: string[] = [];
  if (dinners > 0) mealParts.push(`${dinners} dinner(s)`);
  if (lunches > 0) mealParts.push(`${lunches} lunch(es)`);
  const totalDishes = Math.max(1, Math.min(6, dinners + lunches > 0 ? dinners + lunches : 4));

  const dietaryClause = options.dietaryPrefs?.trim()
    ? `Dietary requirements: ${options.dietaryPrefs.trim()}.`
    : "";
  const difficultyClause = difficulty
    ? `Keep techniques at a ${difficulty} skill level.`
    : "";
  const descriptionClause = options.description?.trim()
    ? `Additional guidance: ${options.description.trim()}.`
    : "";

  const systemPrompt = `You are a professional meal-prep chef writing ONE unified "batch cooking" recipe page — not several separate recipes. This mirrors real French batch-cooking guides: one shopping list, then a single prep session structured in two phases, covering multiple meals for the days ahead.

Structure:
1. A merged, deduplicated ingredient list across all dishes (identical rawName spelling for anything shared).
2. Steps, tagged individually with which dish(es) they belong to (appliesTo — empty for shared prep, one name for a dish-specific step, several names when a step genuinely advances multiple dishes at once, e.g. two things share the same oven bake, or a base is cooked once then split):
   - Phase "Préparation": shared mise-en-place done first — peeling, chopping, pre-cooking grains/proteins, etc. Group by task; tag with dish names only if a prep step is specific to one dish.
   - Phase "Assemblage": the remaining steps that turn prepped components into finished dishes. Actively look for steps that serve multiple dishes at once (shared oven timing, a base split several ways) and tag those with multiple names instead of duplicating the step.

Before designing the dishes, pick 2-3 "anchor" ingredients for the week — substantial vegetables, fruits, or proteins bought in bulk. Design each dish around reusing these anchors, prepared in a genuinely different form each time (e.g. a vegetable roasted in one dish, pureed into a soup in another, shredded raw in a salad in a third). Every dish must share at least one anchor ingredient with another dish — this is a structural requirement, not incidental. Beyond the anchors, pantry staples (oil, garlic, onion, salt) can repeat freely.

Dishes must be genuinely distinct — different techniques and flavor profiles (a tart, a braise, a soup, a stuffed vegetable, a grain salad, a curry), never the same base dish renamed. Favor formats that store well for several days (braises, soups, tarts, grain salads, stuffed vegetables, curries) over ones that degrade fast, unless dayOfInstructions explicitly compensates.

Every dish needs: fridgeDays, freezerFriendly, and a freezerNote if applicable (duration + thawing tip). dayOfInstructions must name the exact reheat method and time, AND a concrete fresh finishing touch (stir in cream, cook fresh grain, add a fresh side, sear a protein, grate cheese) — never generic ("reheat and enjoy" is unacceptable).

For ingredients: quantity is a number only, unit is a separate string. Never combine them.

Estimate prepMins (total hands-on active work across the whole session) and cookMins (total passive oven/stovetop/simmer time) for the whole session, in minutes.

Write every description and instruction as plain prose — never use markdown formatting (no **bold**, no bullet lists, no headers, no asterisks). Keep every field within its stated length limit; a shorter, complete sentence is always better than a longer one that gets cut off. Respond in ${lang}.`;

  const systemPromptWithContext = systemPrompt + (config?.userContext ? `\n\nUser preferences and context:\n${config.userContext}` : "");

  const { object } = await generateObject({
    model,
    schema: BatchCookSchema,
    system: systemPromptWithContext,
    maxOutputTokens: 8000,
    prompt: [
      `Generate a batch-cooking session covering ${mealParts.join(" and ") || `${totalDishes} meals`}.`,
      `Design exactly ${totalDishes} distinct dishes, each for ${servings} servings.`,
      dietaryClause,
      difficultyClause,
      descriptionClause,
    ].filter(Boolean).join(" "),
  });

  return object;
}
