///|
/// Analysis methods available to a repeatable engineering scenario.
pub enum ScenarioMethod {
  WorstCase
  RSS
  MonteCarlo
  AcceptanceSimulation
} derive(Debug, Eq)

///|
/// Select worst-case interval analysis.
pub fn worst_case_method() -> ScenarioMethod {
  WorstCase
}

///|
/// Select root-sum-square analysis.
pub fn rss_method() -> ScenarioMethod {
  RSS
}

///|
/// Select seeded Monte Carlo analysis.
pub fn monte_carlo_method() -> ScenarioMethod {
  MonteCarlo
}

///|
/// Select distribution-aware acceptance-window simulation.
pub fn acceptance_simulation_method() -> ScenarioMethod {
  AcceptanceSimulation
}

///|
/// Inputs for a named, reproducible analysis scenario.
pub struct ScenarioSpec {
  name : String
  chain : Chain
  analysis_method : ScenarioMethod
  samples : Int
  window : AcceptanceWindow
  policy : SamplingPolicy
} derive(Debug)

///|
/// Construct a scenario with a default seeded uniform sampling policy.
pub fn ScenarioSpec::new(
  name : String,
  chain : Chain,
  analysis_method : ScenarioMethod,
  samples : Int,
  window : AcceptanceWindow,
) -> ScenarioSpec {
  if samples <= 0 {
    abort("scenario sample count must be positive")
  }
  {
    name,
    chain,
    analysis_method,
    samples,
    window,
    policy: SamplingPolicy::new(seed=1U),
  }
}

///|
/// Replace the default policy while preserving the other scenario inputs.
pub fn ScenarioSpec::with_policy(
  self : ScenarioSpec,
  policy : SamplingPolicy,
) -> ScenarioSpec {
  { ..self, policy, }
}

///|
/// A normalized result independent of the selected analysis method.
pub struct ScenarioResult {
  name : String
  analysis_method : ScenarioMethod
  nominal : Double
  lower : Double
  upper : Double
  mean : Double
  standard_deviation : Double
  yield_rate : Double
  sensitivity : Array[(String, Double)]
} derive(Debug, Eq)

///|
fn scenario_from_analysis(
  spec : ScenarioSpec,
  result : AnalysisResult,
) -> ScenarioResult {
  {
    name: spec.name,
    analysis_method: spec.analysis_method,
    nominal: result.nominal,
    lower: result.lower,
    upper: result.upper,
    mean: result.mean,
    standard_deviation: result.standard_deviation,
    yield_rate: result.yield_rate,
    sensitivity: result.sensitivity,
  }
}

///|
fn scenario_from_simulation(
  spec : ScenarioSpec,
  result : SimulationSummary,
) -> ScenarioResult {
  {
    name: spec.name,
    analysis_method: spec.analysis_method,
    nominal: result.nominal,
    lower: result.minimum,
    upper: result.maximum,
    mean: result.mean,
    standard_deviation: result.standard_deviation,
    yield_rate: result.yield_rate,
    sensitivity: result.sensitivity,
  }
}

///|
/// Execute a scenario using its declared method and deterministic inputs.
pub fn run_scenario(spec : ScenarioSpec) -> ScenarioResult {
  match spec.analysis_method {
    WorstCase => scenario_from_analysis(spec, spec.chain.worst_case())
    RSS => scenario_from_analysis(spec, spec.chain.rss())
    MonteCarlo =>
      scenario_from_analysis(
        spec,
        spec.chain.monte_carlo(spec.samples, seed=spec.policy.seed),
      )
    AcceptanceSimulation =>
      scenario_from_simulation(
        spec,
        spec.chain.simulate(spec.samples, spec.policy, spec.window),
      )
  }
}

///|
/// Comparison of two normalized scenario outcomes.
pub struct ScenarioComparison {
  baseline_name : String
  candidate_name : String
  nominal_delta : Double
  standard_deviation_delta : Double
  yield_delta : Double
  interval_overlap : Bool
} derive(Debug, Eq)

///|
/// Compare nominal, variation, yield, and interval overlap between scenarios.
pub fn compare_scenarios(
  baseline : ScenarioResult,
  candidate : ScenarioResult,
) -> ScenarioComparison {
  {
    baseline_name: baseline.name,
    candidate_name: candidate.name,
    nominal_delta: candidate.nominal - baseline.nominal,
    standard_deviation_delta: candidate.standard_deviation -
    baseline.standard_deviation,
    yield_delta: candidate.yield_rate - baseline.yield_rate,
    interval_overlap: candidate.lower <= baseline.upper &&
    baseline.lower <= candidate.upper,
  }
}

///|
/// Return whether a scenario interval remains inside an acceptance window.
pub fn ScenarioResult::within_window(
  self : ScenarioResult,
  window : AcceptanceWindow,
) -> Bool {
  self.lower >= window.lower && self.upper <= window.upper
}

///|
/// Return the method-independent interval of a scenario result.
pub fn ScenarioResult::interval(self : ScenarioResult) -> Interval {
  Interval::new(self.lower, self.upper)
}