use crate::error::AppError;
use crate::types::{DataScope, TenantContext};

/// Check whether the `TenantContext` includes the specified permission code.
///
/// Returns `Ok(())` if the permission is present, or `AppError::Forbidden` otherwise.
pub fn require_permission(ctx: &TenantContext, permission: &str) -> Result<(), AppError> {
    if ctx.permissions.iter().any(|p| p == permission) {
        Ok(())
    } else {
        Err(AppError::Forbidden("权限不足".to_string()))
    }
}

/// Check whether the `TenantContext` includes at least one of the specified permission codes.
///
/// Useful when multiple permissions can grant access to the same resource.
pub fn require_any_permission(ctx: &TenantContext, permissions: &[&str]) -> Result<(), AppError> {
    let has_any = permissions
        .iter()
        .any(|p| ctx.permissions.iter().any(|up| up == *p));

    if has_any {
        Ok(())
    } else {
        Err(AppError::Forbidden("权限不足".to_string()))
    }
}

/// Check whether the `TenantContext` includes the specified role code.
///
/// Returns `Ok(())` if the role is present, or `AppError::Forbidden` otherwise.
pub fn require_role(ctx: &TenantContext, role: &str) -> Result<(), AppError> {
    if ctx.roles.iter().any(|r| r == role) {
        Ok(())
    } else {
        Err(AppError::Forbidden("权限不足".to_string()))
    }
}

/// 获取指定权限的数据范围。默认 All（向后兼容）。
///
/// Service 层根据返回值追加对应的查询过滤条件。
pub fn get_data_scope(ctx: &TenantContext, permission: &str) -> DataScope {
    ctx.permission_data_scopes
        .get(permission)
        .cloned()
        .unwrap_or(DataScope::All)
}

#[cfg(test)]
mod tests {
    use super::*;
    use uuid::Uuid;

    fn test_ctx(roles: Vec<&str>, permissions: Vec<&str>) -> TenantContext {
        TenantContext {
            tenant_id: Uuid::now_v7(),
            user_id: Uuid::now_v7(),
            roles: roles.into_iter().map(String::from).collect(),
            permissions: permissions.into_iter().map(String::from).collect(),
            department_ids: vec![],
            permission_data_scopes: std::collections::HashMap::new(),
        }
    }

    #[test]
    fn require_permission_succeeds_when_present() {
        let ctx = test_ctx(vec![], vec!["user.read", "user.write"]);
        assert!(require_permission(&ctx, "user.read").is_ok());
    }

    #[test]
    fn require_permission_fails_when_missing() {
        let ctx = test_ctx(vec![], vec!["user.read"]);
        assert!(require_permission(&ctx, "user.delete").is_err());
    }

    #[test]
    fn require_any_permission_succeeds_with_match() {
        let ctx = test_ctx(vec![], vec!["user.read"]);
        assert!(require_any_permission(&ctx, &["user.delete", "user.read"]).is_ok());
    }

    #[test]
    fn require_any_permission_fails_with_no_match() {
        let ctx = test_ctx(vec![], vec!["user.read"]);
        assert!(require_any_permission(&ctx, &["user.delete", "user.admin"]).is_err());
    }

    #[test]
    fn require_role_succeeds_when_present() {
        let ctx = test_ctx(vec!["admin", "user"], vec![]);
        assert!(require_role(&ctx, "admin").is_ok());
    }

    #[test]
    fn require_role_fails_when_missing() {
        let ctx = test_ctx(vec!["user"], vec![]);
        assert!(require_role(&ctx, "admin").is_err());
    }
}
