import { beaconEffects } from "./calculator.ts";
import { QUALITY_BY_ID } from "./factorio-data.ts";
import type { CalculatorInput, CalculationResult, LogisticsResult, LogisticsSettings, MaterialFlow } from "./types.ts";

export const INSERTERS = {
  inserter: { belt: .83, container: 1.66, stack: 1, kw: 13 },
  "fast-inserter": { belt: 2.31, container: 4.62, stack: 1, kw: 46 },
  "bulk-inserter": { belt: 2.31, container: 4.62, stack: 2, kw: 133 },
  "stack-inserter": { belt: 2.31, container: 4.62, stack: 2, kw: 132 },
} as const;

function perMachineShares(result: CalculationResult, input: CalculatorInput): number[] {
  if (!result.machinesToBuild) return [];
  const counts = result.beaconLayout.counts.length
    ? result.beaconLayout.counts
    : Array.from({ length: result.machinesToBuild }, () => input.beaconCount);
  const quality = QUALITY_BY_ID.get(input.machineQuality) ?? QUALITY_BY_ID.get("normal")!;
  const capacities = counts.map((count) => {
    const effects = beaconEffects(count, input.beaconModules, input.machine, input.recipe, input.beaconQuality);
    return input.machine.speed * quality.multiplier *
      Math.max(.2, 1 + result.machineEffects.speed + effects.speed) / input.recipe.energy;
  });
  const sum = capacities.reduce((a, b) => a + b, 0);
  if (!sum) return capacities.map(() => 1 / capacities.length);
  return capacities.map((value) => value / sum);
}

export function itemThroughput(settings: LogisticsSettings): number {
  const model = INSERTERS[settings.inserterId];
  const handoffRate = settings.handoff.includes("container") ? model.container : model.belt;
  const quality = QUALITY_BY_ID.get(settings.inserterQuality) ?? QUALITY_BY_ID.get("normal")!;
  const stack = Math.max(1, model.stack + settings.capacityBonus);
  return handoffRate * quality.multiplier * stack;
}

export function inserterPowerKw(id: LogisticsSettings["inserterId"]): number {
  return INSERTERS[id].kw;
}

export function calculateLogistics(
  result: CalculationResult,
  input: CalculatorInput,
  settings: LogisticsSettings,
): LogisticsResult {
  if (!result.valid || !result.machinesToBuild) return { flows: [], electricKw: 0, warnings: [] };
  const shares = perMachineShares(result, input);
  const perItemCapacity = itemThroughput(settings);
  const all: Array<{ flow: MaterialFlow; direction: "input" | "output" }> = [
    ...result.inputs.map((flow) => ({ flow, direction: "input" as const })),
    ...result.outputs.map((flow) => ({ flow, direction: "output" as const })),
  ];
  let electricKw = 0;
  const warnings: string[] = [];
  const flows = all.map(({ flow, direction }) => {
    const rates = shares.map((share) => flow.total * share);
    const perMachineMin = rates.length ? Math.min(...rates) : 0;
    const perMachineMax = rates.length ? Math.max(...rates) : 0;
    if (flow.type === "fluid") {
      const pumps = Math.ceil(flow.total / 1200 - 1e-12);
      electricKw += pumps * 30;
      return {
        id: flow.id, name: flow.name, icon: flow.icon, type: flow.type, direction,
        totalPerSecond: flow.total, perMachineMin, perMachineMax, pumps,
      };
    }
    const insertersPerMachine = Math.max(0, Math.ceil(perMachineMax / perItemCapacity - 1e-12));
    const totalInserters = insertersPerMachine * result.machinesToBuild;
    electricKw += totalInserters * INSERTERS[settings.inserterId].kw;
    const warning = perMachineMax > 240
      ? `${flow.name}: one machine handles ${perMachineMax.toFixed(1)} items/s, above one fully stacked turbo belt (240/s).`
      : undefined;
    if (warning) warnings.push(warning);
    return {
      id: flow.id, name: flow.name, icon: flow.icon, type: flow.type, direction,
      totalPerSecond: flow.total, perMachineMin, perMachineMax, insertersPerMachine,
      totalInserters, beltLanes: flow.total / 120, warning,
    };
  });
  return { flows, electricKw, warnings };
}

export const DEFAULT_LOGISTICS: LogisticsSettings = {
  inserterId: "stack-inserter",
  inserterQuality: "normal",
  capacityBonus: 7,
  handoff: "belt-machine",
};
