#!/usr/bin/env tsx
/**
 * Gap-fill för jordnötssmör-cellen (docs/32 Spår B):
 *   rikare, lagade middagsrätter med jordnötssmör som huvudingrediens.
 *
 * - Max 3 recept per huvudingrediens (~12 totalt).
 * - Använder befintlig runPipeline + verifyCandidate.
 * - Dedup mot befintliga 246 recept (titel + slug).
 * - Skriver endast sammanfattning till stdout; full export + rejects till filer.
 */

import { createAamosClient } from "@app/ai-contracts";
import { SEED_INGREDIENTS, SEED_RECIPES } from "@app/database/seed";
import { runPipeline, type PipelineTarget, type PipelineIngredient } from "@app/recipe-generation";
import type { CanonicalIngredientLookup, SimilarityLookup } from "@app/recipe-generation";
import type { SeedRecipe } from "@app/database/seed/data/recipes.js";
import { BRAND } from "@app/shared-types";
import * as fs from "node:fs/promises";
import * as path from "node:path";

const OUTPUT_DIR = path.resolve(import.meta.dirname ?? "..", "..", "output");

const NULLCELL_TARGETS: PipelineTarget[] = [
  // Endast jordnötssmör-cellen återstår; rikare, lagade middagsrätter.
  { mealType: "dinner", mainIngredientId: "peanut_butter", dietVariant: "standard", count: 4 },
];

function normalizeForSlug(title: string): string {
  return title
    .toLowerCase()
    .replace(/[åä]/g, "a")
    .replace(/ö/g, "o")
    .replace(/é/g, "e")
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "")
    .replace(/-+/g, "-");
}

function makeUniqueSlug(title: string, existingSlugs: Set<string>): string {
  let slug = normalizeForSlug(title);
  if (!slug) slug = "recept";
  let candidate = slug;
  let suffix = 2;
  while (existingSlugs.has(candidate)) {
    candidate = `${slug}-${suffix}`;
    suffix++;
  }
  return candidate;
}

function deriveTags(mealType: string, mainId: string): string[] {
  const tags = new Set<string>(["quick"]);
  if (mealType === "breakfast" || mealType === "snack") tags.add("kid_friendly");
  if (mainId === "pork_loin") tags.add("high_protein");
  if (mainId === "peanut_butter") tags.add("high_protein");
  return Array.from(tags);
}

function deriveMethods(mainId: string): string[] {
  if (mainId === "banana" || mainId === "yoghurt_natural") return ["no_cook"];
  return ["stovetop"];
}

function deriveEquipment(mainId: string): string[] {
  if (mainId === "banana" || mainId === "yoghurt_natural") {
    return [];
  }
  return ["stove"];
}

function guessDna(mainId: string, ingredientIds: string[]) {
  const vegetables = ingredientIds.filter((id) =>
    [
      "tomato",
      "spinach",
      "zucchini",
      "paprika",
      "carrot",
      "cucumber",
      "lettuce",
      "corn",
      "onion",
      "garlic",
    ].includes(id),
  );
  const carbs = ingredientIds.filter((id) =>
    ["rice_white", "pasta_dry", "potato", "bread", "tortilla", "oats", "quinoa"].includes(id),
  );
  const proteins = ingredientIds.filter((id) =>
    [
      "pork_loin",
      "chicken_breast",
      "chicken_thigh",
      "minced_beef",
      "salmon",
      "tofu",
      "egg",
      "peanut_butter",
    ].includes(id),
  );
  return {
    protein: proteins[0] ?? null,
    carb: carbs[0] ?? null,
    vegetables: vegetables.slice(0, 4),
    flavor: ["fresh", "balanced"],
  };
}

