"""Integration with Guacamole API for authentication and user management."""

import os
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional

import requests
import structlog

from .models import UserRole
from .permissions import PermissionChecker
from .session_storage import session_storage
from .utils import create_jwt_token

logger = structlog.get_logger(__name__)


class GuacamoleAuthenticator:
    """Class for authentication via Guacamole API."""

    def __init__(self) -> None:
        """
        Initialize Guacamole authenticator.

        Raises:
            ValueError: If system credentials are not set in environment variables.
        """
        self.base_url = os.getenv("GUACAMOLE_URL", "http://guacamole:8080")
        self.session = requests.Session()

        self._system_token: Optional[str] = None
        self._system_token_expires: Optional[datetime] = None

        self._system_username = os.getenv("SYSTEM_ADMIN_USERNAME")
        self._system_password = os.getenv("SYSTEM_ADMIN_PASSWORD")

        if not self._system_username or not self._system_password:
            raise ValueError(
                "SYSTEM_ADMIN_USERNAME and SYSTEM_ADMIN_PASSWORD environment "
                "variables are required. Set these in your .env or "
                "production.env file for security. Never use default "
                "credentials in production!"
            )
    
    def get_system_token(self) -> str:
        """
        Get system user token for administrative operations.

        Returns:
            System user token.

        Raises:
            RuntimeError: If system user authentication fails.
        """
        if (
            self._system_token is None
            or self._system_token_expires is None
            or self._system_token_expires <= datetime.now()
        ):
            logger.debug("Refreshing system token", username=self._system_username)

            auth_url = f"{self.base_url}/guacamole/api/tokens"
            auth_data = {
                "username": self._system_username,
                "password": self._system_password,
            }

            try:
                response = self.session.post(auth_url, data=auth_data, timeout=10)
                response.raise_for_status()

                auth_result = response.json()
                self._system_token = auth_result.get("authToken")

                if not self._system_token:
                    raise RuntimeError("No authToken in response")

                self._system_token_expires = datetime.now() + timedelta(hours=7)

                logger.info(
                    "System token refreshed successfully",
                    username=self._system_username,
                    expires_at=self._system_token_expires.isoformat(),
                )

            except requests.exceptions.RequestException as e:
                logger.error(
                    "Failed to authenticate system user",
                    username=self._system_username,
                    error=str(e),
                )
                raise RuntimeError(
                    f"Failed to authenticate system user: {e}"
                ) from e
            except Exception as e:
                logger.error(
                    "Unexpected error during system authentication",
                    username=self._system_username,
                    error=str(e),
                )
                raise

        return self._system_token

    def authenticate_user(
        self, username: str, password: str
    ) -> Optional[Dict[str, Any]]:
        """
        Authenticate user via Guacamole API.

        Args:
            username: Username in Guacamole.
            password: Password in Guacamole.

        Returns:
            Dictionary with user information or None if authentication fails.
        """
        auth_url = f"{self.base_url}/guacamole/api/tokens"
        auth_data = {"username": username, "password": password}

        try:
            logger.debug("Attempting user authentication", username=username)

            response = self.session.post(auth_url, data=auth_data, timeout=10)

            if response.status_code != 200:
                logger.info(
                    "Authentication failed",
                    username=username,
                    status_code=response.status_code,
                    response=response.text[:200],
                )
                return None

            auth_result = response.json()
            auth_token = auth_result.get("authToken")

            if not auth_token:
                logger.warning(
                    "No authToken in successful response",
                    username=username,
                    response=auth_result,
                )
                return None

            user_info = self.get_user_info(auth_token)
            if not user_info:
                logger.warning(
                    "Failed to get user info after authentication", username=username
                )
                return None

            system_permissions = user_info.get("systemPermissions", [])
            user_role = PermissionChecker.determine_role_from_permissions(
                system_permissions
            )

            result = {
                "username": username,
                "auth_token": auth_token,
                "role": user_role.value,
                "permissions": system_permissions,
                "full_name": user_info.get("fullName"),
                "email": user_info.get("emailAddress"),
                "organization": user_info.get("organization"),
                "organizational_role": user_info.get("organizationalRole"),
            }

            logger.info(
                "User authenticated successfully",
                username=username,
                role=user_role.value,
                permissions_count=len(system_permissions),
            )

            return result

        except requests.exceptions.RequestException as e:
            logger.error(
                "Network error during authentication", username=username, error=str(e)
            )
            return None
        except Exception as e:
            logger.error(
                "Unexpected error during authentication", username=username, error=str(e)
            )
            return None
    
    def get_user_info(self, auth_token: str) -> Optional[Dict[str, Any]]:
        """
        Get user information via Guacamole API.

        Args:
            auth_token: User authentication token.

        Returns:
            Dictionary with user information or None.
        """
        user_url = f"{self.base_url}/guacamole/api/session/data/postgresql/self"
        headers = {"Guacamole-Token": auth_token}

        try:
            response = self.session.get(user_url, headers=headers, timeout=10)

            if response.status_code != 200:
                logger.warning(
                    "Failed to get user info",
                    status_code=response.status_code,
                    response=response.text[:200],
                )
                return None

            user_data = response.json()
            username = user_data.get("username")

            if not username:
                logger.warning("No username in user info response")
                return None

            permissions_url = (
                f"{self.base_url}/guacamole/api/session/data/postgresql/"
                f"users/{username}/permissions"
            )

            try:
                perm_response = self.session.get(
                    permissions_url, headers=headers, timeout=10
                )

                if perm_response.status_code == 200:
                    permissions_data = perm_response.json()
                    system_permissions = permissions_data.get("systemPermissions", [])

                    logger.info(
                        "System permissions retrieved",
                        username=username,
                        system_permissions=system_permissions,
                        permissions_count=len(system_permissions),
                    )
                else:
                    logger.warning(
                        "Failed to get user permissions",
                        username=username,
                        status_code=perm_response.status_code,
                        response=perm_response.text[:200],
                    )
                    system_permissions = []

            except Exception as e:
                logger.warning(
                    "Error getting user permissions", username=username, error=str(e)
                )
                system_permissions = []

            user_data["systemPermissions"] = system_permissions

            attributes = user_data.get("attributes", {})
            user_data.update(
                {
                    "fullName": attributes.get("guac-full-name"),
                    "emailAddress": attributes.get("guac-email-address"),
                    "organization": attributes.get("guac-organization"),
                    "organizationalRole": attributes.get("guac-organizational-role"),
                }
            )

            logger.info(
                "User info retrieved successfully",
                username=username,
                system_permissions=system_permissions,
                permissions_count=len(system_permissions),
            )

            return user_data

        except requests.exceptions.RequestException as e:
            logger.error("Network error getting user info", error=str(e))
            return None
        except Exception as e:
            logger.error("Unexpected error getting user info", error=str(e))
            return None

    def create_jwt_for_user(self, user_info: Dict[str, Any]) -> str:
        """
        Create JWT token for user with session storage.

        Args:
            user_info: User information from authenticate_user.

        Returns:
            JWT token.
        """
        session_id = session_storage.create_session(
            user_info=user_info,
            guac_token=user_info["auth_token"],
            expires_in_minutes=int(
                os.getenv("JWT_ACCESS_TOKEN_EXPIRE_MINUTES", "60")
            ),
        )

        return create_jwt_token(user_info, session_id)

    def get_user_connections(self, auth_token: str) -> List[Dict[str, Any]]:
        """
        Get list of user connections.

        Args:
            auth_token: User authentication token.

        Returns:
            List of connections.
        """
        connections_url = (
            f"{self.base_url}/guacamole/api/session/data/postgresql/connections"
        )
        headers = {"Guacamole-Token": auth_token}

        try:
            response = self.session.get(connections_url, headers=headers, timeout=10)

            if response.status_code != 200:
                logger.warning(
                    "Failed to get user connections", status_code=response.status_code
                )
                return []

            connections_data = response.json()

            if isinstance(connections_data, dict):
                connections = list(connections_data.values())
            else:
                connections = connections_data

            logger.debug("Retrieved user connections", count=len(connections))

            return connections

        except requests.exceptions.RequestException as e:
            logger.error("Network error getting connections", error=str(e))
            return []
        except Exception as e:
            logger.error("Unexpected error getting connections", error=str(e))
            return []

    def create_connection_with_token(
        self, connection_config: Dict[str, Any], auth_token: str
    ) -> Optional[Dict[str, Any]]:
        """
        Create connection using user token.

        Args:
            connection_config: Connection configuration.
            auth_token: User authentication token.

        Returns:
            Information about created connection or None.
        """
        create_url = (
            f"{self.base_url}/guacamole/api/session/data/postgresql/connections"
        )
        headers = {
            "Content-Type": "application/json",
            "Guacamole-Token": auth_token,
        }

        try:
            response = self.session.post(
                create_url, headers=headers, json=connection_config, timeout=30
            )

            if response.status_code not in [200, 201]:
                logger.error(
                    "Failed to create connection",
                    status_code=response.status_code,
                    response=response.text[:500],
                )
                return None

            created_connection = response.json()
            connection_id = created_connection.get("identifier")

            if not connection_id:
                logger.error(
                    "No connection ID in response", response=created_connection
                )
                return None

            logger.info(
                "Connection created successfully",
                connection_id=connection_id,
                protocol=connection_config.get("protocol"),
                hostname=connection_config.get("parameters", {}).get("hostname"),
            )

            return created_connection

        except requests.exceptions.RequestException as e:
            logger.error("Network error creating connection", error=str(e))
            return None
        except Exception as e:
            logger.error("Unexpected error creating connection", error=str(e))
            return None

    def delete_connection_with_token(
        self, connection_id: str, auth_token: str
    ) -> bool:
        """
        Delete connection using user token.

        Args:
            connection_id: Connection ID to delete.
            auth_token: User authentication token.

        Returns:
            True if deletion successful, False otherwise.
        """
        delete_url = (
            f"{self.base_url}/guacamole/api/session/data/postgresql/"
            f"connections/{connection_id}"
        )
        headers = {"Guacamole-Token": auth_token}

        try:
            response = self.session.delete(delete_url, headers=headers, timeout=10)

            if response.status_code == 204:
                logger.info("Connection deleted successfully", connection_id=connection_id)
                return True

            logger.warning(
                "Failed to delete connection",
                connection_id=connection_id,
                status_code=response.status_code,
                response=response.text[:200],
            )
            return False

        except requests.exceptions.RequestException as e:
            logger.error(
                "Network error deleting connection",
                connection_id=connection_id,
                error=str(e),
            )
            return False
        except Exception as e:
            logger.error(
                "Unexpected error deleting connection",
                connection_id=connection_id,
                error=str(e),
            )
            return False

    def validate_token(self, auth_token: str) -> bool:
        """
        Validate Guacamole token.

        Args:
            auth_token: Token to validate.

        Returns:
            True if token is valid, False otherwise.
        """
        try:
            user_info = self.get_user_info(auth_token)
            return user_info is not None
        except Exception:
            return False

    def refresh_user_token(
        self, username: str, current_token: str
    ) -> Optional[str]:
        """
        Refresh user token (if supported by Guacamole).

        Args:
            username: Username.
            current_token: Current token.

        Returns:
            New token or None.
        """
        logger.debug(
            "Token refresh requested but not supported by Guacamole", username=username
        )
        return None 