use bucketwarden_auth::{AccessKey, AuthError, AuthStore, OperatorAction, OperatorRole};

#[test]
fn tenant_scoped_credentials_resolve_principal_and_tenant_identity() {
    let mut store = AuthStore::new();
    store.create_tenant("tenant-a");
    store.create_tenant("tenant-b");
    store
        .create_local_user_in_tenant("alice", "tenant-a")
        .expect("alice");
    store
        .create_service_account_in_tenant("backup", "tenant-b")
        .expect("backup");
    store
        .put_access_key(AccessKey::active("alice", "AKIAALICE", "alice-secret"))
        .expect("alice key");
    store
        .put_access_key(AccessKey::active("backup", "AKIABACKUP", "backup-secret"))
        .expect("backup key");

    let alice = store.resolve_credential("AKIAALICE", 10).expect("alice");
    let backup = store.resolve_credential("AKIABACKUP", 10).expect("backup");

    assert_eq!(alice.principal_id, "alice");
    assert_eq!(alice.tenant_id, "tenant-a");
    assert_eq!(backup.principal_id, "backup");
    assert_eq!(backup.tenant_id, "tenant-b");
    assert_eq!(store.credential_count_for_tenant("tenant-a"), 1);
    assert_eq!(store.credential_count_for_tenant("tenant-b"), 1);
}

#[test]
fn disabled_revoked_and_expired_credentials_fail_closed() {
    let mut store = AuthStore::new();
    store.create_local_user("alice");
    store
        .put_access_key(AccessKey::active("alice", "AKIAACTIVE", "secret"))
        .expect("active key");
    store
        .put_access_key(AccessKey::active("alice", "AKIAEXPIRED", "secret").with_expiry(9))
        .expect("expired key");
    store
        .put_access_key(AccessKey::active("alice", "AKIADISABLED", "secret"))
        .expect("disabled key");
    store
        .put_access_key(AccessKey::active("alice", "AKIAREVOKED", "secret"))
        .expect("revoked key");

    store.disable_access_key("AKIADISABLED").expect("disable");
    store.revoke_credential("AKIAREVOKED", 10).expect("revoke");

    assert!(store.resolve_credential("AKIAACTIVE", 10).is_ok());
    assert_eq!(
        store.resolve_credential("AKIAEXPIRED", 10),
        Err(AuthError::ExpiredCredential("AKIAEXPIRED".to_string()))
    );
    assert_eq!(
        store.resolve_credential("AKIADISABLED", 10),
        Err(AuthError::DisabledAccessKey("AKIADISABLED".to_string()))
    );
    assert!(matches!(
        store.resolve_credential("AKIAREVOKED", 10),
        Err(AuthError::DisabledAccessKey(_)) | Err(AuthError::RevokedCredential(_))
    ));
}

#[test]
fn operator_roles_are_action_and_scope_bounded() {
    let mut store = AuthStore::new();
    store.create_tenant("tenant-a");
    store
        .create_local_user_in_tenant("ops", "tenant-a")
        .expect("ops");
    store
        .assign_operator_role("ops", OperatorRole::TenantAdmin, "tenant-a")
        .expect("tenant admin");
    store
        .assign_operator_role("ops", OperatorRole::Auditor, "tenant-a/bucket-a")
        .expect("auditor");

    assert!(store
        .operator_action_allowed("ops", OperatorAction::AdministerTenant, "tenant-a")
        .expect("tenant admin"));
    assert!(!store
        .operator_action_allowed("ops", OperatorAction::AdministerTenant, "tenant-b")
        .expect("other tenant"));
    assert!(store
        .operator_action_allowed("ops", OperatorAction::ReadAudit, "tenant-a/bucket-a")
        .expect("audit read"));
    assert!(!store
        .operator_action_allowed("ops", OperatorAction::ManageCredentials, "tenant-a")
        .expect("credentials denied"));
}
