//! Environment → [`HostConfig`]. Client-visible bind and the Python upstream are
//! REQUIRED with no default (host-family discipline: a defaulted port could
//! collide with the live oracle). `core_url` defaults to Bridge's convention.

use std::env;

#[derive(Debug, Clone)]
pub struct HostConfig {
    /// Where this host listens (the address FIFA reaches for UTAS). Required.
    pub listen_addr: String,
    /// Base URL of the Python UTAS oracle for fallback, e.g.
    /// `http://127.0.0.1:8199`. Required — must NOT be this host's own address.
    pub python_upstream: String,
    /// OpenFUT Core base URL. Default `http://127.0.0.1:8080` (Bridge convention).
    pub core_url: String,
    /// Directory holding `leagues.json`/`nations.json`/`teams.json`.
    pub tables_dir: String,
    /// FIFA 17 card-definition **identity catalog** (card id → FIFA asset id).
    /// Required production identity source: a `/club` item's `resourceId` comes
    /// from here. Startup fails if it cannot be loaded — never a placeholder.
    pub catalog_path: String,
    /// Persistent external-identity **store** file (owned-instance → stable wire
    /// id). Required: the wire `id` of every owned item is allocated/resolved
    /// here so it survives restart and reverses exactly.
    pub identity_store_path: String,
    /// The launcher-selected FIFA persona id, injected via `OPENFUT_PERSONA_ID`.
    /// Required, non-zero: it stamps `personaId` on the Core-backed
    /// `GET /squad/active`, and must match the persona LSX/Blaze/POW/UTAS use.
    pub persona_id: i64,
}

#[derive(Debug)]
pub struct ConfigError(pub String);

impl std::fmt::Display for ConfigError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}
impl std::error::Error for ConfigError {}

fn required(key: &str) -> Result<String, ConfigError> {
    match env::var(key) {
        Ok(v) if !v.is_empty() => Ok(v),
        _ => Err(ConfigError(format!("{key} is required (no default)"))),
    }
}

/// Parse a required, non-zero i64 env var. A zero identity id is invalid (the
/// client rejects a zero persona), so unset/empty/non-integer/zero is a hard error.
fn required_i64_nonzero(key: &str) -> Result<i64, ConfigError> {
    let raw = required(key)?;
    let val: i64 = raw
        .parse()
        .map_err(|_| ConfigError(format!("{key} must be an integer, got {raw:?}")))?;
    if val == 0 {
        return Err(ConfigError(format!("{key} must be non-zero")));
    }
    Ok(val)
}

impl HostConfig {
    pub fn from_env() -> Result<Self, ConfigError> {
        Ok(HostConfig {
            listen_addr: required("OPENFUT_UTAS_HOST_ADDR")?,
            python_upstream: required("OPENFUT_UTAS_PYTHON_URL")?,
            core_url: env::var("OPENFUT_CORE_URL")
                .unwrap_or_else(|_| "http://127.0.0.1:8080".into()),
            tables_dir: env::var("OPENFUT_FIFA17_TABLES_DIR")
                .unwrap_or_else(|_| "fifa17-recon/data/tables".into()),
            catalog_path: required("OPENFUT_FIFA17_CATALOG")?,
            identity_store_path: required("OPENFUT_IDENTITY_STORE")?,
            persona_id: required_i64_nonzero("OPENFUT_PERSONA_ID")?,
        })
    }
}
