///|
/// Parameters for a deterministic earthquake scenario.
pub struct ScenarioConfig {
  origin_time : Double
  latitude : Double
  longitude : Double
  depth_km : Double
  magnitude : Double
  sample_rate : Double
  duration_seconds : Double
  p_frequency : Double
  noise_level : Double
  seed : Int
}

///|
/// A simulated station channel and its theoretical arrivals.
pub struct ScenarioStationResult {
  station : String
  trace : Trace
  p_arrival : Double
  s_arrival : Double
  distance_km : Double
  pga : Double
  snr_db : Double
}

///|
/// Complete deterministic scenario output.
pub struct ScenarioResult {
  config : ScenarioConfig
  stations : Array[ScenarioStationResult]
  event : NetworkEvent
  catalog_entry : CatalogEntry
}

///|
/// Return a realistic small-network scenario configuration.
pub fn default_scenario_config() -> ScenarioConfig {
  {
    origin_time: 0.0,
    latitude: 30.0,
    longitude: 120.0,
    depth_km: 8.0,
    magnitude: 3.2,
    sample_rate: 100.0,
    duration_seconds: 60.0,
    p_frequency: 8.0,
    noise_level: 0.01,
    seed: 20260819,
  }
}

///|
/// Convert magnitude to a deterministic displacement-like amplitude.
pub fn scenario_amplitude(magnitude : Double, distance_km : Double) -> Double {
  let geometric = @math.pow(1.0 + distance_km, 0.75)
  @math.pow(10.0, magnitude * 0.5 - 2.0) / geometric
}

///|
/// Generate one station waveform from a source scenario.
pub fn simulate_scenario_station(
  config : ScenarioConfig,
  station : StationPosition,
  station_index : Int,
) -> ScenarioStationResult {
  let source = StationPosition::new(
    "source",
    config.latitude,
    config.longitude,
    0.0,
  )
  let distance = surface_distance_km(source, station)
  let path = (distance * distance + config.depth_km * config.depth_km).sqrt()
  let p_arrival = config.origin_time + path / 6.0
  let s_arrival = config.origin_time + path / 3.5
  let length = (config.duration_seconds * config.sample_rate).to_int().max(1)
  let onset = p_arrival - config.origin_time
  let amplitude = scenario_amplitude(config.magnitude, distance)
  let signal = synthetic_event_waveform(
    length,
    config.sample_rate,
    config.p_frequency,
    onset,
    amplitude,
    config.noise_level,
    config.seed + station_index * 31,
  )
  let trace = Trace::new(
    station.station,
    "HHZ",
    config.sample_rate,
    config.origin_time,
    signal,
  )
  let baseline = slice_samples(
    trace.data,
    0,
    (onset * config.sample_rate).to_int().max(1).min(trace.npts()),
  )
  let snr = arrays_snr_db(baseline, trace.data)
  {
    station: station.station,
    trace,
    p_arrival,
    s_arrival,
    distance_km: distance,
    pga: absolute_peak(trace.data),
    snr_db: snr,
  }
}

///|
/// Simulate an event across a station array.
pub fn simulate_scenario(
  config : ScenarioConfig,
  stations : Array[StationPosition],
) -> ScenarioResult {
  let results = Array::new(capacity=stations.length())
  let network_stations = Array::new()
  let mut total_peak = 0.0
  let mut total_quality = 0.0
  let mut earliest = config.origin_time + config.duration_seconds
  for index = 0; index < stations.length(); index = index + 1 {
    let simulated = simulate_scenario_station(config, stations[index], index)
    results.push(simulated)
    network_stations.push(simulated.station)
    total_peak += simulated.pga
    total_quality += (simulated.snr_db + 10.0).max(0.0).min(40.0) / 40.0
    earliest = earliest.min(simulated.p_arrival)
  }
  let count = results.length()
  let event = {
    origin_time: earliest,
    station_count: count,
    duration: config.duration_seconds,
    magnitude: @math.log10(1.0 + total_peak),
    stations: network_stations,
    confidence: if count == 0 {
      0.0
    } else {
      total_quality / count.to_double()
    },
  }
  let catalog_entry = {
    id: 0,
    origin_time: config.origin_time,
    latitude: config.latitude,
    longitude: config.longitude,
    depth_km: config.depth_km,
    magnitude: config.magnitude,
    station_count: count,
    confidence: event.confidence,
  }
  { config, stations: results, event, catalog_entry }
}

///|
/// Compute theoretical P/S arrival separation at one station.
pub fn scenario_phase_separation(result : ScenarioStationResult) -> Double {
  result.s_arrival - result.p_arrival
}

///|
/// Return a station list for a regular local array.
pub fn regular_scenario_stations(
  reference : StationPosition,
  count : Int,
  spacing_km : Double,
) -> Array[StationPosition] {
  let result = Array::new()
  for index = 0; index < count.max(0); index = index + 1 {
    let offset = index.to_double() - (count - 1).to_double() / 2.0
    result.push(
      StationPosition::new(
        "SC" + index.to_string(),
        reference.latitude + offset * spacing_km / 111.32,
        reference.longitude,
        reference.elevation_km,
      ),
    )
  }
  result
}

///|
/// Convert scenario station results to a quality report set.
pub fn scenario_quality_reports(
  result : ScenarioResult,
) -> Array[StationQualityReport] {
  result.stations.map(station => {
    station_quality_report(
      station.trace,
      station.trace.npts(),
      1.0e300,
      1.0e-12,
      station.trace.data,
    )
  })
}

///|
/// Format scenario headline information.
pub fn scenario_result_text(result : ScenarioResult) -> String {
  "origin=" +
  result.config.origin_time.to_string() +
  " magnitude=" +
  result.config.magnitude.to_string() +
  " stations=" +
  result.stations.length().to_string() +
  " event_confidence=" +
  result.event.confidence.to_string() +
  " depth=" +
  result.config.depth_km.to_string()
}