function toSeedRecipe(
  candidate: Awaited<ReturnType<typeof runPipeline>>["verificationResults"][number],
  target: PipelineTarget,
  slug: string,
): SeedRecipe {
  const c = candidate.candidate;
  const dna = guessDna(target.mainIngredientId, candidate.canonicalIngredientIds);

  return {
    slug,
    titleSv: c.titleSv,
    descriptionSv: c.descriptionSv,
    cuisine: (c.cuisine as SeedRecipe["cuisine"]) ?? "international",
    mealTypes: (c.mealTypes as SeedRecipe["mealTypes"]) ?? [
      target.mealType as SeedRecipe["mealTypes"][number],
    ],
    tags: deriveTags(target.mealType, target.mainIngredientId) as SeedRecipe["tags"],
    methods: deriveMethods(target.mainIngredientId) as SeedRecipe["methods"],
    equipment: deriveEquipment(target.mainIngredientId) as SeedRecipe["equipment"],
    difficulty: "beginner",
    prepMin: c.prepTimeMinutes,
    cookMin: c.cookTimeMinutes,
    portions: c.portions,
    spiceLevel: c.spiceLevel,
    mealPrepFriendly: c.mealPrepFriendly,
    freezerFriendly: c.freezerFriendly,
    peakSeasons: [],
    holidayTags: [],
    variantType: target.dietVariant as SeedRecipe["variantType"],
    dnaProtein: dna.protein ?? undefined,
    dnaCarb: dna.carb ?? undefined,
    dnaVegetables: dna.vegetables,
    dnaFlavor: dna.flavor,
    verificationStatus: "verified",
    storageGuidanceSv: c.storageGuidanceSv ?? undefined,
    ingredients: c.ingredients.map((i) => ({
      ing: i.canonicalIngredientId,
      nameSv: i.displayNameSv,
      qty: i.quantity,
      unit: i.unit as SeedRecipe["ingredients"][number]["unit"],
      note: i.note ?? undefined,
      optional: i.optional,
    })),
    steps: c.steps.map((s) => ({
      text: s.instructionSv,
      timerSeconds: s.timerSeconds ?? undefined,
      temperatureC: s.temperatureC ?? undefined,
      tip: s.tip ?? undefined,
    })),
  };
}

