export interface OeeInput {
  allTimeAvailableMin: number
  plannedBreakMin: number
  plannedDowntimeMin: number
  unplannedDowntimeMin: number
  setupMin: number
  maintenanceMin: number
  otherLossMin: number
  idealCycleTimeSec?: number
  optimumTaktTimeSec?: number
  producedOutput: number
  goodOutput: number
  scrapOutput?: number
  reworkOutput?: number
  installedCapacity: number
  purchasedCapacity: number
  daysPerWeek?: number
  hoursPerDay?: number
  shiftsPerDay?: number
}

export interface OeeResult {
  totalOperationsTimeMin: number
  potentialProductionTimeMin: number
  actualProductionTimeMin: number
  availability: number
  performance: number
  quality: number
  oee: number
  theoreticalOutput: number
  plannedLossMin: number
  unplannedLossMin: number
  speedLossMin: number
  qualityLossMin: number
  warnings: OeeWarning[]
}

export interface OeeWarning {
  type: 'error' | 'warning' | 'info'
  field: string
  message: string
}

export function calculateOee(input: OeeInput): OeeResult {
  const warnings: OeeWarning[] = []

  const totalOperationsTimeMin = Math.max(0, input.allTimeAvailableMin - input.plannedBreakMin)
  const potentialProductionTimeMin = Math.max(0, totalOperationsTimeMin - input.plannedDowntimeMin)
  const unplannedTotal =
    input.unplannedDowntimeMin + input.setupMin + input.maintenanceMin + input.otherLossMin
  const actualProductionTimeMin = Math.max(0, potentialProductionTimeMin - unplannedTotal)

  const availability =
    potentialProductionTimeMin > 0
      ? Math.min(1, actualProductionTimeMin / potentialProductionTimeMin)
      : 0

  const taktSec = input.idealCycleTimeSec ?? input.optimumTaktTimeSec

  const theoreticalOutput =
    taktSec && taktSec > 0 ? (actualProductionTimeMin * 60) / taktSec : 0

  const performance =
    theoreticalOutput > 0 ? Math.min(1, input.producedOutput / theoreticalOutput) : 0

  const quality =
    input.producedOutput > 0 ? Math.min(1, input.goodOutput / input.producedOutput) : 0

  const oee = availability * performance * quality

  const plannedLossMin = input.plannedBreakMin + input.plannedDowntimeMin
  const unplannedLossMin = unplannedTotal
  const speedLossMin =
    taktSec && taktSec > 0
      ? Math.max(0, (theoreticalOutput - input.producedOutput) * taktSec / 60)
      : 0
  const qualityLossMin =
    taktSec && taktSec > 0
      ? Math.max(0, (input.producedOutput - input.goodOutput) * taktSec / 60)
      : 0

  // Warnings
  if (!taktSec) {
    warnings.push({
      type: 'error',
      field: 'takt',
      message: 'Taktzeit oder Zykluszeit erforderlich für Performance-Berechnung',
    })
  }

  if (input.goodOutput > input.producedOutput) {
    warnings.push({
      type: 'error',
      field: 'goodOutput',
      message: 'Gutmenge kann nicht größer als Gesamtmenge sein',
    })
  }

  if (theoreticalOutput > 0 && input.producedOutput > theoreticalOutput) {
    warnings.push({
      type: 'warning',
      field: 'producedOutput',
      message: 'Produzierte Menge übersteigt theoretische Kapazität — Taktzeit prüfen',
    })
  }

  if (input.purchasedCapacity > input.installedCapacity && input.installedCapacity > 0) {
    warnings.push({
      type: 'warning',
      field: 'purchasedCapacity',
      message: 'Eingekaufte Kapazität übersteigt installierte Kapazität',
    })
  }

  if (oee > 0 && oee < 0.5) {
    warnings.push({
      type: 'info',
      field: 'oee',
      message: 'OEE unter 50% — größte Verlustquelle identifizieren',
    })
  }

  if (
    potentialProductionTimeMin > 0 &&
    input.unplannedDowntimeMin > potentialProductionTimeMin * 0.3
  ) {
    warnings.push({
      type: 'warning',
      field: 'unplannedDowntime',
      message: 'Ungeplante Stillstände ungewöhnlich hoch (>30%)',
    })
  }

  return {
    totalOperationsTimeMin,
    potentialProductionTimeMin,
    actualProductionTimeMin,
    availability,
    performance,
    quality,
    oee,
    theoreticalOutput,
    plannedLossMin,
    unplannedLossMin,
    speedLossMin,
    qualityLossMin,
    warnings,
  }
}

/**
 * Wertstrom P4 (B3, Capability-Matrix, KAR-878/KAR-986) — standard
 * reliability-engineering Availability from Mean Time Between Failures /
 * Mean Time To Repair: `Availability = MTBF / (MTBF + MTTR)`. Both inputs
 * in minutes (same unit convention as the rest of this module's time
 * fields); the unit cancels out in the ratio, so any consistent unit works,
 * but minutes keeps it consistent with `OeeInput`.
 *
 * This is deliberately a SEPARATE formula from `calculateOee` above (which
 * derives availability from a time-loss breakdown) — MTBF/MTTR is a
 * different, reliability-data-driven way to arrive at the same concept, for
 * callers that have failure/repair statistics rather than a shift-level
 * time ledger. Kept in this module (not `lib/vsm-engine`) because
 * Availability is an OEE-domain formula this module already owns; a caller
 * (e.g. the Wertstrom editor) multiplies the returned fraction by 100 and
 * passes it on as `NodeCapacityOverrides.availabilityPct` to
 * `lib/vsm-engine`'s `computeEffectiveCapacity` — never duplicated there.
 *
 * Returns `null` (not 0, not NaN) when `mtbfMin + mttrMin <= 0` — no silent
 * fabricated availability for a nonsensical/incomplete input.
 */
export function calculateAvailabilityFromMtbfMttr(mtbfMin: number, mttrMin: number): number | null {
  if (!Number.isFinite(mtbfMin) || !Number.isFinite(mttrMin)) return null
  if (mtbfMin < 0 || mttrMin < 0) return null
  const denominator = mtbfMin + mttrMin
  // Review-Fix F10 (adversarial review, PR #355): `mtbfMin`/`mttrMin` can
  // each individually pass the Number.isFinite guard above yet still sum to
  // `Infinity` (e.g. both near Number.MAX_VALUE) — `Infinity` is not `<= 0`,
  // so without this check execution fell through to `mtbfMin / Infinity`,
  // silently yielding `0` for any finite mtbfMin. That directly contradicts
  // this function's own contract two paragraphs up ("Returns null (not 0,
  // not NaN) ... no silent fabricated availability").
  if (!Number.isFinite(denominator)) return null
  if (denominator <= 0) return null
  return mtbfMin / denominator
}

export function formatPercent(value: number): string {
  return `${(value * 100).toFixed(1)}%`
}

export function formatMinutesAsHours(min: number): string {
  const h = Math.floor(min / 60)
  const m = Math.round(min % 60)
  if (h === 0) return `${m} min`
  if (m === 0) return `${h}h`
  return `${h}h ${m}min`
}
