use bucketwarden_auth::{
    AccessKey, AuthError, AuthStore, CredentialRecord, CredentialScope, SessionCredential,
};

const NOW: u64 = 1_700_000_000;

#[test]
fn temporary_credentials_require_parent_identity_and_preserve_scope() {
    let mut store = AuthStore::new();
    store.create_tenant("tenant-a");
    store
        .create_service_account_in_tenant("backup", "tenant-a")
        .expect("principal");

    let session = SessionCredential::new(
        "backup",
        "AKIA-PARENT",
        "ASIA-CHILD",
        "child-secret",
        "child-token",
        NOW + 60,
    )
    .with_scope(CredentialScope::new(
        vec!["s3:GetObject".to_string()],
        vec!["tenant-a/backups/".to_string()],
    ));

    assert!(matches!(
        store.put_session(session.clone()),
        Err(AuthError::UnknownParentAccessKey(_))
    ));

    store
        .put_access_key(AccessKey::active("backup", "AKIA-PARENT", "parent-secret"))
        .expect("parent");
    store.put_session(session).expect("session");

    let resolved = store
        .resolve_credential("ASIA-CHILD", NOW)
        .expect("resolve");
    assert_eq!(resolved.principal_id, "backup");
    assert_eq!(resolved.tenant_id, "tenant-a");
    assert_eq!(
        resolved.credentials.session_token.as_deref(),
        Some("child-token")
    );
    let scope = resolved.scope.expect("scope");
    assert!(scope.permits("s3:GetObject", "tenant-a/backups/day-1.tar"));
    assert!(!scope.permits("s3:PutObject", "tenant-a/backups/day-1.tar"));
    assert!(!scope.permits("s3:GetObject", "tenant-b/backups/day-1.tar"));
}

#[test]
fn temporary_credentials_expire_revoke_and_track_last_used() {
    let mut store = AuthStore::new();
    store.create_service_account("worker");
    store
        .put_access_key(AccessKey::active("worker", "AKIA-PARENT", "parent-secret"))
        .expect("parent");
    store
        .put_session(SessionCredential::new(
            "worker",
            "AKIA-PARENT",
            "ASIA-SESSION",
            "session-secret",
            "session-token",
            NOW + 10,
        ))
        .expect("session");

    store.mark_used("ASIA-SESSION", NOW + 1).expect("mark used");
    assert!(matches!(
        store.credential("ASIA-SESSION"),
        Some(CredentialRecord::Session(session))
            if session.last_used_epoch_seconds == Some(NOW + 1)
    ));

    assert!(matches!(
        store.resolve_credential("ASIA-SESSION", NOW + 11),
        Err(AuthError::ExpiredCredential(_))
    ));

    store
        .revoke_credential("ASIA-SESSION", NOW + 2)
        .expect("revoke");
    assert!(matches!(
        store.resolve_credential("ASIA-SESSION", NOW + 2),
        Err(AuthError::RevokedCredential(_))
    ));
}

#[test]
fn temporary_credential_support_report_exposes_contract_controls_and_caveats() {
    let report = AuthStore::new().temporary_credential_support_report();

    assert!(report.native_support_state.contains(&"parent-identity"));
    assert!(report.native_support_state.contains(&"scoped-credentials"));
    assert!(report.native_support_state.contains(&"expiration"));
    assert!(report.native_support_state.contains(&"revocation"));
    assert!(report.native_support_state.contains(&"policy-inheritance"));
    assert!(report
        .security_governance_impact
        .contains(&"parent access key must resolve before session creation"));
    assert!(report.failure_modes.contains(&"UnknownParentAccessKey"));
    assert!(report.failure_modes.contains(&"ExpiredCredential"));
    assert!(report.failure_modes.contains(&"RevokedCredential"));
    assert!(report
        .validation_test_coverage
        .contains(&"crates/bucketwarden-auth/tests/temporary_credential_support_contract.rs"));
    assert!(report
        .product_specific_caveats
        .iter()
        .any(|caveat| caveat.contains("cannot expand")));
}
