///|
/// An in-memory registry for safely switching between named configurations.
/// Applications can load providers from JSON or a custom source, audit them,
/// and activate a known-good snapshot without changing evaluator call sites.
pub(all) struct ProviderSnapshot {
  name : String
  version : String
  provider : Provider
  fingerprint : String
  audited : Bool
} derive(Debug)

///|
pub fn snapshot(
  name : String,
  version : String,
  provider : Provider,
) -> ProviderSnapshot {
  {
    name,
    version,
    provider,
    fingerprint: provider.fingerprint(),
    audited: provider.is_safe(),
  }
}

///|
pub(all) struct ProviderRegistry {
  snapshots : Map[String, ProviderSnapshot]
  active_name : String?
} derive(Debug)

///|
pub fn new_registry() -> ProviderRegistry {
  { snapshots: Map([]), active_name: None }
}

///|
pub fn ProviderRegistry::register(
  self : ProviderRegistry,
  item : ProviderSnapshot,
) -> ProviderRegistry {
  let next = self.snapshots.copy()
  next[item.name] = item
  { ..self, snapshots: next }
}

///|
pub fn ProviderRegistry::contains(
  self : ProviderRegistry,
  name : String,
) -> Bool {
  self.snapshots.contains(name)
}

///|
pub fn ProviderRegistry::get(
  self : ProviderRegistry,
  name : String,
) -> ProviderSnapshot? {
  self.snapshots.get(name)
}

///|
pub fn ProviderRegistry::activate(
  self : ProviderRegistry,
  name : String,
) -> ProviderRegistry? {
  match self.snapshots.get(name) {
    Some(item) if item.audited => Some({ ..self, active_name: Some(name) })
    _ => None
  }
}

///|
pub fn ProviderRegistry::active(self : ProviderRegistry) -> ProviderSnapshot? {
  match self.active_name {
    Some(name) => self.snapshots.get(name)
    None => None
  }
}

///|
pub fn ProviderRegistry::active_provider(self : ProviderRegistry) -> Provider? {
  self.active().map(item => item.provider)
}

///|
pub fn ProviderRegistry::names(self : ProviderRegistry) -> Array[String] {
  let names = []
  for name in self.snapshots.keys() {
    names.push(name)
  }
  names.sort()
  names
}

///|
pub fn ProviderRegistry::remove(
  self : ProviderRegistry,
  name : String,
) -> ProviderRegistry {
  let next = self.snapshots.copy()
  next.remove(name)
  let active_name = match self.active_name {
    Some(active) if active == name => None
    other => other
  }
  { snapshots: next, active_name }
}

///|
pub fn ProviderRegistry::summary(self : ProviderRegistry) -> String {
  let active = match self.active_name {
    Some(name) => name
    None => "none"
  }
  "snapshots=" + self.snapshots.length().to_string() + ", active=" + active
}