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

/**
 * Publiceringsflöde för användarrecept (spec §35):
 * submitted → AI-kontroll (AAMOS MODERATE_RECIPE) → dubblettkontroll (spec §36)
 * → in_moderation (mänsklig granskning i admin) eller direkt reject.
 */
export async function processModerateRecipe(ctx: WorkerContext, recipeId: string): Promise<void> {
  const [recipe] = await ctx.db
    .select()
    .from(schema.recipes)
    .where(eq(schema.recipes.id, recipeId))
    .limit(1);
  if (!recipe || recipe.status !== "submitted") return;

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

  // 1. AI-kontroll (spec §35 steg 2)
  const result = await ctx.aamos.runTask("MODERATE_RECIPE", {
    titleSv: recipe.titleSv,
    descriptionSv: recipe.descriptionSv,
    ingredients: ingredients.map((i) => `${i.quantity} ${i.unit} ${i.displayNameSv}`),
    steps: steps.map((s) => s.instructionSv),
  });

  if (result.status === "ok" && result.output?.recommendation === "reject") {
    await ctx.db
      .update(schema.recipes)
      .set({
        status: "rejected",
        moderationNote: result.output.flags.map((f) => f.messageSv).join(" "),
        updatedAt: new Date(),
      })
      .where(eq(schema.recipes.id, recipeId));
    return;
  }

  // 2. Dubblettkontroll (spec §36): jämför ingrediensuppsättning + DNA
  const candidates = await ctx.db
    .select({ id: schema.recipes.id, dna: schema.recipes.dna, titleSv: schema.recipes.titleSv })
    .from(schema.recipes)
    .where(and(eq(schema.recipes.status, "published"), ne(schema.recipes.id, recipeId)))
    .limit(500);

  const mySet = new Set(ingredients.map((i) => i.canonicalIngredientId));
  for (const candidate of candidates) {
    const otherIngredients = await ctx.db
      .select({ canonicalIngredientId: schema.recipeIngredients.canonicalIngredientId })
      .from(schema.recipeIngredients)
      .where(eq(schema.recipeIngredients.recipeId, candidate.id));
    const otherSet = new Set(otherIngredients.map((i) => i.canonicalIngredientId));
    const intersection = [...mySet].filter((id) => otherSet.has(id)).length;
    const union = new Set([...mySet, ...otherSet]).size;
    const jaccard = union > 0 ? intersection / union : 0;

    if (jaccard >= 0.6) {
      const classification =
        jaccard >= 0.9 ? "duplicate" : jaccard >= 0.75 ? "variant" : "inspired";
      await ctx.db
        .insert(schema.recipeSimilarities)
        .values({
          recipeAId: recipeId,
          recipeBId: candidate.id,
          similarityScore: Math.round(jaccard * 100) / 100,
          classification,
          details: { method: "ingredient_jaccard", intersection, union },
        })
        .onConflictDoNothing();
    }
  }

  // 3. Till mänsklig moderering (spec §35 steg 4). Vanliga rätter får ha
  //    legitima varianter – dubbletter avgörs av människa, inte automatik.
  const flagsNote =
    result.status === "ok" && result.output
      ? result.output.flags.map((f) => `[${f.severity}] ${f.messageSv}`).join(" ")
      : "AI-kontrollen kunde inte köras – manuell granskning krävs.";
  const dupCount = await ctx.db
    .select({ count: sql<number>`count(*)` })
    .from(schema.recipeSimilarities)
    .where(eq(schema.recipeSimilarities.recipeAId, recipeId));

  await ctx.db
    .update(schema.recipes)
    .set({
      status: "in_moderation",
      moderationNote: `${flagsNote} Dubblettkandidater: ${Number(dupCount[0]?.count ?? 0)}.`.trim(),
      updatedAt: new Date(),
    })
    .where(eq(schema.recipes.id, recipeId));
}
