# #region Test.SecurityBadgeService [C:3] [TYPE Module] [SEMANTICS test,security,badge]
# @BRIEF Verify SecurityBadgeService contracts — role extraction, permission pairs, security summary.
# @RELATION BINDS_TO -> [SecurityBadgeService]
# @TEST_EDGE: missing_field -> empty/None roles, permissions, auth_source handled gracefully
# @TEST_EDGE: invalid_type -> empty role names, whitespace-only names sanitized
# @TEST_EDGE: external_fail -> discover_declared_permissions raises, fallback to user permissions

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

from unittest.mock import MagicMock, patch
import pytest

# #region _make_user [C:1] [TYPE Function]
def _make_user(**kwargs):
    from src.models.auth import User
    user = MagicMock(spec=User)
    for k, v in kwargs.items():
        setattr(user, k, v)
    return user
    # #endregion _make_user
# #region _make_role [C:1] [TYPE Function]
def _make_role(name, permissions=None):
    role = MagicMock()
    role.name = name
    role.permissions = permissions or []
    return role
    # #endregion _make_role
# #region _make_permission [C:1] [TYPE Function]
def _make_permission(resource, action):
    perm = MagicMock()
    perm.resource = resource
    perm.action = action
    return perm
# #endregion _make_permission


class TestCollectUserPermissionPairs:
    """_collect_user_permission_pairs — extract (resource, ACTION) tuples."""
    # #region test_no_roles_returns_empty [C:2] [TYPE Function]
    # @BRIEF User with empty roles list yields no permission pairs.
    def test_no_roles_returns_empty(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        user = _make_user(roles=[])
        result = svc._collect_user_permission_pairs(user)
        assert result == set()
    # #endregion test_no_roles_returns_empty

    # #region test_role_with_permissions [C:2] [TYPE Function]
    # @BRIEF Role permissions are normalized to (resource, ACTION) tuples.
    def test_role_with_permissions(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        perms = [_make_permission("dashboard", "read"), _make_permission("dataset", "write")]
        role = _make_role("Editor", perms)
        user = _make_user(roles=[role])
        result = svc._collect_user_permission_pairs(user)
        assert ("dashboard", "READ") in result
        assert ("dataset", "WRITE") in result
    # #endregion test_role_with_permissions

    # #region test_permission_without_resource_skipped [C:2] [TYPE Function]
    # @BRIEF Permissions with empty or None resource are excluded from collection.
    def test_permission_without_resource_skipped(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        perms = [_make_permission("", "read"), _make_permission(None, "write")]
        role = _make_role("Viewer", perms)
        user = _make_user(roles=[role])
        result = svc._collect_user_permission_pairs(user)
        assert result == set()
    # #endregion test_permission_without_resource_skipped

    # #region test_multiple_roles_dedup [C:2] [TYPE Function]
    # @BRIEF Duplicate permissions across roles are deduplicated in result set.
    def test_multiple_roles_dedup(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        p1 = _make_permission("dashboard", "read")
        role1 = _make_role("Admin", [p1])
        role2 = _make_role("Editor", [p1])
        user = _make_user(roles=[role1, role2])
        result = svc._collect_user_permission_pairs(user)
        assert len(result) == 1
    # #endregion test_multiple_roles_dedup

class TestBuildSecuritySummary:
    """build_security_summary — full security snapshot."""
    # #region test_no_roles [C:2] [TYPE Function]
    # @BRIEF User with no roles produces empty roles list and None current_role.
    def test_no_roles(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        user = _make_user(roles=[], auth_source="database")
        summary = svc.build_security_summary(user)
        assert summary.read_only is True
        assert summary.roles == []
        assert summary.current_role is None
        assert summary.auth_source == "database"
    # #endregion test_no_roles

    # #region test_single_role [C:2] [TYPE Function]
    # @BRIEF Single non-admin role becomes current_role and appears in sorted roles list.
    def test_single_role(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        role = _make_role("Viewer")
        user = _make_user(roles=[role], auth_source="ldap")
        summary = svc.build_security_summary(user)
        assert summary.roles == ["Viewer"]
        assert summary.current_role == "Viewer"
    # #endregion test_single_role

    # #region test_admin_role_detected [C:2] [TYPE Function]
    # @BRIEF Admin role is detected and set as current_role.
    def test_admin_role_detected(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        role = _make_role("Admin")
        user = _make_user(roles=[role], auth_source="database")
        summary = svc.build_security_summary(user)
        assert "Admin" in summary.roles
        assert summary.current_role == "Admin"
    # #endregion test_admin_role_detected

    # #region test_roles_sorted [C:2] [TYPE Function]
    # @BRIEF Roles in summary are deterministically sorted alphabetically.
    def test_roles_sorted(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        role_z = _make_role("ZViewer")
        role_a = _make_role("AAdmin")
        user = _make_user(roles=[role_z, role_a])
        summary = svc.build_security_summary(user)
        assert summary.roles == ["AAdmin", "ZViewer"]
    # #endregion test_roles_sorted

    # #region test_discover_permissions_fallback [C:2] [TYPE Function]
    # @BRIEF When discover_declared_permissions raises, fallback to user permission pairs.
    def test_discover_permissions_fallback(self):
        from src.services.security_badge_service import SecurityBadgeService
        perms = [_make_permission("dashboard", "read")]
        role = _make_role("Editor", perms)
        user = _make_user(roles=[role])
        with patch("src.services.security_badge_service.discover_declared_permissions",
                   side_effect=RuntimeError("Plugin load failed")):
            svc = SecurityBadgeService(plugin_loader=MagicMock())
            summary = svc.build_security_summary(user)
            assert len(summary.permissions) >= 0
    # #endregion test_discover_permissions_fallback

    # #region test_is_admin_allows_all [C:2] [TYPE Function]
    # @BRIEF Admin user sees all declared permissions as allowed=True.
    def test_is_admin_allows_all(self):
        from src.services.security_badge_service import SecurityBadgeService
        perms = [_make_permission("dashboard", "read")]
        role = _make_role("Admin", perms)
        user = _make_user(roles=[role])
        with patch("src.services.security_badge_service.discover_declared_permissions",
                   return_value={("dashboard", "READ"), ("dataset", "WRITE")}):
            svc = SecurityBadgeService()
            summary = svc.build_security_summary(user)
            admin_perm = next((p for p in summary.permissions if p.key == "dashboard"), None)
            assert admin_perm is not None
            assert admin_perm.allowed is True
    # #endregion test_is_admin_allows_all

    # #region test_non_admin_permission_allowed_and_denied [C:2] [TYPE Function]
    # @BRIEF Non-admin user: owned permissions allowed=True, others allowed=False.
    def test_non_admin_permission_allowed_and_denied(self):
        from src.services.security_badge_service import SecurityBadgeService
        perms = [_make_permission("dashboard", "read")]
        role = _make_role("Editor", perms)
        user = _make_user(roles=[role], auth_source="LOCAL")
        with patch("src.services.security_badge_service.discover_declared_permissions",
                   return_value={("dashboard", "READ"), ("dataset", "WRITE")}):
            svc = SecurityBadgeService()
            summary = svc.build_security_summary(user)
            perm_map = {p.key: p.allowed for p in summary.permissions}
            assert perm_map["dashboard"] is True
            assert perm_map["dataset:write"] is False
    # #endregion test_non_admin_permission_allowed_and_denied

    # #region test_role_source_equals_auth_source [C:2] [TYPE Function]
    # @BRIEF role_source field mirrors auth_source from user.
    def test_role_source_equals_auth_source(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        user = _make_user(roles=[], auth_source="ADFS")
        summary = svc.build_security_summary(user)
        assert summary.role_source == "ADFS"
    # #endregion test_role_source_equals_auth_source

    # #region test_auth_source_none [C:2] [TYPE Function]
    # @BRIEF User with None auth_source produces None in summary.
    def test_auth_source_none(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        user = _make_user(roles=[], auth_source=None)
        summary = svc.build_security_summary(user)
        assert summary.auth_source is None
    # #endregion test_auth_source_none

    # #region test_user_roles_none [C:2] [TYPE Function]
    # @BRIEF User with roles=None is treated as empty roles list.
    def test_user_roles_none(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        user = _make_user(roles=None, auth_source="LOCAL")
        summary = svc.build_security_summary(user)
        assert summary.roles == []
        assert summary.current_role is None
    # #endregion test_user_roles_none

    # #region test_permission_key_format_read [C:2] [TYPE Function]
    # @BRIEF READ action produces resource-only key (no action suffix).
    def test_permission_key_format_read(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        key = svc._format_permission_key("dashboard", "READ")
        assert key == "dashboard"
    # #endregion test_permission_key_format_read

    # #region test_permission_key_format_write [C:2] [TYPE Function]
    # @BRIEF Non-READ action produces resource:action key format.
    def test_permission_key_format_write(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        key = svc._format_permission_key("dataset", "WRITE")
        assert key == "dataset:write"
    # #endregion test_permission_key_format_write

class TestFormatPermissionKey:
    """_format_permission_key — compact UI key format."""
    # #region test_read_omits_action [C:2] [TYPE Function]
    # @BRIEF READ action yields bare resource string without action suffix.
    def test_read_omits_action(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        assert svc._format_permission_key("dashboard", "READ") == "dashboard"
    # #endregion test_read_omits_action

    # #region test_write_includes_action [C:2] [TYPE Function]
    # @BRIEF Non-READ action yields resource:lowercase_action compound key.
    def test_write_includes_action(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        assert svc._format_permission_key("dataset", "WRITE") == "dataset:write"
    # #endregion test_write_includes_action

    # #region test_empty_resource [C:2] [TYPE Function]
    # @BRIEF Empty resource produces a valid string key without raising.
    def test_empty_resource(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        result = svc._format_permission_key("", "READ")
        assert result is not None
        assert isinstance(result, str)
    # #endregion test_empty_resource

class TestPermissionEdgeCases:
    """Edge cases for permission processing."""
    # #region test_empty_role_name_skipped [C:2] [TYPE Function]
    # @BRIEF Roles with empty or None names are excluded from summary roles list.
    def test_empty_role_name_skipped(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        role_empty = _make_role("")
        role_none = _make_role(None)
        user = _make_user(roles=[role_empty, role_none])
        summary = svc.build_security_summary(user)
        assert summary.roles == []
    # #endregion test_empty_role_name_skipped

    # #region test_role_name_with_whitespace_sanitized [C:2] [TYPE Function]
    # @BRIEF Role names with surrounding whitespace are trimmed in summary.
    def test_role_name_with_whitespace_sanitized(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        role = _make_role("  Editor  ")
        user = _make_user(roles=[role])
        summary = svc.build_security_summary(user)
        assert "Editor" in summary.roles
    # #endregion test_role_name_with_whitespace_sanitized

    # #region test_collect_with_none_permissions [C:2] [TYPE Function]
    # @BRIEF Role with permissions=None yields empty permission set without error.
    def test_collect_with_none_permissions(self):
        from src.services.security_badge_service import SecurityBadgeService
        svc = SecurityBadgeService()
        role = _make_role("Viewer", permissions=None)
        user = _make_user(roles=[role])
        result = svc._collect_user_permission_pairs(user)
        assert result == set()
    # #endregion test_collect_with_none_permissions
# #endregion Test.SecurityBadgeService
