use bucketwarden_auth::{
    sign_hs256_jwt, AssumeRoleWithWebIdentityRequest, AuthStore, CredentialRecord, CredentialScope,
    IdentityProvider, JwtClaims, OidcProvider,
};

const NOW: u64 = 1_700_000_000;

#[test]
fn assume_role_with_web_identity_creates_expiring_scoped_session() {
    let mut store = AuthStore::new();
    store.upsert_identity_provider(IdentityProvider::Oidc(provider()));

    let session = store
        .assume_role_with_web_identity(
            AssumeRoleWithWebIdentityRequest::new(
                "oidc-main",
                token("alice", NOW + 60),
                30,
                CredentialScope::new(
                    vec!["s3:GetObject".to_string()],
                    vec!["photos/public/".to_string()],
                ),
                "ASIAP2",
                "session-secret",
                "session-token",
            ),
            NOW,
        )
        .expect("assume role");

    assert_eq!(session.principal_id, "oidc:alice");
    assert_eq!(session.expires_at_epoch_seconds, NOW + 30);
    assert!(matches!(
        store.credential("ASIAP2"),
        Some(CredentialRecord::Session(session))
            if session.scope.as_ref().is_some_and(|scope| {
                scope.permits("s3:GetObject", "photos/public/cat.jpg")
                    && !scope.permits("s3:PutObject", "photos/public/cat.jpg")
                    && !scope.permits("s3:GetObject", "photos/private/cat.jpg")
            })
    ));
}

#[test]
fn scoped_session_resolution_preserves_parent_identity_and_scope() {
    let mut store = AuthStore::new();
    store.upsert_identity_provider(IdentityProvider::Oidc(provider()));
    store
        .assume_role_with_web_identity(
            AssumeRoleWithWebIdentityRequest::new(
                "oidc-main",
                token("alice", NOW + 60),
                30,
                CredentialScope::new(
                    vec!["s3:GetObject".to_string()],
                    vec!["photos/".to_string()],
                ),
                "ASIAP2",
                "session-secret",
                "session-token",
            ),
            NOW,
        )
        .expect("assume role");

    let resolved = store
        .resolve_credential("ASIAP2", NOW + 29)
        .expect("resolve");

    assert_eq!(resolved.principal_id, "oidc:alice");
    assert_eq!(
        resolved.credentials.session_token.as_deref(),
        Some("session-token")
    );
    assert!(resolved
        .scope
        .as_ref()
        .is_some_and(|scope| scope.permits("s3:GetObject", "photos/a.jpg")));
}

fn provider() -> OidcProvider {
    OidcProvider::hs256(
        "oidc-main",
        "https://issuer.example",
        "bucketwarden",
        "key-1",
        "shared-secret",
        "oidc:",
    )
}

fn token(subject: &str, exp: u64) -> String {
    sign_hs256_jwt(
        Some("key-1"),
        &JwtClaims {
            iss: "https://issuer.example".to_string(),
            sub: subject.to_string(),
            aud: vec!["bucketwarden".to_string()],
            exp: Some(exp),
            iat: Some(NOW),
        },
        b"shared-secret",
    )
    .expect("token")
}
