// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// Settings given twice with different values, as `Handle` receives them.
#derive(Eq, Debug)
pub(all) struct Clash[T] {
  /// The settings that disagreed, in the order the function takes them.
  names : Array[String]
  /// What the function started from.
  base : T
  /// The two laid together the way `wins` says.
  merged : T
}

///|
pub extend Clash with Eq::{equal, not_equal}

///|
pub extend Clash with Debug::{to_repr}

///|
/// What to do when a setting is given twice with different values.
///
/// The same value given twice is not a clash: there is only one answer, and
/// nothing is reported.
pub(all) enum OnClash[T] {
  /// Take what `wins` says and say nothing. What most libraries do.
  Ignore
  /// Take what `wins` says and pass one line about it to the function, a
  /// logger's `warn` for instance. The library itself prints nothing.
  Warn((String) -> Unit)
  /// Abort, for where two answers are a mistake in the program rather than a
  /// choice about configuration.
  Panic
  /// Pass the clash to the function and take what it returns.
  Handle((Clash[T]) -> T)
}

///|
/// What a function ends up with, given what it started from, the two laid
/// together, and the settings that disagreed. With no names there was no
/// clash, and `merged` comes back whatever `self` is.
///
/// `site` is the call and its two sources, for the warning and the abort:
/// `"jwt.verify: policy and arguments"`.
pub fn[T] OnClash::settle(
  self : OnClash[T],
  base : T,
  merged : T,
  names : Array[String],
  site : String,
) -> T {
  if names.is_empty() {
    return merged
  }
  match self {
    Ignore => merged
    Warn(warn) => {
      warn(message(site, names))
      merged
    }
    Panic => abort(message(site, names))
    Handle(decide) => decide({ names: names.copy(), base, merged, })
  }
}

///|
fn message(site : String, names : Array[String]) -> String {
  "\{site}: \{names.join(", ")} given twice with different values"
}