"""
AIVA Permission Manager - PM-021

Enforces AIVA permission boundaries as defined in AIVA_CHARTER.md.
Supports three risk levels: low_risk (auto-approve), medium (notify), high (require approval).
"""

import os
import json
import logging
from datetime import datetime
from typing import Dict, Optional, Tuple
from enum import Enum
from pathlib import Path

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


class RiskLevel(Enum):
    """Permission risk levels"""
    LOW = "low_risk"      # Auto-approve
    MEDIUM = "medium"     # Notify after execution
    HIGH = "high"         # Require approval before execution
    CRITICAL = "critical" # Human override only


class PermissionResult(Enum):
    """Result of permission check"""
    APPROVED = "approved"
    DENIED = "denied"
    PENDING = "pending_approval"
    NOTIFY = "notify_required"


# Permission definitions based on AIVA Charter
PERMISSION_REGISTRY: Dict[str, Dict] = {
    # Level 1: Autonomous (Low Risk)
    "read_file": {
        "risk_level": RiskLevel.LOW,
        "description": "Read any file in Genesis system",
        "requires_logging": True
    },
    "query_memory": {
        "risk_level": RiskLevel.LOW,
        "description": "Query PostgreSQL, Qdrant, or Redis",
        "requires_logging": True
    },
    "execute_skill": {
        "risk_level": RiskLevel.LOW,
        "description": "Execute approved skills from registry",
        "requires_logging": True
    },
    "generate_report": {
        "risk_level": RiskLevel.LOW,
        "description": "Generate reports and analytics",
        "requires_logging": True
    },

    # Level 2: Notify (Medium Risk)
    "write_file": {
        "risk_level": RiskLevel.MEDIUM,
        "description": "Create/modify files in designated directories",
        "requires_logging": True,
        "notify_channel": "slack"
    },
    "modify_config": {
        "risk_level": RiskLevel.MEDIUM,
        "description": "Modify non-critical configuration",
        "requires_logging": True,
        "notify_channel": "slack"
    },
    "external_api_call": {
        "risk_level": RiskLevel.MEDIUM,
        "description": "Call external APIs",
        "requires_logging": True,
        "notify_channel": None
    },

    # Level 3: Approval Required (High Risk)
    "modify_genesis": {
        "risk_level": RiskLevel.HIGH,
        "description": "Modify core system files",
        "requires_logging": True,
        "requires_approval": True
    },
    "create_branch": {
        "risk_level": RiskLevel.HIGH,
        "description": "Create new Git branches",
        "requires_logging": True,
        "requires_approval": True
    },
    "deploy": {
        "risk_level": RiskLevel.HIGH,
        "description": "Deploy to production",
        "requires_logging": True,
        "requires_approval": True
    },
    "budget_allocation": {
        "risk_level": RiskLevel.HIGH,
        "description": "Allocate or spend budget",
        "requires_logging": True,
        "requires_approval": True
    },

    # Level 4: Human Override Only (Critical)
    "delete_data": {
        "risk_level": RiskLevel.CRITICAL,
        "description": "Delete production data",
        "requires_logging": True,
        "human_only": True
    },
    "modify_credentials": {
        "risk_level": RiskLevel.CRITICAL,
        "description": "Modify security credentials",
        "requires_logging": True,
        "human_only": True
    },
    "modify_charter": {
        "risk_level": RiskLevel.CRITICAL,
        "description": "Alter AIVA Charter",
        "requires_logging": True,
        "human_only": True
    }
}


