"""HTTP Client Protocol for Git Service Integrations."""

from abc import ABC, abstractmethod
from typing import Any

from httpx import AsyncClient, HTTPError, HTTPStatusError
from pydantic import SecretStr

from openhands.core.logger import openhands_logger as logger
from openhands.integrations.service_types import (
    AuthenticationError,
    RateLimitError,
    RequestMethod,
    ResourceNotFoundError,
    UnknownException,
)


class HTTPClient(ABC):
    """Abstract base class defining the HTTP client interface for Git service integrations.

    This class abstracts the common HTTP client functionality needed by all
    Git service providers (GitHub, GitLab, BitBucket) while keeping inheritance in place.
    """

    # Default attributes (subclasses may override)
    token: SecretStr = SecretStr('')
    refresh: bool = False
    external_auth_id: str | None = None
    external_auth_token: SecretStr | None = None
    external_token_manager: bool = False
    base_domain: str | None = None

    # Provider identification must be implemented by subclasses
    @property
    @abstractmethod
    def provider(self) -> str: ...

    # Abstract methods that concrete classes must implement
    @abstractmethod
    async def get_latest_token(self) -> SecretStr | None:
        """Get the latest working token for the service."""
        ...

    @abstractmethod
    async def _get_headers(self) -> dict[str, Any]:
        """Get HTTP headers for API requests."""
        ...

    @abstractmethod
    async def _make_request(
        self,
        url: str,
        params: dict | None = None,
        method: RequestMethod = RequestMethod.GET,
    ) -> tuple[Any, dict]:
        """Make an HTTP request to the Git service API."""
        ...

    def _has_token_expired(self, status_code: int) -> bool:
        """Check if the token has expired based on HTTP status code."""
        return status_code == 401

    async def execute_request(
        self,
        client: AsyncClient,
        url: str,
        headers: dict,
        params: dict | None,
        method: RequestMethod = RequestMethod.GET,
    ):
        """Execute an HTTP request using the provided client."""
        if method == RequestMethod.POST:
            return await client.post(url, headers=headers, json=params)
        return await client.get(url, headers=headers, params=params)

    def handle_http_status_error(
        self, e: HTTPStatusError
    ) -> (
        AuthenticationError | RateLimitError | ResourceNotFoundError | UnknownException
    ):
        """Handle HTTP status errors and convert them to appropriate exceptions."""
        if e.response.status_code == 401:
            return AuthenticationError(f'Invalid {self.provider} token')
        elif e.response.status_code == 404:
            return ResourceNotFoundError(
                f'Resource not found on {self.provider} API: {e}'
            )
        elif e.response.status_code == 429:
            logger.warning(f'Rate limit exceeded on {self.provider} API: {e}')
            return RateLimitError(f'{self.provider} API rate limit exceeded')

        logger.warning(f'Status error on {self.provider} API: {e}')
        return UnknownException(f'Unknown error: {e}')

    def handle_http_error(self, e: HTTPError) -> UnknownException:
        """Handle general HTTP errors."""
        logger.warning(f'HTTP error on {self.provider} API: {type(e).__name__} : {e}')
        return UnknownException(f'HTTP error {type(e).__name__} : {e}')
