//! Record-Level Permission Checker (ABAC Layer)
//!
//! Implements fine-grained record-level access control on top of RBAC.
//! Three-layer permission architecture:
//! - **Layer 0**: ProtectionLevel (highest priority) — controls record mutability
//! - **Layer 1**: RBAC (existing PermissionChecker) — role-based operation permissions
//! - **Layer 2**: ABAC (this module) — ownership, visibility, collaborator permissions
//!
//! Permission check priority within Layer 2:
//! Admin override > Owner rights > Collaborator permissions > Visibility rules

use serde::{Deserialize, Serialize};

use super::permission::AccessContext;
use super::types::{
    OwnershipMeta, ProtectionLevel, RecordPermission, Visibility, SYSTEM_OWNER_ID,
};

// ============================================================================
// Protection Check Result (Layer 0 internal type)
// ============================================================================

/// Result type for protection level checks
pub enum ProtectionCheckResult {
    /// Protection level allows the operation — continue to subsequent checks
    Continue,
    /// Protection level denies the operation — return immediately
    Denied { reason: String },
}

impl ProtectionCheckResult {
    /// Returns true if this result allows continuing
    fn is_continue(&self) -> bool {
        matches!(self, ProtectionCheckResult::Continue)
    }
}

// ============================================================================
// RecordActions (returned to frontend for UI control)
// ============================================================================

/// Available actions for a user on a specific record.
///
/// Passed to the frontend so it can show/hide edit/delete buttons accordingly.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecordActions {
    /// Whether the user can read this record
    pub can_read: bool,
    /// Whether the user can edit/update this record
    pub can_edit: bool,
    /// Whether the user can delete this record
    pub can_delete: bool,
    /// Whether the user can modify visibility and manage collaborators
    pub can_share: bool,
    /// Whether the user can change the protection level
    pub can_change_protection: bool,
    /// Whether the current user is the record owner
    pub is_owner: bool,
    /// Whether this is system-generated data
    pub is_system_data: bool,
    /// Current protection level of the record
    pub protection: ProtectionLevel,
}

// ============================================================================
// RecordPermissionChecker
// ============================================================================

/// Record-level permission checker (ABAC layer).
///
/// After RBAC passes, this checker further validates whether a user
/// can perform a specific operation on a particular record, based on:
/// - Protection level (Layer 0 — highest priority)
/// - Admin override
/// - Ownership (owner_id == caller)
/// - Collaborator permissions
/// - Visibility rules
pub struct RecordPermissionChecker;

impl RecordPermissionChecker {
    // ═══════════════════════════════════════════════════════
    //  Layer 0: Protection Level Checks (highest priority)
    // ═══════════════════════════════════════════════════════

    /// Check if protection level allows editing
    fn check_protection_for_edit(
        ctx: &AccessContext,
        ownership: &OwnershipMeta,
    ) -> ProtectionCheckResult {
        match &ownership.protection {
            ProtectionLevel::None => ProtectionCheckResult::Continue,
            ProtectionLevel::ReadOnly => ProtectionCheckResult::Denied {
                reason: "Record is ReadOnly protected — editing is not allowed".to_string(),
            },
            ProtectionLevel::Immutable => ProtectionCheckResult::Denied {
                reason: "Record is Immutable protected — editing and deletion are not allowed"
                    .to_string(),
            },
            ProtectionLevel::System => {
                if ctx.system_override {
                    ProtectionCheckResult::Continue
                } else {
                    ProtectionCheckResult::Denied {
                        reason: "Record is System protected — requires system_override".to_string(),
                    }
                }
            }
        }
    }

    /// Check if protection level allows deletion
    fn check_protection_for_delete(
        ctx: &AccessContext,
        ownership: &OwnershipMeta,
    ) -> ProtectionCheckResult {
        match &ownership.protection {
            ProtectionLevel::None => ProtectionCheckResult::Continue,
            ProtectionLevel::ReadOnly => ProtectionCheckResult::Continue, // ReadOnly allows deletion
            ProtectionLevel::Immutable => ProtectionCheckResult::Denied {
                reason:
                    "Record is Immutable protected — must downgrade protection before deleting"
                        .to_string(),
            },
            ProtectionLevel::System => {
                if ctx.system_override {
                    ProtectionCheckResult::Continue
                } else {
                    ProtectionCheckResult::Denied {
                        reason: "System protected data cannot be deleted (requires system_override)"
                            .to_string(),
                    }
                }
            }
        }
    }

