import {
  EMPTY_NUTRITION,
  type NutritionDeclaration,
  type NutritionValues,
  type Unit,
} from "@app/shared-types";
import { toBase, toGrams, type IngredientUnitInfo } from "./units.js";

/** Skala näringsvärden med en faktor. */
export function scaleNutrition(values: NutritionValues, factor: number): NutritionValues {
  const scaled: NutritionValues = {
    kcal: values.kcal * factor,
    proteinG: values.proteinG * factor,
    carbsG: values.carbsG * factor,
    fatG: values.fatG * factor,
    saturatedFatG: values.saturatedFatG * factor,
    fiberG: values.fiberG * factor,
    sugarG: values.sugarG * factor,
    saltG: values.saltG * factor,
  };
  if (values.micro) {
    scaled.micro = Object.fromEntries(
      Object.entries(values.micro).map(([k, v]) => [k, v == null ? v : v * factor]),
    );
  }
  return scaled;
}

/** Summera två näringsvärden. */
export function addNutrition(a: NutritionValues, b: NutritionValues): NutritionValues {
  const sum: NutritionValues = {
    kcal: a.kcal + b.kcal,
    proteinG: a.proteinG + b.proteinG,
    carbsG: a.carbsG + b.carbsG,
    fatG: a.fatG + b.fatG,
    saturatedFatG: a.saturatedFatG + b.saturatedFatG,
    fiberG: a.fiberG + b.fiberG,
    sugarG: a.sugarG + b.sugarG,
    saltG: a.saltG + b.saltG,
  };
  const micros = { ...(a.micro ?? {}) } as Record<string, number | undefined>;
  if (b.micro) {
    for (const [k, v] of Object.entries(b.micro)) {
      if (v == null) continue;
      micros[k] = (micros[k] ?? 0) + v;
    }
  }
  if (Object.keys(micros).length > 0) sum.micro = micros;
  return sum;
}

export function sumNutrition(items: NutritionValues[]): NutritionValues {
  return items.reduce(addNutrition, { ...EMPTY_NUTRITION });
}

/** Avrunda för presentation (heltal kcal, en decimal på gram). */
export function roundNutrition(values: NutritionValues): NutritionValues {
  const r1 = (v: number) => Math.round(v * 10) / 10;
  const rounded: NutritionValues = {
    kcal: Math.round(values.kcal),
    proteinG: r1(values.proteinG),
    carbsG: r1(values.carbsG),
    fatG: r1(values.fatG),
    saturatedFatG: r1(values.saturatedFatG),
    fiberG: r1(values.fiberG),
    sugarG: r1(values.sugarG),
    saltG: r1(values.saltG),
  };
  if (values.micro) rounded.micro = values.micro;
  return rounded;
}

/**
 * Beräkna näring för en given mängd av en ingrediens/produkt utifrån dess
 * deklaration. Returnerar null när beräkningen inte kan göras säkert –
 * anroparen ansvarar då för att visa osäkerheten (spec §61.4), aldrig gissa.
 */
export function computeItemNutrition(
  quantity: number,
  unit: Unit,
  declaration: NutritionDeclaration,
  info: IngredientUnitInfo = {},
): NutritionValues | null {
  if (quantity < 0) return null;
  switch (declaration.basis) {
    case "per_100_g": {
      const grams = toGrams(quantity, unit, info);
      if (grams == null) return null;
      return scaleNutrition(declaration.values, grams / 100);
    }
    case "per_100_ml": {
      const base = toBase(quantity, unit);
      let ml: number | null = null;
      if (base.kind === "volume") ml = base.amount;
      else {
        // massa/antal → gram → ml via densitet
        const grams = toGrams(quantity, unit, info);
        const density = info.densityGPerMl;
        if (grams != null && density != null && density > 0) ml = grams / density;
      }
      if (ml == null) return null;
      return scaleNutrition(declaration.values, ml / 100);
    }
    case "per_piece": {
      const base = toBase(quantity, unit);
      if (base.kind === "count") return scaleNutrition(declaration.values, base.amount);
      // Vikt angiven: räkna om via referensvikt.
      const grams = toGrams(quantity, unit, info);
      const ref = declaration.referenceWeightG;
      if (grams == null || ref == null || ref <= 0) return null;
      return scaleNutrition(declaration.values, grams / ref);
    }
    case "per_portion": {
      const base = toBase(quantity, unit);
      if (base.kind === "count") return scaleNutrition(declaration.values, base.amount);
      return null;
    }
  }
}
