//! Per-connection Blaze session state.
//!
//! Note how little there is: a session key, the client's locale, the echoed
//! service name, an auth code and a logged-in flag. That is the whole of it.
//!
//! This is the point of the adapter boundary. Blaze is an auth/session/config
//! protocol — coins, packs, clubs, squads and the rest of the FUT domain never
//! appear on this wire, so there is nothing here tempting the adapter into
//! becoming a second backend. When UTAS is migrated that discipline will need
//! actively defending; here it comes for free.

/// State carried across RPCs on one Blaze connection.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Session {
    /// Minted once per connection. Must appear byte-identically in
    /// `LoginResponse.SESS.KEY`, the `UserAuthenticated` push, and
    /// `PostAuthResponse.TELE.SESS`.
    pub session_key: String,
    /// Whatever `LoginRequest.AUTH` carried; echoed back by `getAuthToken`.
    pub auth_code: String,
    /// Packed four-char locale, seeded from config and overwritten by the
    /// client's own preAuth `LANG`/`LOC`.
    pub account_locale: i64,
    /// Echoed back as `PreAuthResponse.INST`.
    pub service_name: String,
    pub logged_in: bool,
    pub login_time: i64,
}

/// The oracle's default service name when preAuth carries no `CDAT.SVCN`.
pub const DEFAULT_SERVICE_NAME: &str = "fifa-2017-pc";

impl Session {
    /// Start a session with an explicit key.
    ///
    /// The key is injected rather than generated internally so it can be made
    /// deterministic for differential tests — it appears verbatim in three
    /// different responses, so a self-generated one would make every login
    /// fixture unreproducible.
    pub fn new(session_key: impl Into<String>, account_locale: i64) -> Session {
        Session {
            session_key: session_key.into(),
            auth_code: String::new(),
            account_locale,
            service_name: DEFAULT_SERVICE_NAME.into(),
            logged_in: false,
            login_time: 0,
        }
    }

    /// The token `getAuthToken` returns.
    ///
    /// Before login there is no auth code, so the oracle synthesises one from
    /// the session key. Reproduced exactly, including the 16-character slice.
    pub fn auth_token(&self) -> String {
        if !self.auth_code.is_empty() {
            return self.auth_code.clone();
        }
        // Byte slicing is safe here in practice (session keys are ASCII), but
        // char_indices keeps it correct for any injected key.
        let cut = self
            .session_key
            .char_indices()
            .nth(16)
            .map(|(i, _)| i)
            .unwrap_or(self.session_key.len());
        format!("OPENFUT-{}", &self.session_key[..cut])
    }
}

/// Mint a Blaze-shaped session key: 16 hex, an underscore, then 44 alphanumerics.
///
/// The client never validates the format — one public emulator ships the
/// literal `"0"` — so this only has to be stable within a connection. Callers
/// supply the randomness so this crate needs no RNG dependency and stays
/// deterministic under test.
pub fn format_session_key(high_bits: u64, tail: &str) -> String {
    format!("{high_bits:016x}_{tail}")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn synthesises_a_token_before_login() {
        let s = Session::new("0123456789abcdef_TAIL", 0);
        assert_eq!(s.auth_token(), "OPENFUT-0123456789abcdef");
    }

    #[test]
    fn echoes_the_login_auth_code_afterwards() {
        let mut s = Session::new("0123456789abcdef_TAIL", 0);
        s.auth_code = "REAL-CODE".into();
        assert_eq!(s.auth_token(), "REAL-CODE");
    }

    #[test]
    fn short_session_keys_do_not_panic() {
        assert_eq!(Session::new("abc", 0).auth_token(), "OPENFUT-abc");
        assert_eq!(Session::new("", 0).auth_token(), "OPENFUT-");
    }

    #[test]
    fn session_key_has_the_blaze_shape() {
        let k = format_session_key(0x0123456789abcdef, &"x".repeat(44));
        assert_eq!(k.len(), 16 + 1 + 44);
        assert!(k.starts_with("0123456789abcdef_"));
    }
}
