"""Role-based access control (RBAC) for compliance operations."""

from __future__ import annotations

import logging
import time
from typing import Any

from horizon.compliance._audit import AuditTrail
from horizon.compliance._store import ComplianceStore
from horizon.compliance._types import AuditEventType, ComplianceConfig, Role

logger = logging.getLogger("horizon.compliance.access")

# Permission matrix: role -> set of allowed actions
PERMISSIONS: dict[Role, set[str]] = {
    Role.VIEWER: {
        "view_positions", "view_feeds", "view_orders", "view_audit_trail",
        "view_alerts", "view_reports",
    },
    Role.TRADER: {
        "submit_order", "cancel_order", "view_positions", "view_feeds",
        "view_orders", "view_alerts",
    },
    Role.RISK_MANAGER: {
        "submit_order", "cancel_order", "view_positions", "view_feeds",
        "view_orders", "view_alerts", "view_audit_trail", "view_reports",
        "activate_kill_switch", "deactivate_kill_switch", "modify_risk_config",
    },
    Role.COMPLIANCE_OFFICER: {
        "submit_order", "cancel_order", "view_positions", "view_feeds",
        "view_orders", "view_alerts", "view_audit_trail", "view_reports",
        "activate_kill_switch", "deactivate_kill_switch", "modify_risk_config",
        "approve_order", "deny_order", "resolve_alert",
        "generate_report", "modify_compliance_config", "export_audit",
    },
    Role.ADMIN: {
        "submit_order", "cancel_order", "view_positions", "view_feeds",
        "view_orders", "view_alerts", "view_audit_trail", "view_reports",
        "activate_kill_switch", "deactivate_kill_switch", "modify_risk_config",
        "approve_order", "deny_order", "resolve_alert",
        "generate_report", "modify_compliance_config", "export_audit",
        "manage_roles", "purge_data",
    },
}


class AccessControl:
    """Role-based access control for compliance operations.

    Maps actor names to roles and enforces permission checks.
    All access attempts are logged to the audit trail.
    """

    def __init__(
        self,
        config: ComplianceConfig,
        store: ComplianceStore,
        audit: AuditTrail,
    ) -> None:
        self._config = config
        self._store = store
        self._audit = audit
        self._roles: dict[str, Role] = dict(config.roles)

    def get_role(self, actor: str) -> Role | None:
        """Get the role assigned to an actor."""
        return self._roles.get(actor)

    def check_permission(self, actor: str, permission: str) -> bool:
        """Check if actor has a permission. Returns True/False."""
        role = self._roles.get(actor)
        if role is None:
            return False
        allowed = permission in PERMISSIONS.get(role, set())
        return allowed

    def require_permission(self, actor: str, permission: str) -> None:
        """Raise PermissionError if actor lacks permission.

        Logs all access attempts (granted and denied) to audit trail.
        """
        role = self._roles.get(actor)
        allowed = self.check_permission(actor, permission)

        self._audit.record(
            AuditEventType.CONFIG_CHANGED if not allowed else AuditEventType.CONFIG_CHANGED,
            actor,
            {
                "action": "access_check",
                "permission": permission,
                "role": role.value if role else "none",
                "allowed": allowed,
            },
        )

        if not allowed:
            raise PermissionError(
                f"Actor '{actor}' (role={role.value if role else 'none'}) "
                f"lacks permission '{permission}'"
            )

    def assign_role(self, actor: str, role: Role, assigned_by: str) -> None:
        """Assign a role to an actor. The assigner must be an admin."""
        self.require_permission(assigned_by, "manage_roles")
        old_role = self._roles.get(actor)
        self._roles[actor] = role
        self._audit.record(
            AuditEventType.CONFIG_CHANGED, assigned_by,
            {
                "action": "role_assignment",
                "target_actor": actor,
                "old_role": old_role.value if old_role else "none",
                "new_role": role.value,
            },
        )
        logger.info("Role assigned: %s -> %s (by %s)", actor, role.value, assigned_by)

    def revoke_role(self, actor: str, revoked_by: str) -> None:
        """Remove an actor's role. The revoker must be an admin."""
        self.require_permission(revoked_by, "manage_roles")
        old_role = self._roles.pop(actor, None)
        self._audit.record(
            AuditEventType.CONFIG_CHANGED, revoked_by,
            {
                "action": "role_revocation",
                "target_actor": actor,
                "old_role": old_role.value if old_role else "none",
            },
        )

    def list_roles(self) -> dict[str, str]:
        """Return current role assignments."""
        return {actor: role.value for actor, role in self._roles.items()}
