"""
Security Layer - Authentication and Authorization

Mock authentication and RBAC for ALTRUS system.
"""

import hashlib
import secrets
import time
from typing import Dict, Optional, List, Set
from enum import Enum
from dataclasses import dataclass


class Role(Enum):
    """User roles for RBAC"""
    ADMIN = "ADMIN"          # Full system access
    OPERATOR = "OPERATOR"    # Can submit intents, view status
    VIEWER = "VIEWER"        # Read-only access


class Permission(Enum):
    """System permissions"""
    # Intent permissions
    SUBMIT_INTENT = "submit_intent"
    VIEW_INTENT = "view_intent"
    CANCEL_INTENT = "cancel_intent"

    # Module permissions
    REGISTER_MODULE = "register_module"
    VIEW_MODULE = "view_module"
    UNREGISTER_MODULE = "unregister_module"

    # System permissions
    VIEW_SYSTEM_STATE = "view_system_state"
    CHANGE_SYSTEM_STATE = "change_system_state"
    VIEW_LEDGER = "view_ledger"

    # Admin permissions
    MANAGE_USERS = "manage_users"
    VIEW_METRICS = "view_metrics"


# Role-Permission mapping
ROLE_PERMISSIONS: Dict[Role, Set[Permission]] = {
    Role.VIEWER: {
        Permission.VIEW_INTENT,
        Permission.VIEW_MODULE,
        Permission.VIEW_SYSTEM_STATE,
        Permission.VIEW_LEDGER,
        Permission.VIEW_METRICS,
    },
    Role.OPERATOR: {
        Permission.SUBMIT_INTENT,
        Permission.VIEW_INTENT,
        Permission.CANCEL_INTENT,
        Permission.VIEW_MODULE,
        Permission.VIEW_SYSTEM_STATE,
        Permission.VIEW_LEDGER,
        Permission.VIEW_METRICS,
    },
    Role.ADMIN: set(Permission),  # All permissions
}


@dataclass
class User:
    """User account"""
    username: str
    password_hash: str
    role: Role
    api_key: Optional[str] = None
    created_at: float = 0.0
    last_login: Optional[float] = None


@dataclass
class Session:
    """User session"""
    session_id: str
    username: str
    role: Role
    created_at: float
    expires_at: float


class AuthenticationManager:
    """
    Manages user authentication and sessions.

    Features:
    - Password hashing (SHA-256 for simplicity)
    - Session management
    - API key authentication
    - Session expiration
    """

    def __init__(self, session_timeout: int = 3600):
        self._users: Dict[str, User] = {}
        self._sessions: Dict[str, Session] = {}
        self._api_keys: Dict[str, str] = {}  # api_key -> username
        self._session_timeout = session_timeout

        # Create default admin user
        self._create_default_admin()

    def _create_default_admin(self):
        """Create default admin user"""
        self.create_user("admin", "admin", Role.ADMIN)

    def _hash_password(self, password: str) -> str:
        """Hash password using SHA-256"""
        return hashlib.sha256(password.encode()).hexdigest()

    def create_user(self, username: str, password: str, role: Role) -> bool:
        """Create a new user"""
        if username in self._users:
            return False

        user = User(
            username=username,
            password_hash=self._hash_password(password),
            role=role,
            created_at=time.time()
        )
        self._users[username] = user
        return True

    def authenticate(self, username: str, password: str) -> Optional[str]:
        """
        Authenticate user and create session.

        Returns:
            Session ID if successful, None otherwise
        """
        user = self._users.get(username)
        if not user:
            return None

        password_hash = self._hash_password(password)
        if password_hash != user.password_hash:
            return None

        # Create session
        session_id = secrets.token_hex(32)
        session = Session(
            session_id=session_id,
            username=username,
            role=user.role,
            created_at=time.time(),
            expires_at=time.time() + self._session_timeout
        )

        self._sessions[session_id] = session
        user.last_login = time.time()

        return session_id

    def validate_session(self, session_id: str) -> Optional[Session]:
        """Validate session and check expiration"""
        session = self._sessions.get(session_id)
        if not session:
            return None

        # Check expiration
        if time.time() > session.expires_at:
            del self._sessions[session_id]
            return None

        return session

    def logout(self, session_id: str) -> bool:
        """Logout user and invalidate session"""
        if session_id in self._sessions:
            del self._sessions[session_id]
            return True
        return False

    def generate_api_key(self, username: str) -> Optional[str]:
        """Generate API key for user"""
        user = self._users.get(username)
        if not user:
            return None

        api_key = f"altrus_{secrets.token_hex(32)}"
        user.api_key = api_key
        self._api_keys[api_key] = username

        return api_key

    def authenticate_api_key(self, api_key: str) -> Optional[str]:
        """Authenticate using API key, returns username"""
        return self._api_keys.get(api_key)

    def get_user(self, username: str) -> Optional[User]:
        """Get user by username"""
        return self._users.get(username)


