"""User permission system for file access control.

This module provides a permission system to control user access to files
and other resources in the trax application.
"""

import json
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Set

from src.logging import get_logger

logger = get_logger(__name__)


@dataclass(frozen=True)
class Resource:
    """Represents a resource that can be accessed."""
    
    type: str  # e.g., "file", "directory", "api"
    path: str  # Resource path or identifier


@dataclass(frozen=True)
class Permission:
    """Represents a permission to perform an action on a resource."""
    
    action: str  # e.g., "read", "write", "delete"
    resource: Resource


@dataclass(frozen=True)
class User:
    """Represents a user in the system."""
    
    username: str
    display_name: str


class UserPermissionSystem:
    """Manages user permissions for resource access."""

    def __init__(self, permissions_file: Path = Path("~/.trax/permissions.json").expanduser()):
        """Initialize the permission system.

        Args:
            permissions_file: Path to the permissions configuration file
        """
        self.permissions_file = permissions_file
        self.permissions_file.parent.mkdir(parents=True, exist_ok=True)
        
        # Load existing permissions or create new structure
        self._load_permissions()

    def _load_permissions(self) -> None:
        """Load permissions from file."""
        if self.permissions_file.exists():
            try:
                with open(self.permissions_file, 'r') as f:
                    data = json.load(f)
                
                # Load users
                self.users: Dict[str, User] = {}
                for user_data in data.get('users', []):
                    user = User(user_data['username'], user_data['display_name'])
                    self.users[user.username] = user
                
                # Load permissions
                self.user_permissions: Dict[str, Set[Permission]] = {}
                for username, permissions_data in data.get('permissions', {}).items():
                    permissions = set()
                    for perm_data in permissions_data:
                        resource = Resource(perm_data['resource']['type'], perm_data['resource']['path'])
                        permission = Permission(perm_data['action'], resource)
                        permissions.add(permission)
                    self.user_permissions[username] = permissions
                
                logger.info(f"Loaded permissions for {len(self.users)} users")
            except Exception as e:
                logger.error(f"Error loading permissions: {str(e)}")
                self._initialize_empty_permissions()
        else:
            self._initialize_empty_permissions()

    def _initialize_empty_permissions(self) -> None:
        """Initialize empty permission structures."""
        self.users: Dict[str, User] = {}
        self.user_permissions: Dict[str, Set[Permission]] = {}

    def _save_permissions(self) -> None:
        """Save permissions to file."""
        try:
            # Convert users to serializable format
            users_data = []
            for user in self.users.values():
                users_data.append({
                    'username': user.username,
                    'display_name': user.display_name
                })
            
            # Convert permissions to serializable format
            permissions_data = {}
            for username, permissions in self.user_permissions.items():
                permissions_data[username] = []
                for permission in permissions:
                    permissions_data[username].append({
                        'action': permission.action,
                        'resource': {
                            'type': permission.resource.type,
                            'path': permission.resource.path
                        }
                    })
            
            # Save to file
            data = {
                'users': users_data,
                'permissions': permissions_data
            }
            
            with open(self.permissions_file, 'w') as f:
                json.dump(data, f, indent=2)
            
            logger.debug("Permissions saved successfully")
        except Exception as e:
            logger.error(f"Error saving permissions: {str(e)}")

    def add_user(self, user: User) -> bool:
        """Add a new user to the system.

        Args:
            user: User to add

        Returns:
            True if successful, False if user already exists
        """
        if user.username in self.users:
            logger.warning(f"User {user.username} already exists")
            return False
        
        self.users[user.username] = user
        self.user_permissions[user.username] = set()
        self._save_permissions()
        
        logger.info(f"Added user: {user.username}")
        return True

    def remove_user(self, username: str) -> bool:
        """Remove a user from the system.

        Args:
            username: Username of the user to remove

        Returns:
            True if successful, False if user doesn't exist
        """
        if username not in self.users:
            logger.warning(f"User {username} does not exist")
            return False
        
        del self.users[username]
        if username in self.user_permissions:
            del self.user_permissions[username]
        
        self._save_permissions()
        
        logger.info(f"Removed user: {username}")
        return True

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

        Args:
            username: Username to look up

        Returns:
            User object or None if not found
        """
        return self.users.get(username)

    def list_users(self) -> List[User]:
        """List all users in the system.

        Returns:
            List of all users
        """
        return list(self.users.values())

    def grant_permission(self, username: str, permission: Permission) -> bool:
        """Grant a permission to a user.

        Args:
            username: Username of the user
            permission: Permission to grant

        Returns:
            True if successful, False if user doesn't exist
        """
        if username not in self.users:
            logger.warning(f"Cannot grant permission to non-existent user: {username}")
            return False
        
        if username not in self.user_permissions:
            self.user_permissions[username] = set()
        
        self.user_permissions[username].add(permission)
        self._save_permissions()
        
        logger.info(f"Granted {permission.action} permission on {permission.resource.type}:{permission.resource.path} to {username}")
        return True

    def revoke_permission(self, username: str, permission: Permission) -> bool:
        """Revoke a permission from a user.

        Args:
            username: Username of the user
            permission: Permission to revoke

        Returns:
            True if successful, False if user or permission doesn't exist
        """
        if username not in self.users:
            logger.warning(f"Cannot revoke permission from non-existent user: {username}")
            return False
        
        if username not in self.user_permissions:
            logger.warning(f"User {username} has no permissions")
            return False
        
        if permission in self.user_permissions[username]:
            self.user_permissions[username].remove(permission)
            self._save_permissions()
            
            logger.info(f"Revoked {permission.action} permission on {permission.resource.type}:{permission.resource.path} from {username}")
            return True
        
        logger.warning(f"Permission not found for user {username}")
        return False

    def check_permission(self, username: str, permission: Permission) -> bool:
        """Check if a user has a specific permission.

        Args:
            username: Username of the user
            permission: Permission to check

        Returns:
            True if user has the permission, False otherwise
        """
        if username not in self.users:
            logger.debug(f"Permission check failed: user {username} does not exist")
            return False
        
        if username not in self.user_permissions:
            logger.debug(f"Permission check failed: user {username} has no permissions")
            return False
        
        has_permission = permission in self.user_permissions[username]
        logger.debug(f"Permission check for {username}: {permission.action} on {permission.resource.type}:{permission.resource.path} = {has_permission}")
        return has_permission

    def list_user_permissions(self, username: str) -> List[Permission]:
        """List all permissions for a user.

        Args:
            username: Username of the user

        Returns:
            List of user's permissions
        """
        if username not in self.user_permissions:
            return []
        
        return list(self.user_permissions[username])

    def get_users_with_permission(self, permission: Permission) -> List[str]:
        """Get all users who have a specific permission.

        Args:
            permission: Permission to check

        Returns:
            List of usernames with the permission
        """
        users_with_permission = []
        for username, permissions in self.user_permissions.items():
            if permission in permissions:
                users_with_permission.append(username)
        
        return users_with_permission


# Utility functions for common operations
def check_permission(permission_system: UserPermissionSystem, username: str, permission: Permission) -> bool:
    """Check if a user has a specific permission.

    Args:
        permission_system: Permission system instance
        username: Username of the user
        permission: Permission to check

    Returns:
        True if user has the permission, False otherwise
    """
    return permission_system.check_permission(username, permission)


def grant_permission(permission_system: UserPermissionSystem, username: str, permission: Permission) -> bool:
    """Grant a permission to a user.

    Args:
        permission_system: Permission system instance
        username: Username of the user
        permission: Permission to grant

    Returns:
        True if successful, False otherwise
    """
    return permission_system.grant_permission(username, permission)


def revoke_permission(permission_system: UserPermissionSystem, username: str, permission: Permission) -> bool:
    """Revoke a permission from a user.

    Args:
        permission_system: Permission system instance
        username: Username of the user
        permission: Permission to revoke

    Returns:
        True if successful, False otherwise
    """
    return permission_system.revoke_permission(username, permission)


def create_default_permission_system(permissions_file: Path = Path("~/.trax/permissions.json").expanduser()) -> UserPermissionSystem:
    """Create a permission system with default configuration.

    Args:
        permissions_file: Path to the permissions file

    Returns:
        UserPermissionSystem instance
    """
    return UserPermissionSystem(permissions_file)