async function main() {
  const client = createAamosClient(process.env);

  const catalog: PipelineIngredient[] = SEED_INGREDIENTS.map((i) => ({
    id: i.id,
    nameSv: i.nameSv,
    category: i.category,
    defaultUnit: i.defaultUnit,
    isVegan: i.isVegan,
    isVegetarian: i.isVegetarian,
    containsGluten: i.containsGluten,
    containsLactose: i.containsLactose,
    allergens: i.allergens,
  }));

  const ingredientLookup: CanonicalIngredientLookup = {
    getById(id: string) {
      const ing = SEED_INGREDIENTS.find((i) => i.id === id);
      if (!ing) return undefined;
      return {
        id: ing.id,
        nutritionPer100: ing.nutritionPer100,
        defaultUnit: ing.defaultUnit,
        densityGPerMl: ing.densityGPerMl ?? null,
        gramsPerPiece: ing.gramsPerPiece ?? null,
        allergens: ing.allergens,
        isVegan: ing.isVegan,
        isVegetarian: ing.isVegetarian,
        containsGluten: ing.containsGluten,
        containsLactose: ing.containsLactose,
        isPork: ing.isPork,
        isBeef: ing.isBeef,
        isAlcohol: ing.isAlcohol,
      };
    },
  };

  const existingSlugs = new Set(SEED_RECIPES.map((r) => r.slug));
  const existingTitles = new Set(SEED_RECIPES.map((r) => r.titleSv.toLowerCase()));

  const usedSlugs = new Set(existingSlugs);
  const usedTitles = new Set(existingTitles);

  const similarityLookup: SimilarityLookup = {
    async hasSimilarity(title: string) {
      const lower = title.toLowerCase();
      if (usedTitles.has(lower)) return true;
      const slug = normalizeForSlug(title);
      if (usedSlugs.has(slug)) return true;
      return false;
    },
  };

  console.error(
    `[gapfill] ${NULLCELL_TARGETS.length} nollceller, max ${NULLCELL_TARGETS.reduce((s, t) => s + t.count, 0)} recept`,
  );

  const result = await runPipeline(
    client,
    NULLCELL_TARGETS,
    catalog,
    ingredientLookup,
    similarityLookup,
    { maxPrepTimeMinutes: 30, maxCookTimeMinutes: 45, portions: 4 },
  );

  const verifiedExports: Array<{ target: PipelineTarget; slug: string; recipe: SeedRecipe }> = [];
  const rejects: Array<{
    target: PipelineTarget;
    title: string;
    status: string;
    reasons: string[];
  }> = [];

  for (let i = 0; i < result.verificationResults.length; i++) {
    const vr = result.verificationResults[i];
    const target =
      NULLCELL_TARGETS.find((t) => t.mainIngredientId === vr.canonicalIngredientIds[0]) ??
      NULLCELL_TARGETS[0];

    if (vr.status === "verified") {
      const slug = makeUniqueSlug(vr.candidate.titleSv, usedSlugs);
      usedSlugs.add(slug);
      usedTitles.add(vr.candidate.titleSv.toLowerCase());
      verifiedExports.push({ target, slug, recipe: toSeedRecipe(vr, target, slug) });
    } else {
      rejects.push({
        target,
        title: vr.candidate.titleSv,
        status: vr.status,
        reasons: vr.reasons,
      });
    }
  }

  await fs.mkdir(OUTPUT_DIR, { recursive: true });

  const exportPath = path.join(OUTPUT_DIR, "gapfill-nullcells-export.json");
  const rejectsPath = path.join(OUTPUT_DIR, "gapfill-nullcells-rejects.json");
  const summaryPath = path.join(OUTPUT_DIR, "gapfill-nullcells-summary.json");

  await fs.writeFile(
    exportPath,
    JSON.stringify(
      {
        batchId: `gapfill-nullcells-${Date.now()}`,
        generatedAt: new Date().toISOString(),
        brand: BRAND.name,
        targetCount: NULLCELL_TARGETS.reduce((s, t) => s + t.count, 0),
        verifiedCount: result.verifiedCount,
        unverifiedCount: result.unverifiedCount,
        rejectedCount: result.rejectedCount,
        geminiStatus: result.geminiResult.status,
        geminiCostUsd: result.geminiResult.costUsd ?? 0,
        recipes: verifiedExports.map((e) => e.recipe),
      },
      null,
      2,
    ),
    "utf-8",
  );

  await fs.writeFile(
    rejectsPath,
    JSON.stringify(
      {
        batchId: `gapfill-nullcells-${Date.now()}`,
        generatedAt: new Date().toISOString(),
        rejects: rejects,
      },
      null,
      2,
    ),
    "utf-8",
  );

  const summary = {
    batchId: `gapfill-nullcells-${Date.now()}`,
    generatedAt: new Date().toISOString(),
    geminiStatus: result.geminiResult.status,
    geminiCostUsd: result.geminiResult.costUsd ?? 0,
    candidatesGenerated: result.candidates.length,
    verified: result.verifiedCount,
    unverified: result.unverifiedCount,
    rejected: result.rejectedCount,
    exportPath,
    rejectsPath,
  };

  await fs.writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");

  console.error("\n# Gap-fill nollceller — sammanfattning\n");
  console.error(`- Genererade kandidater: ${result.candidates.length}`);
  console.error(`- Verified/godkända:     ${result.verifiedCount}`);
  console.error(`- Unverified:            ${result.unverifiedCount}`);
  console.error(`- Rejected:              ${result.rejectedCount}`);
  console.error(`- Gemini-kostnad:        $${(result.geminiResult.costUsd ?? 0).toFixed(4)}`);
  console.error(`- Export:                ${exportPath}`);
  console.error(`- Rejects:               ${rejectsPath}`);
  console.error(`- Summary:               ${summaryPath}`);

  if (result.geminiResult.error) {
    console.error(`\n**Fel:** ${result.geminiResult.error}`);
  }
}

main().catch((err) => {
  console.error("[gapfill] Fatal:", err);
  process.exit(1);
});
