// Stationsbegriff, Takt und Kapazität der Fertigung (Spezifikation Kap. 7.4/8.8,
// R-10).
//
// Zwei Ebenen liegen im Fertigungsblatt übereinander: beschreibende
// Untervorgänge ohne Zeit und Kosten, und kostenwirksame Stationen. Wer beide
// zusammenwirft, zählt je nach Zählweise 31 oder 32 Stationen — und genau
// dieser Streit lässt sich nicht diskutieren, sondern nur definieren.
//
// Die Definition hier ist die der Spezifikation: kostenwirksam ist eine Zeile
// mit Zykluszeit, Mitarbeitereinsatz oder Kosten. Eine benannte Zeile ohne
// jeden Parameter ist keine Station — sie erzeugt aber einen Datenbefund,
// damit sie nicht einfach verschwindet.
//
// Pure Funktionen, keine I/O.

/** Eine Zeile des Fertigungsblatts, wie die Auswertung sie braucht. */
export interface ManufacturingRow {
  row: number
  name: string
  /** Zykluszeit in Sekunden. */
  cycleSeconds: number | null
  /** Anzahl Mitarbeiter an der Station. */
  workers: number | null
  /** Fertigungskosten je Stück in Angebotswährung. */
  costPerPiece: number | null
}

export type RowKind = 'station' | 'sub_operation' | 'named_without_parameters'

export interface ClassifiedRow extends ManufacturingRow {
  kind: RowKind
}

/**
 * Rollen der Zeilen bestimmen.
 *
 * `named_without_parameters` ist die Klasse, die den Zählstreit auflöst: eine
 * Zeile trägt einen Namen, aber weder Zeit noch Personal noch Kosten. Sie als
 * Station zu zählen überzeichnet die Fertigungstiefe, sie stillschweigend
 * fallen zu lassen verliert einen Befund. Also: eigene Klasse, sichtbar.
 */
export function classifyRows(rows: ManufacturingRow[]): ClassifiedRow[] {
  return rows.map((r) => {
    const hasCycle = (r.cycleSeconds ?? 0) > 0
    const hasWorkers = (r.workers ?? 0) > 0
    const hasCost = (r.costPerPiece ?? 0) > 0
    if (hasCycle || hasWorkers || hasCost) return { ...r, kind: 'station' }
    // Kein Parameter: trägt die Zeile wenigstens einen Namen, ist das ein
    // Befund; sonst ist es eine leere Zeile.
    return { ...r, kind: r.name.trim() === '' ? 'sub_operation' : 'named_without_parameters' }
  })
}

export interface TaktInput {
  /** Arbeitsstunden pro Jahr laut Prämissenblatt. */
  hoursPerYear: number | null
  /** Stückzahl im Spitzenjahr. */
  peakVolumePerYear: number | null
  /** Gesamtvolumen über die Laufzeit. */
  totalVolume: number | null
  /** Serienlaufzeit in Jahren. */
  lifetimeYears: number | null
}

export interface Takt {
  secondsPerYear: number | null
  /** Taktzeit im Spitzenjahr — die Grenze, an der Kapazität gemessen wird. */
  peakTaktSeconds: number | null
  /** Taktzeit im Mittel über die Laufzeit. */
  averageTaktSeconds: number | null
  /**
   * Ausdrücklich `false`: Das Formblatt gibt keinen OEE-Faktor her und lässt
   * ihn auch nicht ableiten. Die Auslastungen sind deshalb Untergrenzen — real
   * liegen sie höher. Das gehört an jede Zahl, die daraus entsteht.
   */
  oeeIncluded: false
}

export function computeTakt(input: TaktInput): Takt {
  const { hoursPerYear, peakVolumePerYear, totalVolume, lifetimeYears } = input
  const secondsPerYear = hoursPerYear === null ? null : hoursPerYear * 3600

  const peakTaktSeconds =
    secondsPerYear === null || peakVolumePerYear === null || peakVolumePerYear <= 0
      ? null
      : secondsPerYear / peakVolumePerYear

  const averageVolume =
    totalVolume === null || lifetimeYears === null || lifetimeYears <= 0 ? null : totalVolume / lifetimeYears
  const averageTaktSeconds =
    secondsPerYear === null || averageVolume === null || averageVolume <= 0 ? null : secondsPerYear / averageVolume

  return { secondsPerYear, peakTaktSeconds, averageTaktSeconds, oeeIncluded: false }
}

export interface StationCapacity {
  row: number
  name: string
  cycleSeconds: number
  /** Stückzahl, die diese Station in einem Jahr schafft. */
  capacityPerYear: number | null
  /** Auslastung im Spitzenjahr, 1 = genau ausgelastet. */
  utilizationPeak: number | null
  utilizationAverage: number | null
  /** Wie oft die Station parallel stehen muss, um den Spitzenbedarf zu decken. */
  parallelNeededPeak: number | null
}

/**
 * Kapazität je Station gegen die Taktzeit.
 *
 * Eine Station über 100 % Auslastung ist nicht knapp, sondern rechnerisch
 * unmöglich — sie braucht ein zweites Exemplar. Die Zahl ohne OEE ist dabei
 * die freundlichste denkbare: mit Verfügbarkeitsverlusten wird es schlechter,
 * nie besser.
 */
export function computeCapacity(stations: ClassifiedRow[], takt: Takt): StationCapacity[] {
  return stations
    .filter((s): s is ClassifiedRow & { cycleSeconds: number } => s.kind === 'station' && (s.cycleSeconds ?? 0) > 0)
    .map((s) => {
      const capacityPerYear = takt.secondsPerYear === null ? null : takt.secondsPerYear / s.cycleSeconds
      const utilizationPeak = takt.peakTaktSeconds === null ? null : s.cycleSeconds / takt.peakTaktSeconds
      const utilizationAverage =
        takt.averageTaktSeconds === null ? null : s.cycleSeconds / takt.averageTaktSeconds
      return {
        row: s.row,
        name: s.name,
        cycleSeconds: s.cycleSeconds,
        capacityPerYear,
        utilizationPeak,
        utilizationAverage,
        // Aufrunden genügt: die Auslastung ist hier immer grösser als null
        // (Stationen ohne Zykluszeit sind oben herausgefiltert, und ohne
        // Taktzeit steht null da). Eine zusätzliche Untergrenze wäre ein
        // Schutz gegen einen Fall, den es nicht gibt — die Mutations-Probe hat
        // sie als wirkungslos entlarvt.
        parallelNeededPeak: utilizationPeak === null ? null : Math.ceil(utilizationPeak),
      }
    })
}

/** Stationen, die den Spitzenbedarf allein nicht decken können. */
export function stationsOverPeak(capacity: StationCapacity[]): StationCapacity[] {
  return capacity.filter((c) => c.utilizationPeak !== null && c.utilizationPeak > 1)
}
