from __future__ import annotations

from abc import abstractmethod
from typing import Any
from uuid import UUID

import httpx

from src.connectors.base import BaseInventoryConnector, ConnectorResult
from src.connectors.sdk.auth import AuthHandler, BearerAuthHandler
from src.connectors.sdk.circuit_breaker import CircuitBreaker
from src.connectors.sdk.context import ConnectorContext
from src.connectors.sdk.retry import RetryPolicy, with_retry
from src.core.exceptions import ConnectorException
from src.core.logging import get_logger
from src.core.metrics import CONNECTOR_OPERATIONS

logger = get_logger("sdk_connector")


class SDKConnector(BaseInventoryConnector):
    connector_type: str = "sdk"

    def __init__(self) -> None:
        self._context: ConnectorContext | None = None
        self._auth_handler: AuthHandler = BearerAuthHandler()
        self._retry_policy: RetryPolicy = RetryPolicy()
        self._circuit_breaker: CircuitBreaker = CircuitBreaker(name=self.connector_type)
        self._http_client: httpx.AsyncClient | None = None
        self._base_url: str = ""
        self._timeout: float = 30.0

    def configure(self, config: dict[str, Any]) -> None:
        self._base_url = config.get("base_url", "")
        self._timeout = config.get("timeout", 30.0)
        auth_type = config.get("auth_type", "bearer")
        if auth_type == "basic":
            from src.connectors.sdk.auth import BasicAuthHandler
            self._auth_handler = BasicAuthHandler()
        elif auth_type == "api_key":
            from src.connectors.sdk.auth import APIKeyAuthHandler
            self._auth_handler = APIKeyAuthHandler(
                header_name=config.get("api_key_header", "X-API-Key"),
                key_secret=config.get("api_key_secret_key", "api_key"),
            )
        elif auth_type == "oauth2":
            from src.connectors.sdk.auth import OAuth2Handler
            self._auth_handler = OAuth2Handler()

        cb_config = config.get("circuit_breaker", {})
        if cb_config:
            self._circuit_breaker = CircuitBreaker(
                name=self.connector_type,
                failure_threshold=cb_config.get("failure_threshold", 5),
                recovery_timeout=cb_config.get("recovery_timeout", 30.0),
            )

        retry_config = config.get("retry", {})
        if retry_config:
            self._retry_policy = RetryPolicy(
                max_attempts=retry_config.get("max_attempts", 3),
                base_delay=retry_config.get("base_delay", 1.0),
                max_delay=retry_config.get("max_delay", 30.0),
            )

    def set_context(self, context: ConnectorContext) -> None:
        self._context = context
        if context.config:
            self.configure(context.config)

    @property
    def http_client(self) -> httpx.AsyncClient:
        if self._http_client is None or self._http_client.is_closed:
            self._http_client = httpx.AsyncClient(
                base_url=self._base_url,
                timeout=self._timeout,
            )
        return self._http_client

    async def _request(
        self,
        method: str,
        path: str,
        *,
        json: dict[str, Any] | None = None,
        params: dict[str, Any] | None = None,
        headers: dict[str, str] | None = None,
    ) -> httpx.Response:
        if not self._context:
            raise ConnectorException(self.connector_type, "Connector context not set. Call set_context() first.")

        async def _do_request() -> httpx.Response:
            request = self.http_client.build_request(method, path, json=json, params=params, headers=headers)
            request = await self._auth_handler.apply(request, self._context)
            response = await self.http_client.send(request)
            response.raise_for_status()
            return response

        return await self._circuit_breaker.call(
            with_retry,
            _do_request,
            policy=self._retry_policy,
            connector_type=self.connector_type,
            operation=f"{method}:{path}",
        )

    def _record_metric(self, operation: str, status: str) -> None:
        CONNECTOR_OPERATIONS.labels(connector_type=self.connector_type, operation=operation, status=status).inc()

    async def close(self) -> None:
        if self._http_client and not self._http_client.is_closed:
            await self._http_client.aclose()
            self._http_client = None

    @abstractmethod
    async def get_stock(self, pharmacy_id: UUID, medication_id: UUID | None = None, nregistro: str | None = None) -> ConnectorResult:
        ...

    @abstractmethod
    async def reserve(self, pharmacy_id: UUID, medication_id: UUID, quantity: int, idempotency_key: str | None = None) -> ConnectorResult:
        ...

    @abstractmethod
    async def cancel_reservation(self, pharmacy_id: UUID, reservation_id: str) -> ConnectorResult:
        ...

    @abstractmethod
    async def pharmacy_information(self, pharmacy_id: UUID) -> ConnectorResult:
        ...

    @abstractmethod
    async def synchronize(self, pharmacy_id: UUID, strategy: str = "incremental", since: str | None = None) -> ConnectorResult:
        ...

    @abstractmethod
    async def heartbeat(self, pharmacy_id: UUID) -> ConnectorResult:
        ...
