// A3 Customer Takt (execution-prompt §8.1/§8.2) — Wertstrom P1.
//
// Takt time = Net available production time / Customer demand.
//
// Standard input is deliberately the four fields §8.2 names (hours/shift,
// shifts/day, break time, working days) plus one demand figure — "the
// standard user should only need to enter: demand, working time" (§8.1).
// Detailed shift calendars are Expert Mode (P4+, Capability-Matrix B2) and
// explicitly NOT built here — no shift-calendar entity, no DB migration
// (P1-Brief Nicht-Scope). Product mix (B1) is also not modeled — Standard
// mode is single-product, matching B1's "NIE Pflicht" doctrine.
//
// This module owns its own parameter types (ShiftModelInput/TaktInput)
// rather than reading VsmNode fields directly, because none of these
// concepts (shift structure, demand quantity) exist on VsmNode today and
// P1 is scoped to NOT add new fields there (see README "Why no VsmNode
// changes"). A future caller (P3+ UI/API wiring) assembles these from
// wherever the real inputs live.

import type { MetricExplain } from './types'

/** The "simple work-time model" (§8.2 Standard input) for ONE working day. */
export interface ShiftModelInput {
  /** Hours per shift, e.g. 8. Must be > 0. */
  hoursPerShift: number
  /** Number of shifts per working day, e.g. 2. Must be > 0. */
  shiftsPerDay: number
  /** Total break minutes PER SHIFT (all breaks in that shift, summed), e.g. 30. May be 0. */
  breakMinPerShift: number
  /** Advanced/optional: planned downtime minutes per shift (maintenance,
   * changeover, …) — §8.1 lists "planned downtime" as a consideration but
   * §8.2's Standard input list omits it, so it stays optional here. Omitted
   * ⇒ NOT subtracted (never silently treated as "confirmed zero downtime" —
   * flagged explicitly in the result's `explain.exclusions` whenever the
   * caller left it out). */
  plannedDowntimeMinPerShift?: number
}

export interface NetAvailableTimeResult {
  /** Net available production seconds for ONE working day, or `null` when
   * the shift model is missing/invalid (never a fabricated 0). */
  netSecPerDay: number | null
  netSecPerShift: number | null
  explain: MetricExplain
}

const SHIFT_MODEL_FORMULA =
  'Netto-Arbeitszeit/Schicht = Std./Schicht × 3600 − Pausen[min]×60 − geplanter Stillstand[min]×60 (Stillstand nur wenn angegeben). Netto-Arbeitszeit/Tag = Netto-Arbeitszeit/Schicht × Schichten/Tag.'

/**
 * Net available production time for a single working day from the simple
 * shift model (§8.2). Pure building block reused by both `computeTakt`
 * (multiplied out over `workingDays`) and `computeInventoryCoverage`
 * (used directly, per day) — the single source of truth for "how much
 * production time is actually in a working day" so the two metrics can
 * never drift apart (the concrete mechanism behind §8.5's "use a
 * consistent demand basis").
 */
export function computeNetAvailableTimePerDay(shiftModel: ShiftModelInput | undefined): NetAvailableTimeResult {
  const exclusions: string[] = []
  if (!shiftModel) {
    return {
      netSecPerDay: null,
      netSecPerShift: null,
      explain: { formula: SHIFT_MODEL_FORMULA, dataBasis: 'ShiftModelInput (Std./Schicht, Schichten/Tag, Pausen, optional Stillstand).', exclusions: ['Kein Arbeitszeitmodell übergeben — nicht berechenbar.'] },
    }
  }
  if (shiftModel.plannedDowntimeMinPerShift === undefined) {
    exclusions.push('Kein geplanter Stillstand angegeben — NICHT als "bestätigt null" behandelt, einfach nicht abgezogen. Die Netto-Zeit kann dadurch optimistischer sein als real.')
  }
  const invalid =
    !(shiftModel.hoursPerShift > 0) ||
    !(shiftModel.shiftsPerDay > 0) ||
    shiftModel.breakMinPerShift < 0 ||
    (shiftModel.plannedDowntimeMinPerShift !== undefined && shiftModel.plannedDowntimeMinPerShift < 0)
  if (invalid) {
    return {
      netSecPerDay: null,
      netSecPerShift: null,
      explain: {
        formula: SHIFT_MODEL_FORMULA,
        dataBasis: 'ShiftModelInput (Std./Schicht, Schichten/Tag, Pausen, optional Stillstand).',
        exclusions: ['Ungültige Eingabe (Std./Schicht oder Schichten/Tag ≤ 0, oder eine negative Pausen-/Stillstandszeit) — nicht berechenbar.'],
      },
    }
  }
  const netSecPerShift = shiftModel.hoursPerShift * 3600 - shiftModel.breakMinPerShift * 60 - (shiftModel.plannedDowntimeMinPerShift ?? 0) * 60
  if (netSecPerShift <= 0) {
    return {
      netSecPerDay: null,
      netSecPerShift: null,
      explain: {
        formula: SHIFT_MODEL_FORMULA,
        dataBasis: 'ShiftModelInput (Std./Schicht, Schichten/Tag, Pausen, optional Stillstand).',
        exclusions: ['Pausen und/oder geplanter Stillstand summieren sich auf die volle Schichtzeit oder mehr — Netto-Arbeitszeit wäre 0 oder negativ. Nicht berechenbar (kein stiller 0-Ersatz).'],
      },
    }
  }
  return {
    netSecPerDay: netSecPerShift * shiftModel.shiftsPerDay,
    netSecPerShift,
    explain: { formula: SHIFT_MODEL_FORMULA, dataBasis: 'ShiftModelInput (Std./Schicht, Schichten/Tag, Pausen, optional Stillstand).', exclusions },
  }
}