    /// Check if a protection level change is allowed
    ///
    /// Rules:
    /// - System level can only be modified with system_override
    /// - Upgrading to System also requires system_override
    /// - Only Admin (or system_override) can modify protection levels
    pub fn check_protection_change(
        ctx: &AccessContext,
        current: &ProtectionLevel,
        target: &ProtectionLevel,
    ) -> ProtectionCheckResult {
        // System level can only be changed via system_override
        if *current == ProtectionLevel::System && !ctx.system_override {
            return ProtectionCheckResult::Denied {
                reason: "System protection level can only be modified via internal system operations"
                    .to_string(),
            };
        }

        // Upgrading to System also requires system_override
        if *target == ProtectionLevel::System && !ctx.system_override {
            return ProtectionCheckResult::Denied {
                reason: "Cannot set protection level to System via API".to_string(),
            };
        }

        // Only Admin or system_override can change protection levels
        if Self::is_admin(ctx) || ctx.system_override {
            ProtectionCheckResult::Continue
        } else {
            ProtectionCheckResult::Denied {
                reason: "Only administrators can modify data protection levels".to_string(),
            }
        }
    }

    // ═══════════════════════════════════════════════════════
    //  Layer 2: ABAC Checks (ownership / visibility / collaborator)
    // ═══════════════════════════════════════════════════════

    /// Check if the user can read this record
    ///
    /// Priority: Admin > Owner > Collaborator(Read) > Visibility rules
    pub fn can_read(ctx: &AccessContext, ownership: &OwnershipMeta) -> bool {
        // 1. Admin can read everything
        if Self::is_admin(ctx) {
            return true;
        }

        // 2. Owner can always read their own records
        if ownership.owner_id == ctx.caller {
            return true;
        }

        // 3. Collaborator with Read permission
        if Self::has_collaborator_permission(ctx, ownership, RecordPermission::Read) {
            return true;
        }

        // 4. Visibility rules
        match &ownership.visibility {
            Visibility::Public => true,
            Visibility::Private => false,
            Visibility::Shared { user_ids } => user_ids.contains(&ctx.caller),
            Visibility::RoleRestricted { roles } => ctx.roles.iter().any(|r| roles.contains(r)),
        }
    }

    /// Check if the user can edit this record (includes Layer 0 protection check)
    ///
    /// Priority: Layer 0 Protection > Admin > Owner > Collaborator(Write)
    pub fn can_edit(ctx: &AccessContext, ownership: &OwnershipMeta) -> bool {
        // Layer 0: Protection level check (highest priority)
        if let ProtectionCheckResult::Denied { .. } =
            Self::check_protection_for_edit(ctx, ownership)
        {
            return false;
        }

        // Layer 2: Ownership check (Admin > Owner > Collaborator)
        if Self::is_admin(ctx) {
            return true;
        }
        if ownership.owner_id == ctx.caller {
            return true;
        }
        Self::has_collaborator_permission(ctx, ownership, RecordPermission::Write)
    }

    /// Check if the user can delete this record (includes Layer 0 protection check)
    ///
    /// Deletion is the most restrictive — only Admin, Owner, or explicit
    /// Collaborator(Delete) can delete. Collaborators default to no delete access.
    pub fn can_delete(ctx: &AccessContext, ownership: &OwnershipMeta) -> bool {
        // Layer 0: Protection level check (highest priority)
        if let ProtectionCheckResult::Denied { .. } =
            Self::check_protection_for_delete(ctx, ownership)
        {
            return false;
        }

        // Layer 2: Ownership check
        if Self::is_admin(ctx) {
            return true;
        }
        if ownership.owner_id == ctx.caller {
            return true;
        }
        Self::has_collaborator_permission(ctx, ownership, RecordPermission::Delete)
    }

    /// Check if the user can modify record visibility and collaborators
    ///
    /// Only Admin and Owner can modify sharing settings.
    pub fn can_share(ctx: &AccessContext, ownership: &OwnershipMeta) -> bool {
        Self::is_admin(ctx) || ownership.owner_id == ctx.caller
    }

