import type { Allergen, DietPattern, ReligiousRule } from "@app/shared-types";

/**
 * DETERMINISTISK säkerhets- och kostfiltrering (spec §13, §61.2).
 * Ingen AI får någonsin avgöra allergisäkerhet. Denna modul är enda vägen.
 *
 * Försiktighetsprincip: om en ingrediens saknar data ("unknown") behandlas
 * receptet som EJ säkert för berörda filter och flaggas för granskning.
 */

export interface IngredientSafetyInfo {
  id: string;
  allergens: Allergen[];
  mayContainAllergens?: Allergen[];
  isVegan: boolean;
  isVegetarian: boolean;
  containsGluten: boolean;
  containsLactose: boolean;
  isPork: boolean;
  isBeef: boolean;
  isAlcohol: boolean;
  /** true när datan är verifierad; ovverifierad data ger varning i stället för tyst OK. */
  dataVerified?: boolean;
}

export interface DietaryConstraints {
  allergens: Allergen[];
  intolerances?: string[];
  dietPattern?: DietPattern;
  religiousRule?: ReligiousRule;
  avoidIngredientIds?: string[];
  spiceLevelMax?: number;
  /** Behandla "kan innehålla spår av" som blockerande (default true vid allergi). */
  blockMayContain?: boolean;
}

export type ViolationSeverity = "blocker" | "warning";

export interface SafetyViolation {
  severity: ViolationSeverity;
  code:
    | "allergen"
    | "allergen_may_contain"
    | "diet_pattern"
    | "religious_rule"
    | "avoided_ingredient"
    | "spice_level"
    | "unverified_data";
  ingredientId?: string;
  allergen?: Allergen;
  messageSv: string;
}

export interface RecipeSafetyInput {
  ingredients: Array<{ canonicalIngredientId: string; optional: boolean }>;
  spiceLevel: number;
}

/**
 * Returnerar ALLA överträdelser (tom lista = säkert enligt tillgänglig data).
 * Valfria ingredienser ger varning i stället för blocker (kan uteslutas).
 */
export function checkRecipeSafety(
  recipe: RecipeSafetyInput,
  constraints: DietaryConstraints,
  ingredientInfo: Map<string, IngredientSafetyInfo>,
): SafetyViolation[] {
  const violations: SafetyViolation[] = [];
  const userAllergens = new Set(constraints.allergens);
  const avoid = new Set(constraints.avoidIngredientIds ?? []);
  const blockMayContain = constraints.blockMayContain ?? userAllergens.size > 0;

  for (const ing of recipe.ingredients) {
    const info = ingredientInfo.get(ing.canonicalIngredientId);
    const severity: ViolationSeverity = ing.optional ? "warning" : "blocker";

    if (!info) {
      violations.push({
        severity: "warning",
        code: "unverified_data",
        ingredientId: ing.canonicalIngredientId,
        messageSv: `Ingrediensen ${ing.canonicalIngredientId} saknar säkerhetsdata – kontrollera manuellt.`,
      });
      continue;
    }

    for (const allergen of info.allergens) {
      if (userAllergens.has(allergen)) {
        violations.push({
          severity,
          code: "allergen",
          ingredientId: info.id,
          allergen,
          messageSv: `Innehåller ${allergen} (${info.id}).`,
        });
      }
    }
    if (blockMayContain) {
      for (const allergen of info.mayContainAllergens ?? []) {
        if (userAllergens.has(allergen)) {
          violations.push({
            severity: "warning",
            code: "allergen_may_contain",
            ingredientId: info.id,
            allergen,
            messageSv: `Kan innehålla spår av ${allergen} (${info.id}).`,
          });
        }
      }
    }

    const diet = constraints.dietPattern;
    if (diet === "vegan" && !info.isVegan) {
      violations.push({
        severity,
        code: "diet_pattern",
        ingredientId: info.id,
        messageSv: `${info.id} är inte veganskt.`,
      });
    } else if ((diet === "vegetarian" || diet === "pescatarian") && !info.isVegetarian) {
      const isFish = info.allergens.includes("fish") || info.allergens.includes("crustaceans");
      const allowed = diet === "pescatarian" && isFish;
      if (!allowed) {
        violations.push({
          severity,
          code: "diet_pattern",
          ingredientId: info.id,
          messageSv: `${info.id} är inte ${diet === "vegetarian" ? "vegetariskt" : "pescetarianskt"}.`,
        });
      }
    }

    const rule = constraints.religiousRule;
    if (rule === "halal" && (info.isPork || info.isAlcohol)) {
      violations.push({
        severity,
        code: "religious_rule",
        ingredientId: info.id,
        messageSv: `${info.id} är inte förenligt med halal (${info.isPork ? "fläsk" : "alkohol"}).`,
      });
    }
    if (
      rule === "kosher" &&
      (info.isPork || info.allergens.includes("crustaceans") || info.allergens.includes("molluscs"))
    ) {
      violations.push({
        severity,
        code: "religious_rule",
        ingredientId: info.id,
        messageSv: `${info.id} är inte förenligt med kosher.`,
      });
    }
    if (rule === "hindu_no_beef" && info.isBeef) {
      violations.push({
        severity,
        code: "religious_rule",
        ingredientId: info.id,
        messageSv: `${info.id} innehåller nötkött.`,
      });
    }
    if (rule === "buddhist_vegetarian" && !info.isVegetarian) {
      violations.push({
        severity,
        code: "religious_rule",
        ingredientId: info.id,
        messageSv: `${info.id} är inte vegetariskt.`,
      });
    }

    if (avoid.has(info.id)) {
      violations.push({
        severity,
        code: "avoided_ingredient",
        ingredientId: info.id,
        messageSv: `${info.id} finns på din undviklista.`,
      });
    }
  }

  if (constraints.spiceLevelMax != null && recipe.spiceLevel > constraints.spiceLevelMax) {
    violations.push({
      severity: "warning",
      code: "spice_level",
      messageSv: `Styrka ${recipe.spiceLevel} överstiger din maxnivå ${constraints.spiceLevelMax}.`,
    });
  }

  return violations;
}

/** true om receptet är helt fritt från blockers. */
export function isRecipeSafe(violations: SafetyViolation[]): boolean {
  return !violations.some((v) => v.severity === "blocker");
}

/** Härled ett recepts allergener deterministiskt ur ingredienserna (spec §61.2). */
export function deriveRecipeAllergens(
  ingredientIds: string[],
  ingredientInfo: Map<string, IngredientSafetyInfo>,
): Allergen[] {
  const set = new Set<Allergen>();
  for (const id of ingredientIds) {
    const info = ingredientInfo.get(id);
    for (const a of info?.allergens ?? []) set.add(a);
  }
  return [...set].sort();
}