class PermissionManager:
    """
    Manages AIVA permissions and enforces boundaries.

    Usage:
        pm = PermissionManager()
        result, message = pm.check_permission("write_file", context={"path": "/AIVA/test.py"})
        if result == PermissionResult.APPROVED:
            # Proceed with action
        elif result == PermissionResult.NOTIFY:
            # Execute and send notification
        elif result == PermissionResult.PENDING:
            # Wait for approval
        else:
            # Action denied
    """

    def __init__(self, log_dir: str = "logs"):
        """Initialize PermissionManager with logging directory."""
        self.log_dir = Path(log_dir)
        self.log_dir.mkdir(parents=True, exist_ok=True)
        self.permission_log_file = self.log_dir / "permission_checks.jsonl"
        self.pending_approvals: Dict[str, Dict] = {}
        logger.info("PermissionManager initialized")

    def check_permission(
        self,
        action: str,
        context: Optional[Dict] = None,
        requester: str = "AIVA"
    ) -> Tuple[PermissionResult, str]:
        """
        Check if an action is permitted.

        Args:
            action: The action to check (e.g., "write_file", "deploy")
            context: Additional context about the action
            requester: Who is requesting the permission

        Returns:
            Tuple of (PermissionResult, explanation message)
        """
        context = context or {}

        # Check if action is registered
        if action not in PERMISSION_REGISTRY:
            self._log_check(action, context, requester, PermissionResult.DENIED, "Unknown action")
            return PermissionResult.DENIED, f"Unknown action: {action}"

        perm = PERMISSION_REGISTRY[action]
        risk_level = perm["risk_level"]

        # Check based on risk level
        if risk_level == RiskLevel.LOW:
            result = PermissionResult.APPROVED
            message = f"Action '{action}' auto-approved (low risk)"

        elif risk_level == RiskLevel.MEDIUM:
            result = PermissionResult.NOTIFY
            message = f"Action '{action}' approved with notification requirement"

        elif risk_level == RiskLevel.HIGH:
            # Check if pre-approved
            if self._has_approval(action, context):
                result = PermissionResult.APPROVED
                message = f"Action '{action}' has prior approval"
            else:
                result = PermissionResult.PENDING
                message = f"Action '{action}' requires approval"
                self._request_approval(action, context, requester)

        else:  # CRITICAL
            result = PermissionResult.DENIED
            message = f"Action '{action}' requires human override - denied for autonomous execution"

        # Log the permission check
        self._log_check(action, context, requester, result, message)

        return result, message

    def approve_action(self, action: str, context: Optional[Dict] = None, approver: str = "human") -> bool:
        """
        Approve a pending action.

        Args:
            action: The action to approve
            context: Context to match the approval
            approver: Who approved the action

        Returns:
            True if approval recorded, False otherwise
        """
        approval_key = self._generate_approval_key(action, context or {})

        self.pending_approvals[approval_key] = {
            "action": action,
            "context": context,
            "approver": approver,
            "approved_at": datetime.utcnow().isoformat(),
            "expires_at": None  # Could add expiration logic
        }

        logger.info(f"Action '{action}' approved by {approver}")
        self._log_approval(action, context, approver)

        return True

    def deny_action(self, action: str, context: Optional[Dict] = None, reason: str = "") -> bool:
        """
        Deny a pending action.

        Args:
            action: The action to deny
            context: Context for the denial
            reason: Reason for denial

        Returns:
            True if denial recorded
        """
        approval_key = self._generate_approval_key(action, context or {})

        if approval_key in self.pending_approvals:
            del self.pending_approvals[approval_key]

        logger.info(f"Action '{action}' denied: {reason}")
        self._log_denial(action, context, reason)

        return True

    def get_risk_level(self, action: str) -> Optional[RiskLevel]:
        """Get the risk level for an action."""
        if action in PERMISSION_REGISTRY:
            return PERMISSION_REGISTRY[action]["risk_level"]
        return None

    def list_permissions(self, risk_level: Optional[RiskLevel] = None) -> Dict[str, Dict]:
        """List all registered permissions, optionally filtered by risk level."""
        if risk_level is None:
            return PERMISSION_REGISTRY

        return {
            k: v for k, v in PERMISSION_REGISTRY.items()
            if v["risk_level"] == risk_level
        }

    def _has_approval(self, action: str, context: Dict) -> bool:
        """Check if action has prior approval."""
        approval_key = self._generate_approval_key(action, context)
        return approval_key in self.pending_approvals

    def _request_approval(self, action: str, context: Dict, requester: str) -> None:
        """Request approval for a high-risk action."""
        approval_request = {
            "action": action,
            "context": context,
            "requester": requester,
            "requested_at": datetime.utcnow().isoformat(),
            "status": "pending"
        }

        # Log the request
        logger.warning(f"Approval requested for action '{action}' by {requester}")

        # In production, this would trigger Slack/email notification
        # For now, log to file
        request_file = self.log_dir / "pending_approvals.jsonl"
        with open(request_file, "a") as f:
            f.write(json.dumps(approval_request) + "\n")

    def _generate_approval_key(self, action: str, context: Dict) -> str:
        """Generate a unique key for approval tracking."""
        context_str = json.dumps(context, sort_keys=True)
        return f"{action}:{hash(context_str)}"

    def _log_check(
        self,
        action: str,
        context: Dict,
        requester: str,
        result: PermissionResult,
        message: str
    ) -> None:
        """Log a permission check."""
        log_entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "action": action,
            "context": context,
            "requester": requester,
            "result": result.value,
            "message": message
        }

        with open(self.permission_log_file, "a") as f:
            f.write(json.dumps(log_entry) + "\n")

        logger.debug(f"Permission check logged: {action} -> {result.value}")

    def _log_approval(self, action: str, context: Optional[Dict], approver: str) -> None:
        """Log an approval event."""
        log_entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "event": "approval",
            "action": action,
            "context": context,
            "approver": approver
        }

        with open(self.permission_log_file, "a") as f:
            f.write(json.dumps(log_entry) + "\n")

    def _log_denial(self, action: str, context: Optional[Dict], reason: str) -> None:
        """Log a denial event."""
        log_entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "event": "denial",
            "action": action,
            "context": context,
            "reason": reason
        }

        with open(self.permission_log_file, "a") as f:
            f.write(json.dumps(log_entry) + "\n")


# Singleton instance for easy import
_permission_manager: Optional[PermissionManager] = None


def get_permission_manager() -> PermissionManager:
    """Get or create the singleton PermissionManager instance."""
    global _permission_manager
    if _permission_manager is None:
        _permission_manager = PermissionManager()
    return _permission_manager


if __name__ == "__main__":
    # Example usage
    pm = PermissionManager()

    # Test low risk action
    result, msg = pm.check_permission("read_file", {"path": "/AIVA/test.py"})
    print(f"read_file: {result.value} - {msg}")

    # Test medium risk action
    result, msg = pm.check_permission("write_file", {"path": "/AIVA/new_file.py"})
    print(f"write_file: {result.value} - {msg}")

    # Test high risk action
    result, msg = pm.check_permission("deploy", {"environment": "production"})
    print(f"deploy: {result.value} - {msg}")

    # Test critical action
    result, msg = pm.check_permission("delete_data", {"table": "users"})
    print(f"delete_data: {result.value} - {msg}")

    # Test approval flow
    pm.approve_action("deploy", {"environment": "production"}, "admin")
    result, msg = pm.check_permission("deploy", {"environment": "production"})
    print(f"deploy (after approval): {result.value} - {msg}")
