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

const RAW_PROTEIN_REQUIRING_SAFE_COOKING = new Set([
  "chicken_breast",
  "chicken_thigh",
  "pork_loin",
  "minced_beef",
  "minced_mixed",
  "meatball_pork_beef",
  "falukorv",
  "cod",
  "salmon",
  "shrimp",
]);

const SAFE_COOKING_KEYWORDS_SV = [
  /\bgenomstek/i,
  /\bgenomkokt\b/i,
  /\bgenomgrillad\b/i,
  /\bgenomv\w+\b/i,
  /\binte längre rosa\b/i,
  /\binte rosa\b/i,
  /\bflagnar\b/i,
  /\bkärntemperatur\b/i,
  /\binnertemperatur\b/i,
  /\btemperatur\b/i,
  /\b°\s*c\b/i,
  /\bgrader\b/i,
  /\btill(?:s)? den är klar\b/i,
  /\btill(?:s)? köttet släpper vätskan\b/i,
  /\b72\s*c?\b/i,
  /\b74\s*c?\b/i,
  /\b75\s*c?\b/i,
  /\b63\s*c?\b/i,
  /\b65\s*c?\b/i,
  /\b70\s*c?\b/i,
];

const UNSAFE_APPEARANCE_ONLY_SV = [
  /\bgyllenbrun\b/i,
  /\bgyllene\b/i,
  /\bkrispig\b/i,
  /\bkrispiga\b/i,
  /\bfint färg\b/i,
  /\bfint färgade\b/i,
  /\bfärgad\b/i,
  /\bfräsch\b/i,
  /\bfräscha\b/i,
];

function requiresSafeCookingStep(ingredientIds: string[]): boolean {
  return ingredientIds.some((id) => RAW_PROTEIN_REQUIRING_SAFE_COOKING.has(id));
}

function hasSafeCookingStep(steps: Array<{ instructionSv: string }>): boolean {
  return steps.some((s) => {
    const instruction = s.instructionSv;
    const hasPositive = SAFE_COOKING_KEYWORDS_SV.some((re) => re.test(instruction));
    const onlyAppearance =
      UNSAFE_APPEARANCE_ONLY_SV.some((re) => re.test(instruction)) &&
      !SAFE_COOKING_KEYWORDS_SV.some((re) => re.test(instruction));
    return hasPositive && !onlyAppearance;
  });
}

function toSafetyInfo(ing: {
  id: string;
  allergens: string[];
  isVegan: boolean;
  isVegetarian: boolean;
  containsGluten: boolean;
  containsLactose: boolean;
  isPork: boolean;
  isBeef: boolean;
  isAlcohol: boolean;
}): IngredientSafetyInfo {
  return {
    id: ing.id,
    allergens: ing.allergens as IngredientSafetyInfo["allergens"],
    isVegan: ing.isVegan,
    isVegetarian: ing.isVegetarian,
    containsGluten: ing.containsGluten,
    containsLactose: ing.containsLactose,
    isPork: ing.isPork,
    isBeef: ing.isBeef,
    isAlcohol: ing.isAlcohol,
    dataVerified: true,
  };
}

export interface SafetyCanaryResult {
  allergenInvariantBrott: number;
  overifieradeVisade: number;
  foodSafetyLintAvvisade7d: number;
}

/**
 * Hourly safety canary: deterministic re-derivation of allergens and a
 * food-safety lint sample. Results are persisted in ops_safety_canary;
 * /ops/v1/summary only reads the latest row.
 */
export async function processSafetyCanary(ctx: WorkerContext): Promise<SafetyCanaryResult> {
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);

  const allIngredients = await ctx.db
    .select({
      id: schema.canonicalIngredients.id,
      allergens: schema.canonicalIngredients.allergens,
      isVegan: schema.canonicalIngredients.isVegan,
      isVegetarian: schema.canonicalIngredients.isVegetarian,
      containsGluten: schema.canonicalIngredients.containsGluten,
      containsLactose: schema.canonicalIngredients.containsLactose,
      isPork: schema.canonicalIngredients.isPork,
      isBeef: schema.canonicalIngredients.isBeef,
      isAlcohol: schema.canonicalIngredients.isAlcohol,
    })
    .from(schema.canonicalIngredients);

  const infoMap = new Map<string, IngredientSafetyInfo>();
  for (const ing of allIngredients) {
    infoMap.set(ing.id, toSafetyInfo(ing));
  }

  const recipes = await ctx.db
    .select({
      id: schema.recipes.id,
      allergens: schema.recipes.allergens,
      status: schema.recipes.status,
      verificationStatus: schema.recipes.verificationStatus,
      updatedAt: schema.recipes.updatedAt,
    })
    .from(schema.recipes);

  let allergenInvariantBrott = 0;
  let foodSafetyLintAvvisade7d = 0;

  for (const recipe of recipes) {
    const ingredients = await ctx.db
      .select({ canonicalIngredientId: schema.recipeIngredients.canonicalIngredientId })
      .from(schema.recipeIngredients)
      .where(eq(schema.recipeIngredients.recipeId, recipe.id));

    const ids = ingredients.map((i) => i.canonicalIngredientId);
    const derived = [...deriveRecipeAllergens(ids, infoMap)].sort();
    const stored = [...(recipe.allergens ?? [])].sort();
    if (JSON.stringify(derived) !== JSON.stringify(stored)) {
      allergenInvariantBrott++;
    }

    if (recipe.updatedAt && new Date(recipe.updatedAt) >= sevenDaysAgo) {
      const steps = await ctx.db
        .select({ instructionSv: schema.recipeSteps.instructionSv })
        .from(schema.recipeSteps)
        .where(eq(schema.recipeSteps.recipeId, recipe.id));
      if (requiresSafeCookingStep(ids) && !hasSafeCookingStep(steps)) {
        foodSafetyLintAvvisade7d++;
      }
    }
  }

  const publicUnverified = await ctx.db
    .select({ count: sql<number>`count(*)::int` })
    .from(schema.recipes)
    .where(
      and(
        eq(schema.recipes.status, "published"),
        sql`${schema.recipes.verificationStatus} IN ('unverified', 'rejected')`,
      ),
    );
  const overifieradeVisade = publicUnverified[0]?.count ?? 0;

  await ctx.db.insert(schema.opsSafetyCanary).values({
    allergenInvariantBrott,
    overifieradeVisade,
    foodSafetyLintAvvisade7d,
  });

  return { allergenInvariantBrott, overifieradeVisade, foodSafetyLintAvvisade7d };
}
