import { and, eq } from "drizzle-orm";
import { schema } from "@app/database";
import type { WorkerContext } from "../context.js";

/**
 * Receptöversättning (i18n-spec §13–14, M3):
 *   TRANSLATE_RECIPE-jobb → AAMOS översätter TEXT → deterministisk verifiering
 *   → sparas som draft_ai → människa publicerar i admin.
 *
 * AI kan aldrig ändra mängder, ingredient-IDs, tider eller allergener –
 * de bor i strukturerade fält utanför översättningen. Verifieringen här
 * kontrollerar det som ÄNDÅ kan gå fel i text: stegantal, bevarade tal
 * (temperaturer/mängder inbakade i löptext) och tomma fält.
 */
export async function processTranslateRecipe(
  ctx: WorkerContext,
  data: { recipeId: string; targetLanguageTag: string },
): Promise<void> {
  const { recipeId, targetLanguageTag } = data;
  const [recipe] = await ctx.db
    .select()
    .from(schema.recipes)
    .where(eq(schema.recipes.id, recipeId))
    .limit(1);
  if (!recipe) return;

  const steps = await ctx.db
    .select()
    .from(schema.recipeSteps)
    .where(eq(schema.recipeSteps.recipeId, recipeId))
    .orderBy(schema.recipeSteps.stepNumber);

  const input = {
    sourceLanguageTag: "sv",
    targetLanguageTag,
    title: recipe.titleSv,
    description: recipe.descriptionSv ?? null,
    storageGuidance: recipe.storageGuidanceSv ?? null,
    steps: steps.map((s) => ({
      stepNumber: s.stepNumber,
      instruction: s.instructionSv,
      tip: s.tip ?? null,
    })),
  };

  const result = await ctx.aamos.runTask("TRANSLATE_RECIPE", input);
  if (result.status !== "ok" || !result.output) {
    throw new Error(`TRANSLATE_RECIPE misslyckades: ${result.status}`);
  }
  const out = result.output;

  // --- Deterministisk verifiering (spec §61.1: AI:s svar litas aldrig på rakt av) ---
  const checks: Record<string, boolean> = {
    stepCountMatches: out.steps.length === input.steps.length,
    stepNumbersMatch: out.steps.every((s, i) => s.stepNumber === input.steps[i]?.stepNumber),
    titleNonEmpty: out.title.trim().length > 0,
    numbersPreserved: numbersPreserved(
      [input.title, input.description ?? "", ...input.steps.map((s) => s.instruction)],
      [out.title, out.description ?? "", ...out.steps.map((s) => s.instruction)],
    ),
    confidenceAcceptable: out.confidence >= 0.5,
  };
  const ok = Object.values(checks).every(Boolean);
  const notes = Object.entries(checks)
    .filter(([, v]) => !v)
    .map(([k]) => `Verifiering föll: ${k}`);

  // --- Spara utkast (upsert per språk) ---
  const [existing] = await ctx.db
    .select({ id: schema.recipeTranslations.id })
    .from(schema.recipeTranslations)
    .where(
      and(
        eq(schema.recipeTranslations.recipeId, recipeId),
        eq(schema.recipeTranslations.languageTag, targetLanguageTag),
      ),
    )
    .limit(1);

  const row = {
    title: out.title,
    description: out.description,
    storageGuidance: out.storageGuidance,
    status: "draft_ai" as const,
    source: "ai" as const,
    verification: { ok, checks, ...(notes.length ? { notes } : {}) },
    updatedAt: new Date(),
  };
  if (existing) {
    await ctx.db
      .update(schema.recipeTranslations)
      .set(row)
      .where(eq(schema.recipeTranslations.id, existing.id));
  } else {
    await ctx.db
      .insert(schema.recipeTranslations)
      .values({ recipeId, languageTag: targetLanguageTag, ...row });
  }

  // Stegtexter: ersätt hela uppsättningen för språket (idempotent).
  await ctx.db
    .delete(schema.recipeStepTranslations)
    .where(
      and(
        eq(schema.recipeStepTranslations.recipeId, recipeId),
        eq(schema.recipeStepTranslations.languageTag, targetLanguageTag),
      ),
    );
  if (out.steps.length > 0) {
    await ctx.db.insert(schema.recipeStepTranslations).values(
      out.steps.map((s) => ({
        recipeId,
        languageTag: targetLanguageTag,
        stepNumber: s.stepNumber,
        instruction: s.instruction,
        tip: s.tip,
      })),
    );
  }
}

/**
 * Alla tal i källtexten ska finnas kvar i måltexten (multiset-jämförelse).
 * Fångar när en modell "översätter om" 225°C till 437°F eller tappar en mängd –
 * enhetskonvertering är visningslagrets jobb, aldrig översättningens.
 */
export function numbersPreserved(sourceTexts: string[], targetTexts: string[]): boolean {
  const extract = (texts: string[]) => {
    const counts = new Map<string, number>();
    for (const m of texts.join(" ").matchAll(/\d+(?:[.,]\d+)?/g)) {
      const key = m[0].replace(",", ".");
      counts.set(key, (counts.get(key) ?? 0) + 1);
    }
    return counts;
  };
  const src = extract(sourceTexts);
  const tgt = extract(targetTexts);
  for (const [num, count] of src) {
    if ((tgt.get(num) ?? 0) < count) return false;
  }
  return true;
}
