// A5 Inventory Coverage / Bestandsreichweite (execution-prompt §8.5) —
// Wertstrom P1. Little's Law: coverage time = quantity / demand rate.
//
// "Use a consistent demand basis" (§8.5) is implemented literally: minutes/
// hours/shifts/days are ALL derived from the SAME `coverageDays` (quantity ÷
// unitsPerDay) and the SAME shift model — never a different demand figure
// per unit. Hours/minutes are PRODUCTION time (via ShiftModelInput), not
// calendar time — a buffer described as "1.5 shifts of coverage" is the
// standard lean reading, not "36 calendar hours"; see README for the
// reasoning. `computeNetAvailableTimePerDay` (work-time.ts) is the single
// shared source for that shift-time conversion, reused verbatim here.

import type { MetricExplain } from './types'
import { computeNetAvailableTimePerDay, type ShiftModelInput } from './work-time'

/** Canonical demand basis — units consumed per working day. Mirrors
 * VsmNode.demand's EXISTING "Stk/Tag" unit 1:1 (see vsm-editor.tsx: `{node.demand} Stk/Tag`
 * on customer/supplier nodes) so a future caller can pass that field straight
 * through with zero conversion. Kept as the engine's own parameter type
 * (not reading VsmNode itself) for the same purity reasons as TaktInput. */
export interface InventoryDemandRate {
  unitsPerDay: number
}

export interface InventoryCoverageResult {
  /** Echoed back for convenience; `null` only when the input itself was `undefined`. */
  quantity: number | null
  unitsPerDay: number | null
  coverageDays: number | null
  /** `null` when no ShiftModelInput was supplied — days→shifts still needs shiftsPerDay. */
  coverageShifts: number | null
  /** `null` when no ShiftModelInput was supplied — production hours need the shift model. */
  coverageHours: number | null
  coverageMin: number | null
  explain: MetricExplain
}

const FORMULA =
  'Reichweite[Tage] = Bestandsmenge ÷ Bedarf[Stk/Tag] (Little\'s Law). Reichweite[Schichten] = Reichweite[Tage] × Schichten/Tag. Reichweite[Std./Min.] = Reichweite[Tage] × Netto-Arbeitszeit/Tag (Produktionszeit, nicht Kalenderzeit) in Std. bzw. Min.'
const DATA_BASIS = 'Bestandsmenge (z. B. VsmNode.quantity) + Bedarfsrate [Stk/Tag] (z. B. VsmNode.demand eines Kunden-Nodes) + optional ShiftModelInput für die Std./Schicht/Min.-Umrechnung.'

/**
 * A5 Bestandsreichweite. `quantity`/`demand` follow the P0-F3 nullish
 * doctrine: `undefined` ⇒ "unbekannt" (`null` result), a real `0` ⇒
 * "gemessen, tatsächlich null Bestand" (computes to `coverageDays: 0`, NOT
 * null) — these are deliberately distinguished, see the "quantity: 0" test.
 */
export function computeInventoryCoverage(
  quantity: number | undefined,
  demand: InventoryDemandRate | undefined,
  shiftModel?: ShiftModelInput,
): InventoryCoverageResult {
  const exclusions = [
    'Std./Schicht/Min.-Reichweite ist Produktionszeit (aus dem Arbeitszeitmodell), keine Kalenderzeit — "1,5 Schichten Reichweite" ist die lean-übliche Lesart, nicht "36 Kalenderstunden".',
  ]
  if (quantity === undefined || quantity === null) {
    return {
      quantity: null,
      unitsPerDay: demand?.unitsPerDay ?? null,
      coverageDays: null,
      coverageShifts: null,
      coverageHours: null,
      coverageMin: null,
      explain: { formula: FORMULA, dataBasis: DATA_BASIS, exclusions: [...exclusions, 'Bestandsmenge fehlt (undefined) — nicht berechenbar, nicht als 0 angenommen.'] },
    }
  }
  if (quantity < 0) {
    return {
      quantity,
      unitsPerDay: demand?.unitsPerDay ?? null,
      coverageDays: null,
      coverageShifts: null,
      coverageHours: null,
      coverageMin: null,
      explain: { formula: FORMULA, dataBasis: DATA_BASIS, exclusions: [...exclusions, 'Bestandsmenge ist negativ — ungültige Eingabe, nicht berechenbar.'] },
    }
  }
  if (!demand || !(demand.unitsPerDay > 0)) {
    return {
      quantity,
      unitsPerDay: demand?.unitsPerDay ?? null,
      coverageDays: null,
      coverageShifts: null,
      coverageHours: null,
      coverageMin: null,
      explain: { formula: FORMULA, dataBasis: DATA_BASIS, exclusions: [...exclusions, 'Bedarfsrate fehlt oder ist 0 — Division durch 0 vermieden, nicht berechenbar (kein stiller 0-Nenner).'] },
    }
  }

  const coverageDays = quantity / demand.unitsPerDay

  if (!shiftModel) {
    return {
      quantity,
      unitsPerDay: demand.unitsPerDay,
      coverageDays,
      coverageShifts: null,
      coverageHours: null,
      coverageMin: null,
      explain: { formula: FORMULA, dataBasis: DATA_BASIS, exclusions: [...exclusions, 'Kein Arbeitszeitmodell übergeben — Schichten/Std./Min.-Reichweite nicht berechenbar, nur Tage.'] },
    }
  }
  const net = computeNetAvailableTimePerDay(shiftModel)
  if (net.netSecPerDay == null) {
    // shiftsPerDay itself may be part of why the shift model was invalid
    // (e.g. shiftsPerDay <= 0) — only fall back to a shifts-only coverage
    // figure when shiftsPerDay is independently valid, never multiply by a
    // value that was itself part of the failure.
    const shiftsPerDayValid = shiftModel.shiftsPerDay > 0
    return {
      quantity,
      unitsPerDay: demand.unitsPerDay,
      coverageDays,
      coverageShifts: shiftsPerDayValid ? coverageDays * shiftModel.shiftsPerDay : null,
      coverageHours: null,
      coverageMin: null,
      explain: {
        formula: FORMULA,
        dataBasis: DATA_BASIS,
        exclusions: [
          ...exclusions,
          ...net.explain.exclusions,
          shiftsPerDayValid
            ? 'Arbeitszeitmodell ungültig — Std./Min.-Reichweite nicht berechenbar, Schichten-Reichweite braucht nur Schichten/Tag und bleibt verfügbar.'
            : 'Arbeitszeitmodell ungültig (auch Schichten/Tag) — weder Schichten- noch Std./Min.-Reichweite berechenbar.',
        ],
      },
    }
  }

  const totalCoverageSec = coverageDays * net.netSecPerDay
  return {
    quantity,
    unitsPerDay: demand.unitsPerDay,
    coverageDays,
    coverageShifts: coverageDays * shiftModel.shiftsPerDay,
    coverageHours: totalCoverageSec / 3600,
    coverageMin: totalCoverageSec / 60,
    explain: { formula: FORMULA, dataBasis: DATA_BASIS, exclusions },
  }
}