    /// Get all available actions for a user on a specific record.
    ///
    /// Used by REST API to include `actions` in query responses,
    /// enabling frontend UI to show/hide edit/delete/share buttons.
    pub fn get_available_actions(
        ctx: &AccessContext,
        ownership: &OwnershipMeta,
    ) -> RecordActions {
        RecordActions {
            can_read: Self::can_read(ctx, ownership),
            can_edit: Self::can_edit(ctx, ownership),
            can_delete: Self::can_delete(ctx, ownership),
            can_share: Self::can_share(ctx, ownership),
            can_change_protection: Self::check_protection_change(
                ctx,
                &ownership.protection,
                &ProtectionLevel::None,
            )
            .is_continue(),
            is_owner: ownership.owner_id == ctx.caller,
            is_system_data: ownership.owner_id == SYSTEM_OWNER_ID,
            protection: ownership.protection.clone(),
        }
    }

    // ═══════════════════════════════════════════════════════
    //  Helper methods
    // ═══════════════════════════════════════════════════════

    /// Check if the caller is an admin
    fn is_admin(ctx: &AccessContext) -> bool {
        ctx.roles
            .iter()
            .any(|r| r == "admin" || r == "federation_admin")
    }

    /// Check if the caller is a collaborator with the given permission
    fn has_collaborator_permission(
        ctx: &AccessContext,
        ownership: &OwnershipMeta,
        perm: RecordPermission,
    ) -> bool {
        ownership.collaborators.iter().any(|c| {
            c.user_id == ctx.caller && c.permissions.contains(&perm)
        })
    }
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use crate::federation::types::{Collaborator, Visibility};

    fn now_ms() -> i64 {
        use std::time::{SystemTime, UNIX_EPOCH};
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as i64
    }

    fn make_ownership(owner: &str) -> OwnershipMeta {
        OwnershipMeta::new_for_user(owner)
    }

    fn admin_ctx() -> AccessContext {
        AccessContext::new("admin_user", vec!["admin".to_string()])
    }

    fn user_ctx(user: &str) -> AccessContext {
        AccessContext::new(user, vec!["writer".to_string()])
    }

    fn reader_ctx(user: &str) -> AccessContext {
        AccessContext::new(user, vec!["reader".to_string()])
    }

    // ── Admin tests ──

    #[test]
    fn test_admin_has_all_permissions() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123");

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    // ── Owner tests ──

    #[test]
    fn test_owner_has_crud_permissions() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123");

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    // ── Non-owner tests ──