class AuthorizationManager:
    """
    Manages role-based access control (RBAC).

    Features:
    - Role-based permissions
    - Permission checking
    - Audit logging
    """

    def __init__(self):
        self._audit_log: List[Dict] = []

    def has_permission(self, role: Role, permission: Permission) -> bool:
        """Check if role has permission"""
        role_perms = ROLE_PERMISSIONS.get(role, set())
        return permission in role_perms

    def check_permission(
        self,
        session: Session,
        permission: Permission,
        resource: Optional[str] = None
    ) -> bool:
        """
        Check if session has permission for action.

        Logs all permission checks for audit.
        """
        has_perm = self.has_permission(session.role, permission)

        # Audit log
        self._audit_log.append({
            "timestamp": time.time(),
            "username": session.username,
            "role": session.role.value,
            "permission": permission.value,
            "resource": resource,
            "granted": has_perm
        })

        return has_perm

    def get_audit_log(self, limit: int = 100) -> List[Dict]:
        """Get recent audit log entries"""
        return self._audit_log[-limit:]

    def require_permission(
        self,
        session: Session,
        permission: Permission,
        resource: Optional[str] = None
    ):
        """
        Require permission or raise exception.

        Raises:
            PermissionError if permission denied
        """
        if not self.check_permission(session, permission, resource):
            raise PermissionError(
                f"User {session.username} ({session.role.value}) "
                f"does not have permission: {permission.value}"
            )


class RateLimiter:
    """
    Rate limiter for API endpoints.

    Features:
    - Per-user rate limiting
    - Sliding window algorithm
    - Configurable limits
    """

    def __init__(self, max_requests: int = 100, window_seconds: int = 60):
        self._max_requests = max_requests
        self._window_seconds = window_seconds
        self._requests: Dict[str, List[float]] = {}  # username -> timestamps

    def check_rate_limit(self, username: str) -> bool:
        """
        Check if user is within rate limit.

        Returns:
            True if allowed, False if rate limit exceeded
        """
        now = time.time()

        # Get user's request history
        if username not in self._requests:
            self._requests[username] = []

        requests = self._requests[username]

        # Remove old requests outside window
        cutoff = now - self._window_seconds
        requests = [ts for ts in requests if ts > cutoff]
        self._requests[username] = requests

        # Check limit
        if len(requests) >= self._max_requests:
            return False

        # Record this request
        requests.append(now)
        return True

    def get_remaining_requests(self, username: str) -> int:
        """Get number of remaining requests in current window"""
        now = time.time()

        if username not in self._requests:
            return self._max_requests

        requests = self._requests[username]
        cutoff = now - self._window_seconds
        recent_requests = [ts for ts in requests if ts > cutoff]

        return max(0, self._max_requests - len(recent_requests))


# Global instances
_auth_manager: Optional[AuthenticationManager] = None
_authz_manager: Optional[AuthorizationManager] = None
_rate_limiter: Optional[RateLimiter] = None


def get_auth_manager() -> AuthenticationManager:
    """Get global authentication manager"""
    global _auth_manager
    if _auth_manager is None:
        _auth_manager = AuthenticationManager()
    return _auth_manager


def get_authz_manager() -> AuthorizationManager:
    """Get global authorization manager"""
    global _authz_manager
    if _authz_manager is None:
        _authz_manager = AuthorizationManager()
    return _authz_manager


def get_rate_limiter() -> RateLimiter:
    """Get global rate limiter"""
    global _rate_limiter
    if _rate_limiter is None:
        _rate_limiter = RateLimiter()
    return _rate_limiter
