use bucketwarden_auth::{
    AccessKey, AuthError, AuthStore, MtlsClientCertificate, PrincipalKind, DEFAULT_TENANT_ID,
};

#[test]
fn mtls_client_certificate_authenticates_typed_principal_and_tracks_use() {
    let mut store = AuthStore::new();
    store
        .upsert_principal(bucketwarden_auth::Principal::mtls_client("device-1"))
        .expect("principal");
    store
        .put_mtls_client_certificate(MtlsClientCertificate::active("device-1", "sha256:device-1"))
        .expect("certificate");

    let identity = store
        .authenticate_mtls_client("sha256:device-1", 42)
        .expect("mtls identity");
    let principal = store.principal("device-1").expect("principal");

    assert_eq!(identity.principal_id, "device-1");
    assert_eq!(identity.tenant_id, DEFAULT_TENANT_ID);
    assert_eq!(principal.kind, PrincipalKind::MtlsClient);
    assert_eq!(
        store
            .mtls_certificate("sha256:device-1")
            .and_then(|certificate| certificate.last_used_epoch_seconds),
        Some(42)
    );
}

#[test]
fn mtls_client_certificates_fail_closed_when_unknown_disabled_revoked_or_expired() {
    let mut store = AuthStore::new();
    store
        .upsert_principal(bucketwarden_auth::Principal::mtls_client("device-1"))
        .expect("principal");
    store
        .put_mtls_client_certificate(MtlsClientCertificate::active("device-1", "sha256:disabled"))
        .expect("disabled");
    store
        .put_mtls_client_certificate(MtlsClientCertificate::active("device-1", "sha256:revoked"))
        .expect("revoked");
    store
        .put_mtls_client_certificate(
            MtlsClientCertificate::active("device-1", "sha256:expired").with_expiry(9),
        )
        .expect("expired");

    store
        .disable_mtls_client_certificate("sha256:disabled")
        .expect("disable");
    store
        .revoke_mtls_client_certificate("sha256:revoked", 10)
        .expect("revoke");

    assert_eq!(
        store.authenticate_mtls_client("sha256:missing", 10),
        Err(AuthError::UnknownClientCertificate(
            "sha256:missing".to_string()
        ))
    );
    assert_eq!(
        store.authenticate_mtls_client("sha256:disabled", 10),
        Err(AuthError::DisabledClientCertificate(
            "sha256:disabled".to_string()
        ))
    );
    assert_eq!(
        store.authenticate_mtls_client("sha256:revoked", 10),
        Err(AuthError::DisabledClientCertificate(
            "sha256:revoked".to_string()
        ))
    );
    assert_eq!(
        store.authenticate_mtls_client("sha256:expired", 10),
        Err(AuthError::ExpiredCredential("sha256:expired".to_string()))
    );
}

#[test]
fn login_attempt_governance_counts_blocks_and_resets_principal_failures() {
    let mut store = AuthStore::new();
    store.create_local_user("alice");

    for attempt in 1..=5 {
        store.record_login_failure("alice", 100 + attempt, format!("bad-signature-{attempt}"));
    }

    let record = store.login_attempt_record("alice").expect("attempt record");
    assert_eq!(record.consecutive_failed_attempts, 5);
    assert_eq!(record.last_failed_epoch_seconds, Some(105));
    assert_eq!(
        record.last_failure_detail.as_deref(),
        Some("bad-signature-5")
    );
    assert_eq!(
        store.enforce_login_attempt_limit("alice", 5),
        Err(AuthError::TooManyLoginAttempts("alice".to_string()))
    );

    store.record_login_success("alice", 106);

    let record = store.login_attempt_record("alice").expect("reset record");
    assert_eq!(record.consecutive_failed_attempts, 0);
    assert_eq!(record.last_success_epoch_seconds, Some(106));
    assert_eq!(record.last_failure_detail, None);
    assert_eq!(store.enforce_login_attempt_limit("alice", 5), Ok(()));
}

#[test]
fn credential_support_report_exposes_methods_controls_failures_and_caveats() {
    let mut store = AuthStore::new();
    store.create_service_account("backup");
    store
        .put_access_key(AccessKey::active("backup", "AKIABACKUP", "secret"))
        .expect("service key");

    let report = store.credential_support_report();

    assert!(report.native_support_state.contains(&"access-key-secret"));
    assert!(report.native_support_state.contains(&"sigv4"));
    assert!(report
        .native_support_state
        .contains(&"temporary-credentials"));
    assert!(report.native_support_state.contains(&"session-tokens"));
    assert!(report
        .native_support_state
        .contains(&"mtls-client-certificates"));
    assert!(report.native_support_state.contains(&"jwt"));
    assert!(report.native_support_state.contains(&"service-accounts"));
    assert!(report
        .semantic_parity
        .contains("known enabled principal and tenant"));
    assert!(report
        .configuration_admin_surface
        .contains(&"AuthStore::put_mtls_client_certificate"));
    assert!(report
        .security_governance_impact
        .contains(&"mTLS fingerprint binding"));
    assert!(report
        .observability_evidence_fields
        .contains(&"certificate_fingerprint"));
    assert!(report.failure_modes.contains(&"UnknownClientCertificate"));
    assert!(report
        .validation_test_coverage
        .contains(&"crates/bucketwarden-auth/tests/credential_support_contract.rs"));
    assert!(report
        .product_specific_caveats
        .iter()
        .any(|caveat| caveat.contains("TLS handshake termination")));
}