    #[test]
    fn test_non_owner_cannot_edit_or_delete() {
        let ctx = user_ctx("other_user");
        let ownership = make_ownership("user_123");

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership)); // Public
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    // ── Visibility tests ──

    #[test]
    fn test_private_visibility() {
        let ctx = user_ctx("other_user");
        let ownership = make_ownership("user_123").with_visibility(Visibility::Private);

        assert!(!RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_shared_visibility() {
        let ctx = user_ctx("friend_user");
        let ownership = make_ownership("user_123").with_visibility(Visibility::Shared {
            user_ids: vec!["friend_user".to_string()],
        });

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_shared_visibility_excludes_unlisted() {
        let ctx = user_ctx("stranger");
        let ownership = make_ownership("user_123").with_visibility(Visibility::Shared {
            user_ids: vec!["friend_user".to_string()],
        });

        assert!(!RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_role_restricted_visibility() {
        let ctx = AccessContext::new("user_a", vec!["editor".to_string()]);
        let ownership =
            make_ownership("user_123").with_visibility(Visibility::RoleRestricted {
                roles: vec!["editor".to_string(), "reviewer".to_string()],
            });

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_role_restricted_visibility_no_match() {
        let ctx = reader_ctx("user_a");
        let ownership =
            make_ownership("user_123").with_visibility(Visibility::RoleRestricted {
                roles: vec!["editor".to_string()],
            });

        assert!(!RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    // ── Collaborator tests ──

    #[test]
    fn test_collaborator_read_write() {
        let mut ownership = make_ownership("user_123").with_visibility(Visibility::Private);
        ownership.collaborators.push(Collaborator {
            user_id: "collab_user".to_string(),
            permissions: vec![RecordPermission::Read, RecordPermission::Write],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx = user_ctx("collab_user");
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership)); // No Delete perm
        assert!(!RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    #[test]
    fn test_collaborator_with_delete() {
        let mut ownership = make_ownership("user_123");
        ownership.collaborators.push(Collaborator {
            user_id: "collab_user".to_string(),
            permissions: vec![
                RecordPermission::Read,
                RecordPermission::Write,
                RecordPermission::Delete,
            ],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx = user_ctx("collab_user");
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    // ── Protection Level tests ──

    #[test]
    fn test_readonly_protection_blocks_edit() {
        let ctx = user_ctx("user_123"); // owner
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership)); // ReadOnly allows delete
    }

    #[test]
    fn test_readonly_protection_blocks_edit_for_admin() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);

        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership)); // Even admin can't edit
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership)); // But can delete
    }

    #[test]
    fn test_immutable_protection_blocks_edit_and_delete() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    #[test]
    fn test_system_protection_blocks_all_without_override() {
        let ctx = admin_ctx();
        let ownership =
            OwnershipMeta::new_system(ProtectionLevel::System);

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    #[test]
    fn test_system_protection_allows_with_override() {
        let ctx = admin_ctx().with_system_override();
        let ownership =
            OwnershipMeta::new_system(ProtectionLevel::System);

        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    // ── get_available_actions tests ──

    #[test]
    fn test_get_available_actions_owner() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123");
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);

        assert!(actions.can_read);
        assert!(actions.can_edit);
        assert!(actions.can_delete);
        assert!(actions.can_share);
        assert!(actions.is_owner);
        assert!(!actions.is_system_data);
        assert_eq!(actions.protection, ProtectionLevel::None);
    }

    #[test]
    fn test_get_available_actions_non_owner() {
        let ctx = user_ctx("other_user");
        let ownership = make_ownership("user_123");
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);

        assert!(actions.can_read);
        assert!(!actions.can_edit);
        assert!(!actions.can_delete);
        assert!(!actions.can_share);
        assert!(!actions.is_owner);
    }

    #[test]
    fn test_get_available_actions_system_data() {
        let ctx = admin_ctx();
        let ownership = OwnershipMeta::new_system(ProtectionLevel::System);
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);

        assert!(actions.can_read);
        assert!(!actions.can_edit);
        assert!(!actions.can_delete);
        assert!(actions.is_system_data);
        assert_eq!(actions.protection, ProtectionLevel::System);
    }

    // ── Backward compatibility tests ──

    #[test]
    fn test_backward_compat_no_collaborators() {
        let ctx = user_ctx("other_user");
        let ownership = make_ownership("user_123");
        // No collaborators at all — should not crash, just deny
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_backward_compat_empty_roles() {
        let ctx = AccessContext::new("user_a", vec![]);
        let ownership = make_ownership("user_123");
        // No roles — not admin, not owner, should only see public
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    // ========================================================================
    // T15.11 补充测试：ABAC 边界场景
    // ========================================================================

    #[test]
    fn test_owner_can_read_private_record() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_visibility(Visibility::Private);
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_collaborator_read_only_cannot_write_or_delete() {
        let mut ownership = make_ownership("user_123").with_visibility(Visibility::Private);
        ownership.collaborators.push(Collaborator {
            user_id: "readonly_user".to_string(),
            permissions: vec![RecordPermission::Read],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx = user_ctx("readonly_user");
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    #[test]
    fn test_collaborator_with_share_permission() {
        let mut ownership = make_ownership("user_123");
        ownership.collaborators.push(Collaborator {
            user_id: "sharer".to_string(),
            permissions: vec![RecordPermission::Read, RecordPermission::Share],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx = user_ctx("sharer");
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        // Share permission is owner/admin only, not driven by collaborator permissions
        assert!(!RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    #[test]
    fn test_multiple_collaborators_only_matching_has_access() {
        let mut ownership = make_ownership("user_123").with_visibility(Visibility::Private);
        ownership.collaborators.push(Collaborator {
            user_id: "collab_a".to_string(),
            permissions: vec![RecordPermission::Read, RecordPermission::Write],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });
        ownership.collaborators.push(Collaborator {
            user_id: "collab_b".to_string(),
            permissions: vec![RecordPermission::Read],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx_a = user_ctx("collab_a");
        assert!(RecordPermissionChecker::can_read(&ctx_a, &ownership));
        assert!(RecordPermissionChecker::can_edit(&ctx_a, &ownership));

        let ctx_b = user_ctx("collab_b");
        assert!(RecordPermissionChecker::can_read(&ctx_b, &ownership));
        assert!(!RecordPermissionChecker::can_edit(&ctx_b, &ownership));

        let ctx_c = user_ctx("collab_c");
        assert!(!RecordPermissionChecker::can_read(&ctx_c, &ownership));
    }

    #[test]
    fn test_shared_visibility_with_multiple_users() {
        let ownership = make_ownership("user_123").with_visibility(Visibility::Shared {
            user_ids: vec!["user_a".to_string(), "user_b".to_string(), "user_c".to_string()],
        });

        assert!(RecordPermissionChecker::can_read(&user_ctx("user_a"), &ownership));
        assert!(RecordPermissionChecker::can_read(&user_ctx("user_b"), &ownership));
        assert!(RecordPermissionChecker::can_read(&user_ctx("user_c"), &ownership));
        assert!(!RecordPermissionChecker::can_read(&user_ctx("user_d"), &ownership));
    }

    #[test]
    fn test_role_restricted_user_with_multiple_roles() {
        // User has ["reader", "editor"], visibility requires ["editor"]
        let ctx = AccessContext::new("user_a", vec!["reader".to_string(), "editor".to_string()]);
        let ownership = make_ownership("user_123").with_visibility(Visibility::RoleRestricted {
            roles: vec!["editor".to_string()],
        });
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_federation_admin_role_has_all_permissions() {
        let ctx = AccessContext::new("fed_admin", vec!["federation_admin".to_string()]);
        let ownership = make_ownership("user_123").with_visibility(Visibility::Private);

        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
        assert!(RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    #[test]
    fn test_admin_can_share_non_owned_record() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123");
        assert!(RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    #[test]
    fn test_get_available_actions_collaborator_rw() {
        let mut ownership = make_ownership("user_123");
        ownership.collaborators.push(Collaborator {
            user_id: "collab_user".to_string(),
            permissions: vec![RecordPermission::Read, RecordPermission::Write],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx = user_ctx("collab_user");
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);
        assert!(actions.can_read);
        assert!(actions.can_edit);
        assert!(!actions.can_delete);
        assert!(!actions.can_share);
        assert!(!actions.is_owner);
        assert!(!actions.is_system_data);
    }

    #[test]
    fn test_get_available_actions_readonly_protected() {
        let ctx = user_ctx("user_123"); // owner
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);

        assert!(actions.can_read);
        assert!(!actions.can_edit);   // ReadOnly blocks edit
        assert!(actions.can_delete);  // ReadOnly allows delete
        assert!(actions.can_share);
        assert!(actions.is_owner);
        assert_eq!(actions.protection, ProtectionLevel::ReadOnly);
    }

    #[test]
    fn test_shared_empty_user_list_denies_all() {
        let ownership = make_ownership("user_123").with_visibility(Visibility::Shared {
            user_ids: vec![],
        });
        let ctx = user_ctx("other_user");
        assert!(!RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_role_restricted_empty_roles_denies_all() {
        let ownership = make_ownership("user_123").with_visibility(Visibility::RoleRestricted {
            roles: vec![],
        });
        let ctx = user_ctx("other_user");
        assert!(!RecordPermissionChecker::can_read(&ctx, &ownership));
    }

    #[test]
    fn test_non_owner_cannot_share() {
        let ctx = user_ctx("other_user");
        let ownership = make_ownership("user_123");
        assert!(!RecordPermissionChecker::can_share(&ctx, &ownership));
    }

    // ========================================================================
    // T15.16：ProtectionLevel 完整矩阵测试 (4 级别 × 2 操作 × 2 角色 = 16)
    // ========================================================================

    // ── None 级别 ──

    #[test]
    fn test_protection_none_owner_can_edit() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::None);
        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_none_owner_can_delete() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::None);
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    #[test]
    fn test_protection_none_admin_can_edit() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::None);
        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_none_admin_can_delete() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::None);
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    // ── ReadOnly 级别 ──

    #[test]
    fn test_protection_readonly_owner_cannot_edit() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_readonly_owner_can_delete() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    #[test]
    fn test_protection_readonly_admin_cannot_edit() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_readonly_admin_can_delete() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    // ── Immutable 级别 ──

    #[test]
    fn test_protection_immutable_owner_cannot_edit() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_immutable_owner_cannot_delete() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    #[test]
    fn test_protection_immutable_admin_cannot_edit() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_immutable_admin_cannot_delete() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    // ── System 级别 ──

    #[test]
    fn test_protection_system_owner_cannot_edit() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::System);
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_system_owner_cannot_delete() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::System);
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    #[test]
    fn test_protection_system_admin_cannot_edit_without_override() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::System);
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_system_admin_cannot_delete_without_override() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::System);
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    // ── system_override 标志 ──

    #[test]
    fn test_protection_system_with_override_can_edit() {
        let ctx = admin_ctx().with_system_override();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::System);
        assert!(RecordPermissionChecker::can_edit(&ctx, &ownership));
    }

    #[test]
    fn test_protection_system_with_override_can_delete() {
        let ctx = admin_ctx().with_system_override();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::System);
        assert!(RecordPermissionChecker::can_delete(&ctx, &ownership));
    }

    // ── 保护级别变更权限 ──

    #[test]
    fn test_protection_change_admin_none_to_readonly() {
        let ctx = admin_ctx();
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::None, &ProtectionLevel::ReadOnly,
        );
        assert!(result.is_continue());
    }

    #[test]
    fn test_protection_change_admin_readonly_to_immutable() {
        let ctx = admin_ctx();
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::ReadOnly, &ProtectionLevel::Immutable,
        );
        assert!(result.is_continue());
    }

    #[test]
    fn test_protection_change_admin_immutable_to_none() {
        let ctx = admin_ctx();
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::Immutable, &ProtectionLevel::None,
        );
        assert!(result.is_continue());
    }

    #[test]
    fn test_protection_change_non_admin_denied() {
        let ctx = user_ctx("user_123");
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::None, &ProtectionLevel::ReadOnly,
        );
        assert!(!result.is_continue());
    }

    #[test]
    fn test_protection_change_to_system_denied_without_override() {
        let ctx = admin_ctx();
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::None, &ProtectionLevel::System,
        );
        assert!(!result.is_continue());
    }

    #[test]
    fn test_protection_change_from_system_denied_without_override() {
        let ctx = admin_ctx();
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::System, &ProtectionLevel::None,
        );
        assert!(!result.is_continue());
    }

    #[test]
    fn test_protection_change_system_with_override_allowed() {
        let ctx = admin_ctx().with_system_override();
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::System, &ProtectionLevel::None,
        );
        assert!(result.is_continue());
    }

    #[test]
    fn test_protection_change_to_system_with_override_allowed() {
        let ctx = admin_ctx().with_system_override();
        let result = RecordPermissionChecker::check_protection_change(
            &ctx, &ProtectionLevel::None, &ProtectionLevel::System,
        );
        assert!(result.is_continue());
    }

    // ── 保护级别 × 协作者交互 ──

    #[test]
    fn test_protection_readonly_blocks_collaborator_write() {
        let mut ownership = make_ownership("user_123").with_protection(ProtectionLevel::ReadOnly);
        ownership.collaborators.push(Collaborator {
            user_id: "collab_user".to_string(),
            permissions: vec![RecordPermission::Read, RecordPermission::Write],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx = user_ctx("collab_user");
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_edit(&ctx, &ownership)); // Protection > Collaborator
    }

    #[test]
    fn test_protection_immutable_blocks_collaborator_delete() {
        let mut ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);
        ownership.collaborators.push(Collaborator {
            user_id: "collab_user".to_string(),
            permissions: vec![RecordPermission::Read, RecordPermission::Delete],
            granted_at: now_ms(),
            granted_by: "user_123".to_string(),
        });

        let ctx = user_ctx("collab_user");
        assert!(RecordPermissionChecker::can_read(&ctx, &ownership));
        assert!(!RecordPermissionChecker::can_delete(&ctx, &ownership)); // Protection > Collaborator
    }

    // ── get_available_actions 保护级别场景 ──

    #[test]
    fn test_get_available_actions_immutable_owner() {
        let ctx = user_ctx("user_123");
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);

        assert!(actions.can_read);
        assert!(!actions.can_edit);
        assert!(!actions.can_delete);
        assert!(actions.can_share);
        assert!(actions.is_owner);
        assert_eq!(actions.protection, ProtectionLevel::Immutable);
        // Owner is not admin, cannot change protection
        assert!(!actions.can_change_protection);
    }

    #[test]
    fn test_get_available_actions_admin_can_change_protection() {
        let ctx = admin_ctx();
        let ownership = make_ownership("user_123").with_protection(ProtectionLevel::Immutable);
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);

        assert!(actions.can_read);
        assert!(!actions.can_edit);     // Immutable blocks
        assert!(!actions.can_delete);   // Immutable blocks
        assert!(actions.can_share);     // Admin can share
        assert!(actions.can_change_protection); // Admin can downgrade
    }

    #[test]
    fn test_get_available_actions_system_admin_no_override() {
        let ctx = admin_ctx();
        let ownership = OwnershipMeta::new_system(ProtectionLevel::System);
        let actions = RecordPermissionChecker::get_available_actions(&ctx, &ownership);

        assert!(actions.can_read);
        assert!(!actions.can_edit);
        assert!(!actions.can_delete);
        assert!(!actions.can_change_protection); // System requires override
        assert!(actions.is_system_data);
    }
}
