import { calculateBeaconDesign, calculateBiolabBeaconDesign } from "./beacons.ts";
import { beaconEffects, effectsForModules } from "./calculator.ts";
import { QUALITY_BY_ID, iconFor } from "./factorio-data.ts";
import type { Machine, Recipe, ScienceInput, ScienceResult } from "./types.ts";

export const SCIENCE_RECIPE: Recipe = {
  id: "research", name: "Research", categories: ["research"], energy: 60,
  ingredients: [], products: [], allowProductivity: true, allowQuality: false, icon: iconFor("lab"),
};
export const LABS: Record<"lab" | "biolab", Machine & { packDrain: number }> = {
  lab: {
    id: "lab", shortName: "Lab", name: "Lab", icon: iconFor("lab"), categories: ["research"],
    speed: 1, moduleSlots: 2, allowedEffects: ["consumption", "speed", "productivity", "pollution"],
    baseProductivity: 0, energyKw: 60, drainKw: 2, heatingKw: 100, energyKind: "electric",
    width: 3, height: 3, maxBeacons: 12, packDrain: 1,
  },
  biolab: {
    id: "biolab", shortName: "Biolab", name: "Biolab", icon: iconFor("biolab"), categories: ["research"],
    speed: 2, moduleSlots: 4, allowedEffects: ["consumption", "speed", "productivity", "pollution"],
    baseProductivity: 0, energyKw: 300, drainKw: 10, heatingKw: 100, energyKind: "electric",
    width: 5, height: 5, maxBeacons: 16, packDrain: .5,
  },
};
const PACK_DURABILITY = { normal: 1, uncommon: 2, rare: 3, epic: 4, legendary: 6 } as const;

export function calculateScience(input: ScienceInput): ScienceResult {
  const lab = LABS[input.labId];
  const machineEffects = effectsForModules(input.machineModules, lab, SCIENCE_RECIPE);
  const forcedBiolabRing = input.labId === "biolab" && input.beaconEnabled && input.biolabBeaconMode !== "manual";
  const requested = !input.beaconEnabled ? 0 : forcedBiolabRing ? 16 : Math.min(input.beaconCount, lab.maxBeacons);
  const isolated = beaconEffects(requested, input.beaconModules, lab, SCIENCE_RECIPE, input.beaconQuality);
  const speedFactor = Math.max(.2, 1 + machineEffects.speed + isolated.speed);
  const labQuality = QUALITY_BY_ID.get(input.labQuality) ?? QUALITY_BY_ID.get("normal")!;
  const researchSpeed = lab.speed * labQuality.multiplier * speedFactor;
  const productivity = Math.max(0, 1 + machineEffects.productivity);
  const effectivePerPack =
    (PACK_DURABILITY[input.packQuality] * productivity) / lab.packDrain;
  const actualSpm =
    input.mode === "actual"
      ? input.amountPerMinute
      : input.amountPerMinute / effectivePerPack;
  const effectiveSpm =
    input.mode === "effective"
      ? input.amountPerMinute
      : input.amountPerMinute * effectivePerPack;
  const actualCapacityPerLab = researchSpeed * lab.packDrain;
  const effectiveCapacityPerLab =
    researchSpeed * PACK_DURABILITY[input.packQuality] * productivity;
  const labsTheoretical =
    input.mode === "actual"
      ? actualCapacityPerLab > 0
        ? actualSpm / actualCapacityPerLab
        : 0
      : effectiveCapacityPerLab > 0
        ? effectiveSpm / effectiveCapacityPerLab
        : 0;
  const labsToBuild = actualSpm > 0 ? Math.max(1, Math.ceil(labsTheoretical - 1e-12)) : 0;
  const beaconLayout =
    input.labId === "biolab" && requested > 0
      ? calculateBiolabBeaconDesign(lab, labsToBuild, requested)
      : calculateBeaconDesign(lab, labsToBuild, requested);
  const utilization = labsToBuild > 0 ? Math.min(1, labsTheoretical / labsToBuild) : 0;
  const energyModifier = Math.max(.2, 1 + machineEffects.consumption + isolated.consumption);
  const machineKw = labsToBuild * (lab.energyKw * energyModifier * utilization + (lab.drainKw ?? 0));
  const beaconQ = QUALITY_BY_ID.get(input.beaconQuality) ?? QUALITY_BY_ID.get("normal")!;
  const beaconKw = beaconLayout.uniqueBeacons * 480 * beaconQ.beaconPowerMultiplier;
  return {
    actualSpm, effectiveSpm, effectivePerPack, actualCapacityPerLab,
    effectiveCapacityPerLab, labsTheoretical, labsToBuild, researchSpeed,
    packDrainMultiplier: lab.packDrain, electricKw: machineKw + beaconKw,
    beaconLayout, forcedBiolabRing,
  };
}
