/**
 * Pure aggregation helpers for the reporting dashboard.
 * All functions are side-effect-free and unit-testable.
 */

// ─── Types ─────────────────────────────────────────────────────────────────

export interface ProcessStepWithMeasurements {
  id: string
  project_id: string
  station_name: string
  /** cycle_measurements joined on process_step_id */
  cycle_measurements: Array<{
    cycle_time_sec: number
    is_outlier: boolean
  }>
}

export interface LscWorkshopStats {
  totalStations: number
  measuredStations: number
  avgCycleTimeSec: number | null
}

export interface BottleneckResult {
  stationName: string
  avgCycleTimeSec: number
  /** true = avg CT > customer_takt_time_sec (takt-relative per F-019) */
  isOverTakt: boolean
  /** percent of takt, e.g. 1.25 means 125% of takt */
  taktRatio: number | null
}

export interface TaktAchievement {
  /** Measured avg CT vs customer_takt_time_sec, as ratio */
  ctVsCustomerTakt: number | null
  /** Measured avg CT vs target_cycle_time_sec, as ratio */
  ctVsTargetCt: number | null
}

export interface LscMeasureStatus {
  open: number
  in_progress: number
  done: number
}

// ─── Functions ─────────────────────────────────────────────────────────────

/**
 * Mean cycle time across the non-outlier measurements (`is_outlier` falsy).
 *
 * Single source of truth for "average cycle time of a station/measurement set"
 * (KAR-704 O7). Returns `null` when there is no valid measurement, so callers
 * never divide by zero. Outlier exclusion is applied here — pass the raw
 * measurement list (optionally pre-filtered by station/step).
 */
export function avgCycleTimeSec(
  measurements: ReadonlyArray<{ cycle_time_sec: number; is_outlier?: boolean | null }>,
): number | null {
  const valid = measurements.filter((m) => !m.is_outlier)
  if (valid.length === 0) return null
  return valid.reduce((sum, m) => sum + m.cycle_time_sec, 0) / valid.length
}

/**
 * Aggregate LSC workshop progress from process steps and their measurements.
 * - totalStations: number of steps in the project
 * - measuredStations: steps that have at least one non-outlier measurement
 * - avgCycleTimeSec: average CT across all non-outlier measurements
 */
export function calcLscWorkshopStats(
  steps: ProcessStepWithMeasurements[],
): LscWorkshopStats {
  const totalStations = steps.length

  let measuredStations = 0
  let sumCt = 0
  let countCt = 0

  for (const step of steps) {
    const validMeasurements = step.cycle_measurements.filter(
      (m) => !m.is_outlier,
    )
    if (validMeasurements.length > 0) {
      measuredStations++
      for (const m of validMeasurements) {
        sumCt += m.cycle_time_sec
        countCt++
      }
    }
  }

  return {
    totalStations,
    measuredStations,
    avgCycleTimeSec: countCt > 0 ? sumCt / countCt : null,
  }
}

/**
 * Identify the bottleneck station for a project.
 * Per F-019: only mark as bottleneck when avg CT > customer_takt_time_sec.
 * When all stations are under takt, returns the worst station but marks
 * isOverTakt=false.
 * Returns null when no steps have measurements.
 */
export function calcBottleneck(
  steps: ProcessStepWithMeasurements[],
  customerTaktTimeSec: number | null,
): BottleneckResult | null {
  type StationAvg = { stationName: string; avgCt: number }

  const stationAvgs: StationAvg[] = []

  for (const step of steps) {
    const avg = avgCycleTimeSec(step.cycle_measurements)
    if (avg === null) continue
    stationAvgs.push({ stationName: step.station_name, avgCt: avg })
  }

  if (stationAvgs.length === 0) return null

  // Sort descending by avgCt; highest is worst
  stationAvgs.sort((a, b) => b.avgCt - a.avgCt)
  const worst = stationAvgs[0]

  const taktRatio =
    customerTaktTimeSec != null && customerTaktTimeSec > 0
      ? worst.avgCt / customerTaktTimeSec
      : null

  const isOverTakt =
    customerTaktTimeSec != null &&
    customerTaktTimeSec > 0 &&
    worst.avgCt > customerTaktTimeSec

  return {
    stationName: worst.stationName,
    avgCycleTimeSec: worst.avgCt,
    isOverTakt,
    taktRatio,
  }
}

/**
 * Compute takt achievement ratios for a project.
 * Returns null ratios when data is missing.
 *
 * @param avgCycleTimeSec - The measured avg CT across all non-outlier measurements
 * @param customerTaktTimeSec - From projects.customer_takt_time_sec
 * @param targetCycleTimeSec - From projects.target_cycle_time_sec
 */
export function calcTaktAchievement(
  avgCycleTimeSec: number | null,
  customerTaktTimeSec: number | null,
  targetCycleTimeSec: number | null,
): TaktAchievement {
  return {
    ctVsCustomerTakt:
      avgCycleTimeSec != null &&
      customerTaktTimeSec != null &&
      customerTaktTimeSec > 0
        ? avgCycleTimeSec / customerTaktTimeSec
        : null,
    ctVsTargetCt:
      avgCycleTimeSec != null &&
      targetCycleTimeSec != null &&
      targetCycleTimeSec > 0
        ? avgCycleTimeSec / targetCycleTimeSec
        : null,
  }
}

/**
 * Normalize lsc_measures status codes to a unified count object.
 * DB uses German codes: 'offen', 'in_arbeit', 'erledigt' (see bootstrap SQL / F-03).
 * Maps them to display-neutral English keys.
 */
export function calcLscMeasureStatus(
  measures: Array<{ status: string }>,
): LscMeasureStatus {
  const result: LscMeasureStatus = { open: 0, in_progress: 0, done: 0 }
  for (const m of measures) {
    switch (m.status) {
      case 'offen':
      case 'open':
        result.open++
        break
      case 'in_arbeit':
      case 'in_progress':
        result.in_progress++
        break
      case 'erledigt':
      case 'done':
        result.done++
        break
      // unknown statuses are silently ignored — see F-03 (status divergence)
    }
  }
  return result
}
