"""Base client for Keepeek API."""

import logging
from typing import Any, Dict, Optional, Union
import requests
from requests.auth import HTTPBasicAuth
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

from .exceptions import (
    APIError,
    AuthenticationError,
    AuthorizationError,
    APIConnectionError,
    RateLimitError,
    ResourceNotFoundError,
    ValidationError,
)

logger = logging.getLogger(__name__)


class KeepeekClient:
    """Base client for interacting with Keepeek REST API."""

    def __init__(
        self,
        base_url: str,
        token: str = None,
        username: str = None,
        password: str = None,
        timeout: int = 30,
        max_retries: int = 3,
        verify_ssl: bool = True,
    ):
        """
        Initialize Keepeek client.

        Args:
            base_url: Base URL of the Keepeek instance (e.g., 'https://example.keepeek.com')
            token: Authorization token for API access
            timeout: Request timeout in seconds (default: 30)
            max_retries: Maximum number of retry attempts (default: 3)
            verify_ssl: Whether to verify SSL certificates (default: True)
        """
        self.base_url = base_url.rstrip("/")
        self.token = token
        self.username = username
        self.password = password
        self.timeout = timeout
        self.verify_ssl = verify_ssl

        # Create session with retry strategy
        self.session = requests.Session()
        retry_strategy = Retry(
            total=max_retries,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["HEAD", "GET", "OPTIONS", "POST", "PUT", "DELETE"],
            backoff_factor=1,
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("http://", adapter)
        self.session.mount("https://", adapter)

        # Set default headers
        self.session.headers.update(
            {
                "Content-Type": "application/json",
            }
        )

        if token:
            self.session.headers["Authorization"] = f"Bearer {token}"

    def _build_url(self, endpoint: str) -> str:
        """Build full URL from endpoint."""
        endpoint = endpoint.lstrip("/")
        if not endpoint.startswith("api/"):
            endpoint = f"api/dam/{endpoint}"
        return f"{self.base_url}/{endpoint}"

    def _handle_response(self, response: requests.Response) -> Union[Dict[str, Any], list]:
        """
        Handle API response and raise appropriate exceptions.

        Args:
            response: Response object from requests

        Returns:
            Parsed JSON response

        Raises:
            AuthenticationError: For 401 errors
            AuthorizationError: For 403 errors
            ResourceNotFoundError: For 404 errors
            ValidationError: For 400 errors
            RateLimitError: For 429 errors
            APIError: For other error responses
        """
        try:
            logger.info(response.request.url)
            response_data = response.json() if response.content else {}
        except ValueError:
            response_data = {"message": response.text}

        if response.status_code < 299:
            return response_data
        if response.status_code == 204:
            return {}
        if response.status_code == 400:
            raise ValidationError(
                response_data.get("message", "Validation error"),
            )
        if response.status_code == 401:
            raise AuthenticationError(
                response_data.get("message", "Authentication failed"),
            )
        if response.status_code == 403:
            raise AuthorizationError(
                response_data.get("message", "Access forbidden"),
            )
        if response.status_code == 404:
            raise ResourceNotFoundError(
                response_data.get("message", "Resource not found"),
            )
        if response.status_code == 429:
            raise RateLimitError(
                response_data.get("message", "Rate limit exceeded"),
            )
        raise APIError(
            response_data.get("message", f"API error: {response.status_code}"),
            status_code=response.status_code,
            response=response_data,
        )

    def get(
        self,
        endpoint: str,
        params: Optional[Dict[str, Any]] = None,
        **kwargs: Any,
    ) -> Union[Dict[str, Any], list]:
        """
        Make GET request to API.

        Args:
            endpoint: API endpoint
            params: Query parameters
            **kwargs: Additional arguments for requests

        Returns:
            Response data
        """
        url = self._build_url(endpoint)
        try:
            if not self.token:
                kwargs["auth"] = HTTPBasicAuth(self.username, self.password)

            response = self.session.get(
                url,
                params=params,
                timeout=self.timeout,
                verify=self.verify_ssl,
                **kwargs,
            )
            return self._handle_response(response)
        except requests.exceptions.RequestException as e:
            raise APIConnectionError(f"Connection error: {str(e)}") from e

    def post(
        self,
        endpoint: str,
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None,
        **kwargs: Any,
    ) -> Union[Dict[str, Any], list]:
        """
        Make POST request to API.

        Args:
            endpoint: API endpoint
            data: Form data
            json: JSON data
            params: Query parameters
            **kwargs: Additional arguments for requests

        Returns:
            Response data
        """
        url = self._build_url(endpoint)
        try:
            if not self.token:
                kwargs["auth"] = HTTPBasicAuth(self.username, self.password)

            response = self.session.post(
                url,
                data=data,
                json=json,
                params=params,
                timeout=self.timeout,
                verify=self.verify_ssl,
                **kwargs,
            )
            return self._handle_response(response)
        except requests.exceptions.RequestException as e:
            raise APIConnectionError(f"Connection error: {str(e)}") from e

    def put(
        self,
        endpoint: str,
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None,
        **kwargs: Any,
    ) -> Union[Dict[str, Any], list]:
        """
        Make PUT request to API.

        Args:
            endpoint: API endpoint
            data: Form data
            json: JSON data
            params: Query parameters
            **kwargs: Additional arguments for requests

        Returns:
            Response data
        """
        url = self._build_url(endpoint)
        try:
            if not self.token:
                kwargs["auth"] = HTTPBasicAuth(self.username, self.password)

            response = self.session.put(
                url,
                data=data,
                json=json,
                params=params,
                timeout=self.timeout,
                verify=self.verify_ssl,
                **kwargs,
            )
            return self._handle_response(response)
        except requests.exceptions.RequestException as e:
            raise APIConnectionError(f"Connection error: {str(e)}") from e

    def delete(
        self,
        endpoint: str,
        params: Optional[Dict[str, Any]] = None,
        **kwargs: Any,
    ) -> Union[Dict[str, Any], list]:
        """
        Make DELETE request to API.

        Args:
            endpoint: API endpoint
            params: Query parameters
            **kwargs: Additional arguments for requests

        Returns:
            Response data
        """
        url = self._build_url(endpoint)
        try:
            if not self.token:
                kwargs["auth"] = HTTPBasicAuth(self.username, self.password)

            response = self.session.delete(
                url,
                params=params,
                timeout=self.timeout,
                verify=self.verify_ssl,
                **kwargs,
            )
            return self._handle_response(response)
        except requests.exceptions.RequestException as e:
            raise APIConnectionError(f"Connection error: {str(e)}") from e

    def close(self) -> None:
        """Close the session."""
        self.session.close()

    def __enter__(self) -> "KeepeekClient":
        """Context manager entry."""
        return self

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        """Context manager exit."""
        self.close()
