//! The FIFA 17 TLS profile, exercised through the shared listener.
//!
//! These assertions used to live inside this host's own `tls` module. That
//! module was split in two — mechanism into `openfut-tls`, FIFA 17's observed
//! suites into `openfut-adapter-fifa17::tls` — and neither half can prove the
//! interesting property alone: the generic crate does not know what FIFA
//! offers, and the adapter is dependency-free and cannot open a socket.
//!
//! This host is where the two are composed, so this is where the composition is
//! tested. Every case here was passing before the split and must still pass
//! after it; the refactor is only sound if the retail handshake is unchanged.
//!
//! Live confirmation, 2026-08-11 17:09: a retail client negotiated
//! `TLSv1.2 / AES256-GCM-SHA384` against this host, presenting
//! `sni=winter15.gosredirector.ea.com`, and reached the FUT hub.

use openfut_adapter_fifa17::tls as profile;
use openfut_tls::{self, ProtocolVersion, TlsConfig};

/// Reuse the proven Python redirector's material, so the TLS implementation is
/// the only variable in an A/B.
///
/// NOTE: this is the repo's copy, which is **not** the certificate the deployed
/// container serves. That difference is irrelevant here — these tests exercise
/// cipher and version negotiation, for which any valid RSA pair does — but it
/// is emphatically not irrelevant at runtime, where serving the wrong one costs
/// gates. A host takes its certificate from configuration, never from here.
fn oracle_cert() -> (String, String) {
    let base = concat!(env!("CARGO_MANIFEST_DIR"), "/../fifa17-recon/tools");
    (
        format!("{base}/redir_cert.pem"),
        format!("{base}/redir_key.pem"),
    )
}

fn cfg() -> TlsConfig {
    let (c, k) = oracle_cert();
    TlsConfig::new(
        c,
        k,
        profile::CIPHER_LIST,
        ProtocolVersion::parse(profile::MIN_VERSION).unwrap(),
        ProtocolVersion::parse(profile::MAX_VERSION).unwrap(),
    )
}

fn rehearse(client_ciphers: &str) -> Result<openfut_tls::Negotiated, openfut_tls::TlsError> {
    openfut_tls::self_test(&cfg(), client_ciphers, profile::CLIENT_SNI)
}

#[test]
fn acceptor_builds_with_the_oracle_certificate() {
    assert!(openfut_tls::build_acceptor(&cfg()).is_ok());
}

/// The decisive case: a client restricted to exactly the suites the retail
/// ClientHello offered must complete a handshake, and must land on the suite
/// the real client already negotiated against Python.
#[test]
fn a_client_offering_only_fifas_suites_completes_a_handshake() {
    let neg = rehearse(profile::OBSERVED_CLIENT_SUITES).expect("handshake");
    assert_eq!(neg.cipher, profile::EXPECTED_SUITE, "negotiated {neg:?}");
    assert_eq!(neg.version, "TLSv1.2", "negotiated {neg:?}");
}

/// Each enabled suite individually, so a client offering only one still connects.
#[test]
fn every_enabled_suite_can_be_negotiated_alone() {
    for suite in profile::CIPHER_LIST.split(':') {
        let neg =
            rehearse(suite).unwrap_or_else(|e| panic!("{suite} could not be negotiated: {e}"));
        assert_eq!(neg.cipher, suite);
    }
}

/// RC4/MD5 are offered by the client and deliberately refused, so a client
/// offering ONLY those must fail. This is what makes the refusal real rather
/// than aspirational.
#[test]
fn a_client_offering_only_rc4_is_refused() {
    let only_refused = profile::REFUSED_SUITES.join(":");
    let r = rehearse(&only_refused);
    assert!(r.is_err(), "RC4-only client should not connect: {r:?}");
}

/// A forward-secret-only client must also fail — proving we did not silently
/// inherit a modern profile that would exclude FIFA.
#[test]
fn a_forward_secret_only_client_is_not_what_we_serve() {
    let r = rehearse("ECDHE-RSA-AES256-GCM-SHA384");
    assert!(r.is_err(), "we should not be offering ECDHE; got {r:?}");
}

#[test]
fn security_level_is_not_lowered_by_default() {
    assert_eq!(
        cfg().security_level,
        None,
        "the default policy is tried first; lowering requires evidence"
    );
}

#[test]
fn protocol_window_matches_the_observed_client() {
    let c = cfg();
    assert_eq!(c.min_version, ProtocolVersion::Tls12);
    assert_eq!(c.max_version, ProtocolVersion::Tls12);
}

/// The refactor's own guard.
///
/// The values below are copied from the host as it was when gates 1-14 passed,
/// written out literally rather than referenced. If a later edit to the adapter
/// changes what this host serves, this fails and names the drift — which is the
/// only way a "pure refactor" can be shown to have been pure.
#[test]
fn the_composed_profile_still_matches_the_gate_proven_host() {
    let c = cfg();
    assert_eq!(
        c.cipher_list,
        "AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA"
    );
    assert_eq!(c.min_version.as_str(), "TLSv1.2");
    assert_eq!(c.max_version.as_str(), "TLSv1.2");
    assert_eq!(c.security_level, None);
    assert_eq!(profile::CLIENT_SNI, "winter15.gosredirector.ea.com");
}

/// A host must be able to report which certificate it serves.
///
/// Not decoration: the failure this guards against is invisible at startup.
/// Serving a certificate that differs from the rest of the stack produced no
/// error anywhere — the client cached the first one it saw and every later
/// service failed its handshake in silence.
#[test]
fn the_certificate_fingerprint_is_reportable() {
    let (cert, _) = oracle_cert();
    let fp = openfut_tls::certificate_fingerprint(&cert).expect("fingerprint");
    assert_eq!(fp.matches(':').count(), 31, "32 octets: {fp}");
}
