/**
 * Pure aggregation helpers for the Stoppuhr-Auswertung (stopwatch evaluation).
 *
 * Two derived views over the raw `cycle_measurements` of a project's stations
 * (= "Prozesse"):
 *
 *  1. {@link buildPerProcessSeries} — one series per station, one bar per
 *     measurement (in measurement order), for the per-process bar charts.
 *  2. {@link buildProcessAverages} — one row per station holding its average
 *     cycle time, for the cross-process comparison chart.
 *
 * Average cycle time is delegated to {@link avgCycleTimeSec} (the single source
 * of truth, KAR-704 O7) so outlier exclusion stays consistent across the app.
 * Invalid/missing values (null, NaN, ≤ 0) are dropped before any aggregation so
 * they never enter an average or appear as a meaningless bar.
 */

import { avgCycleTimeSec } from '@/lib/reporting/aggregations'

export interface EvalStep {
  id: string
  station_name: string
  /** lower = earlier; used to keep process order stable across charts */
  sort_order?: number | null
}

export interface EvalMeasurement {
  process_step_id: string
  cycle_number: number
  cycle_time_sec: number | null
  is_outlier?: boolean | null
}

/** A single bar in a per-process chart. */
export interface MeasurementBar {
  /** sequential label over the plottable measurements, e.g. "#1", "#2" */
  label: string
  /** the original DB cycle_number (may have gaps after deletes) */
  cycleNumber: number
  timeSec: number
  isOutlier: boolean
}

/** All bars for one process plus its summary stats. */
export interface ProcessSeries {
  stepId: string
  stationName: string
  bars: MeasurementBar[]
  /** mean over non-outlier, valid measurements; null when none */
  avgSec: number | null
  /** number of non-outlier, valid measurements (the ones feeding the average) */
  validCount: number
  /** total plotted bars (valid measurements incl. outliers) */
  barCount: number
}

/** One row for the cross-process average comparison chart. */
export interface ProcessAverage {
  stepId: string
  stationName: string
  avgSec: number
  /** non-outlier measurements used for the average */
  count: number
}

/** A plottable cycle time: a finite, positive number. */
function isPlottable(t: number | null | undefined): t is number {
  return typeof t === 'number' && Number.isFinite(t) && t > 0
}

/** Stations in display order (sort_order asc, then name), order-stable. */
function orderedSteps<T extends EvalStep>(steps: readonly T[]): T[] {
  return [...steps].sort((a, b) => {
    const ao = a.sort_order ?? 0
    const bo = b.sort_order ?? 0
    if (ao !== bo) return ao - bo
    return a.station_name.localeCompare(b.station_name)
  })
}

/** Measurements of one step, valid + ordered by cycle_number (order preserved). */
function orderedValidMeasurements(
  measurements: readonly EvalMeasurement[],
  stepId: string,
): EvalMeasurement[] {
  return measurements
    .filter((m) => m.process_step_id === stepId && isPlottable(m.cycle_time_sec))
    .sort((a, b) => a.cycle_number - b.cycle_number)
}

/**
 * One {@link ProcessSeries} per station — every (valid) measurement becomes a
 * bar, labelled #1..#n in measurement order. Stations with zero valid
 * measurements are still returned (empty bars) so the UI can show an explicit
 * "noch keine Messungen" state.
 */
export function buildPerProcessSeries(
  steps: readonly EvalStep[],
  measurements: readonly EvalMeasurement[],
): ProcessSeries[] {
  return orderedSteps(steps).map((step) => {
    const valid = orderedValidMeasurements(measurements, step.id)
    const bars: MeasurementBar[] = valid.map((m, i) => ({
      label: `#${i + 1}`,
      cycleNumber: m.cycle_number,
      timeSec: m.cycle_time_sec as number,
      isOutlier: Boolean(m.is_outlier),
    }))
    const validForAvg = valid.filter((m) => !m.is_outlier)
    return {
      stepId: step.id,
      stationName: step.station_name,
      bars,
      avgSec: avgCycleTimeSec(
        valid.map((m) => ({ cycle_time_sec: m.cycle_time_sec as number, is_outlier: m.is_outlier })),
      ),
      validCount: validForAvg.length,
      barCount: bars.length,
    }
  })
}

/**
 * One {@link ProcessAverage} per station that has at least one non-outlier,
 * valid measurement. Stations without a computable average are omitted so the
 * comparison chart only shows bars it can actually draw.
 */
export function buildProcessAverages(
  steps: readonly EvalStep[],
  measurements: readonly EvalMeasurement[],
): ProcessAverage[] {
  const rows: ProcessAverage[] = []
  for (const step of orderedSteps(steps)) {
    const valid = orderedValidMeasurements(measurements, step.id).map((m) => ({
      cycle_time_sec: m.cycle_time_sec as number,
      is_outlier: m.is_outlier,
    }))
    const avg = avgCycleTimeSec(valid)
    if (avg == null) continue
    rows.push({
      stepId: step.id,
      stationName: step.station_name,
      avgSec: avg,
      count: valid.filter((m) => !m.is_outlier).length,
    })
  }
  return rows
}

/** Uniform second formatting for axes/tooltips, e.g. 12.34 → "12,34 s". */
export function formatSeconds(sec: number, fractionDigits = 2): string {
  return `${sec.toLocaleString('de-DE', {
    minimumFractionDigits: fractionDigits,
    maximumFractionDigits: fractionDigits,
  })} s`
}