export interface TaktInput {
  shiftModel: ShiftModelInput
  /** Working days spanned by `demandQuantity` — e.g. 20 working days in the
   * month `demandQuantity` covers. Must be > 0. */
  workingDays: number
  /** Total demand quantity for the SAME period `workingDays` spans (§8.1
   * "the standard user should only need to enter: demand, working time").
   * Must be > 0. */
  demandQuantity: number
}

export interface TaktResult {
  taktTimeSec: number | null
  netAvailableTimeSecTotal: number | null
  netAvailableTimeSecPerDay: number | null
  explain: MetricExplain
}

const TAKT_FORMULA =
  'Taktzeit = Netto-Arbeitszeit (gesamter Zeitraum) ÷ Bedarfsmenge (für denselben Zeitraum). Netto-Arbeitszeit (gesamt) = Netto-Arbeitszeit/Tag × Arbeitstage.'
const TAKT_DATA_BASIS = 'TaktInput: ShiftModelInput + Arbeitstage + Bedarfsmenge, jeweils für denselben Zeitraum. Kein VsmNode-Feld — die Engine liest diese Werte nicht selbst aus dem Wertstrom, der Aufrufer liefert sie (z. B. aus einem Node mit demand-Feld oder aus projects.customer_takt_time_sec-Kontext).'
const TAKT_EXCLUSIONS_BASE = [
  'Kein Produktmix — Standardmodus rechnet mit einer einzigen Bedarfsmenge (Capability-Matrix B1 ist Expert-Mode, "NIE Pflicht").',
  'Kein Schichtkalender — nur das einfache Arbeitszeitmodell; benannte/differenzierte Kalender sind Expert-Mode (§8.2, außerhalb P1-Scope).',
  'Kein Runden des Ergebnisses — Rundung/Darstellung ist Sache der aufrufenden UI, nicht der Engine.',
]

/**
 * A3 Customer Takt (§8.1). `Takt = Netto-Arbeitszeit / Bedarf`, both over
 * the SAME period (`workingDays`). See module doc for why this takes its
 * own parameter object instead of reading VsmNode/project fields.
 */
export function computeTakt(input: TaktInput | undefined): TaktResult {
  if (!input) {
    return { taktTimeSec: null, netAvailableTimeSecTotal: null, netAvailableTimeSecPerDay: null, explain: { formula: TAKT_FORMULA, dataBasis: TAKT_DATA_BASIS, exclusions: [...TAKT_EXCLUSIONS_BASE, 'Keine Eingabe übergeben — nicht berechenbar.'] } }
  }
  const perDay = computeNetAvailableTimePerDay(input.shiftModel)
  if (perDay.netSecPerDay == null) {
    return {
      taktTimeSec: null,
      netAvailableTimeSecTotal: null,
      netAvailableTimeSecPerDay: null,
      explain: { formula: TAKT_FORMULA, dataBasis: TAKT_DATA_BASIS, exclusions: [...TAKT_EXCLUSIONS_BASE, ...perDay.explain.exclusions] },
    }
  }
  if (!(input.workingDays > 0) || !(input.demandQuantity > 0)) {
    return {
      taktTimeSec: null,
      netAvailableTimeSecTotal: null,
      netAvailableTimeSecPerDay: perDay.netSecPerDay,
      explain: {
        formula: TAKT_FORMULA,
        dataBasis: TAKT_DATA_BASIS,
        exclusions: [...TAKT_EXCLUSIONS_BASE, !(input.workingDays > 0) ? 'Arbeitstage ≤ 0 — nicht berechenbar.' : 'Bedarfsmenge ist 0 oder fehlt — Division durch 0 vermieden, nicht berechenbar (kein stiller 0-Takt).'],
      },
    }
  }
  const netAvailableTimeSecTotal = perDay.netSecPerDay * input.workingDays
  return {
    taktTimeSec: netAvailableTimeSecTotal / input.demandQuantity,
    netAvailableTimeSecTotal,
    netAvailableTimeSecPerDay: perDay.netSecPerDay,
    explain: { formula: TAKT_FORMULA, dataBasis: TAKT_DATA_BASIS, exclusions: [...TAKT_EXCLUSIONS_BASE, ...perDay.explain.exclusions] },
  }
}
