///|
/// credential_store.mbt — persistent credential storage abstraction.

///|
/// Stores OAuth credentials keyed by provider_id. Hosts implement this
/// (file-based, keychain, etc.); providers are agnostic to the storage.
pub(open) trait CredentialStore {
  /// Read the stored credential for a provider. None if not stored or invalid.
  async fn read(Self, provider_id : String) -> Credential? raise OAuthError

  /// Persist a credential for a provider. Overwrites if exists.
  async fn write(Self, provider_id : String, credential : Credential) -> Unit raise OAuthError

  /// Delete the stored credential for a provider (logout).
  async fn delete(Self, provider_id : String) -> Unit raise OAuthError
}

///|
/// Canonical provider credential storage.  Unlike the legacy OAuth-only and
/// API-key-only stores below, this contract has one tagged record per provider
/// and therefore preserves the user's explicit authentication-method choice
/// across process restarts and host recomposition.
pub(open) trait ProviderCredentialStore {
  /// Read the active tagged credential for a provider. Missing is unconfigured.
  async fn read(Self, provider_id : String) -> ProviderCredential? raise OAuthError

  /// Atomically replace the active tagged credential for a provider.
  async fn write(Self, provider_id : String, credential : ProviderCredential) -> Unit raise OAuthError

  /// Delete the active tagged credential for a provider.
  async fn delete(Self, provider_id : String) -> Unit raise OAuthError
}

///|
/// In-memory canonical store used by router and host conformance tests.
pub(all) struct InMemoryProviderCredentialStore {
  credentials : Map[String, ProviderCredential]
}

///|
pub fn InMemoryProviderCredentialStore::InMemoryProviderCredentialStore() -> InMemoryProviderCredentialStore {
  { credentials: Map([]), }
}

///|
pub impl ProviderCredentialStore for InMemoryProviderCredentialStore with fn read(
  self : InMemoryProviderCredentialStore,
  provider_id : String,
) -> ProviderCredential? {
  self.credentials.get(provider_id)
}

///|
pub extend InMemoryProviderCredentialStore with ProviderCredentialStore::{
  read,
  delete,
  write,
}

///|
pub impl ProviderCredentialStore for InMemoryProviderCredentialStore with fn write(
  self : InMemoryProviderCredentialStore,
  provider_id : String,
  credential : ProviderCredential,
) -> Unit {
  self.credentials[provider_id] = credential
}

///|
pub impl ProviderCredentialStore for InMemoryProviderCredentialStore with fn delete(
  self : InMemoryProviderCredentialStore,
  provider_id : String,
) -> Unit {
  self.credentials.remove(provider_id)
}

///|
/// In-memory credential store for tests/dev. Not persistent.
pub(all) struct InMemoryCredentialStore {
  credentials : Map[String, Credential]
}

///|
pub fn InMemoryCredentialStore::InMemoryCredentialStore() -> InMemoryCredentialStore {
  { credentials: Map([]), }
}

///|
pub impl CredentialStore for InMemoryCredentialStore with fn read(
  self : InMemoryCredentialStore,
  provider_id : String,
) -> Credential? {
  match self.credentials.get(provider_id) {
    Some(c) => Some(c)
    None => None
  }
}

///|
pub extend InMemoryCredentialStore with CredentialStore::{read, delete, write}

///|
pub impl CredentialStore for InMemoryCredentialStore with fn write(
  self : InMemoryCredentialStore,
  provider_id : String,
  credential : Credential,
) -> Unit {
  self.credentials[provider_id] = credential
}

///|
pub impl CredentialStore for InMemoryCredentialStore with fn delete(
  self : InMemoryCredentialStore,
  provider_id : String,
) -> Unit {
  self.credentials.remove(provider_id)
}

///|
/// Stores provider-neutral API-key credentials. Hosts may back this with the
/// same secure store as OAuth credentials, but the two contracts remain
/// separate so providers never receive an untyped secret map.
pub(open) trait ApiKeyStore {
  async fn read(Self, provider_id : String) -> ApiKeyCredential? raise OAuthError
  async fn write(Self, provider_id : String, credential : ApiKeyCredential) -> Unit raise OAuthError
  async fn delete(Self, provider_id : String) -> Unit raise OAuthError
}

///|
/// In-memory API-key store for deterministic extension tests.
pub(all) struct InMemoryApiKeyStore {
  credentials : Map[String, ApiKeyCredential]
}

///|
pub fn InMemoryApiKeyStore::InMemoryApiKeyStore() -> InMemoryApiKeyStore {
  { credentials: Map([]), }
}

///|
pub impl ApiKeyStore for InMemoryApiKeyStore with fn read(
  self : InMemoryApiKeyStore,
  provider_id : String,
) -> ApiKeyCredential? {
  self.credentials.get(provider_id)
}

///|
pub extend InMemoryApiKeyStore with ApiKeyStore::{read, delete, write}

///|
pub impl ApiKeyStore for InMemoryApiKeyStore with fn write(
  self : InMemoryApiKeyStore,
  provider_id : String,
  credential : ApiKeyCredential,
) -> Unit {
  self.credentials[provider_id] = credential
}

///|
pub impl ApiKeyStore for InMemoryApiKeyStore with fn delete(
  self : InMemoryApiKeyStore,
  provider_id : String,
) -> Unit {
  self.credentials.remove(provider_id)
}