"""
HTTP client for the Lynara SDK.
Handles requests, retries, error parsing, and rate limiting.
"""

import time
import logging
from typing import Optional, Dict, Any
from urllib.parse import urljoin

try:
    import httpx
    _HAS_HTTPX = True
except ImportError:
    _HAS_HTTPX = False

import requests as _requests

from .exceptions import (
    LynaraError,
    AuthenticationError,
    PermissionError,
    NotFoundError,
    RateLimitError,
    ValidationError,
    ServerError,
)

logger = logging.getLogger("lynara")


class HttpClient:
    """Low-level HTTP client with retry and error handling."""

    def __init__(
        self,
        base_url: str,
        api_key: str,
        company_id: str = "",
        timeout: int = 30,
        max_retries: int = 3,
        retry_delay: float = 1.0,
    ):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.company_id = company_id
        self.timeout = timeout
        self.max_retries = max_retries
        self.retry_delay = retry_delay

        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
            "User-Agent": "lynara-python-sdk/1.0.1",
        }
        if company_id:
            self.headers["X-Company-Id"] = company_id

        # Use httpx if available (async support), fallback to requests
        if _HAS_HTTPX:
            self._session = httpx.Client(
                base_url=self.base_url,
                headers=self.headers,
                timeout=timeout,
            )
        else:
            self._session = _requests.Session()
            self._session.headers.update(self.headers)

    def _build_url(self, path: str) -> str:
        """Build full URL from path."""
        if _HAS_HTTPX:
            return path  # httpx uses base_url
        return f"{self.base_url}{path}"

    def request(
        self,
        method: str,
        path: str,
        params: Optional[Dict] = None,
        json: Optional[Any] = None,
        **kwargs,
    ) -> Any:
        """Execute an HTTP request with retry logic."""
        url = self._build_url(path)

        for attempt in range(self.max_retries + 1):
            try:
                if _HAS_HTTPX:
                    response = self._session.request(
                        method, url, params=params, json=json, **kwargs
                    )
                    status_code = response.status_code
                    response_text = response.text
                else:
                    response = self._session.request(
                        method, url, params=params, json=json, timeout=self.timeout, **kwargs
                    )
                    status_code = response.status_code
                    response_text = response.text

                # Parse response
                try:
                    data = response.json()
                except Exception:
                    data = {"detail": response_text}

                # Handle errors
                if status_code == 401:
                    raise AuthenticationError(
                        data.get("detail", "Authentication failed"),
                        status_code=401,
                    )
                elif status_code == 403:
                    raise PermissionError(
                        data.get("detail", "Permission denied"),
                        status_code=403,
                    )
                elif status_code == 404:
                    raise NotFoundError(
                        data.get("detail", "Not found"),
                        status_code=404,
                    )
                elif status_code == 422:
                    raise ValidationError(
                        data.get("detail", "Validation error"),
                        status_code=422,
                    )
                elif status_code == 429:
                    retry_after = int(response.headers.get("Retry-After", 60))
                    if attempt < self.max_retries:
                        logger.warning(
                            "Rate limited, retrying in %ds (attempt %d/%d)",
                            retry_after, attempt + 1, self.max_retries,
                        )
                        time.sleep(retry_after)
                        continue
                    raise RateLimitError(
                        "Rate limit exceeded",
                        retry_after=retry_after,
                    )
                elif status_code >= 500:
                    if attempt < self.max_retries:
                        delay = self.retry_delay * (2 ** attempt)
                        logger.warning(
                            "Server error %d, retrying in %.1fs (attempt %d/%d)",
                            status_code, delay, attempt + 1, self.max_retries,
                        )
                        time.sleep(delay)
                        continue
                    raise ServerError(
                        data.get("detail", f"Server error {status_code}"),
                        status_code=status_code,
                    )
                elif status_code >= 400:
                    raise LynaraError(
                        data.get("detail", f"Error {status_code}"),
                        status_code=status_code,
                    )

                return data

            except (AuthenticationError, PermissionError, NotFoundError, ValidationError):
                raise  # Don't retry client errors
            except (RateLimitError, ServerError):
                raise  # Already handled retry
            except LynaraError:
                raise
            except Exception as e:
                if attempt < self.max_retries:
                    delay = self.retry_delay * (2 ** attempt)
                    logger.warning(
                        "Request failed: %s, retrying in %.1fs (attempt %d/%d)",
                        str(e), delay, attempt + 1, self.max_retries,
                    )
                    time.sleep(delay)
                    continue
                raise LynaraError(f"Request failed after {self.max_retries} retries: {str(e)}")

    def get(self, path: str, params: Optional[Dict] = None, **kwargs) -> Any:
        return self.request("GET", path, params=params, **kwargs)

    def post(self, path: str, json: Optional[Any] = None, **kwargs) -> Any:
        return self.request("POST", path, json=json, **kwargs)

    def put(self, path: str, json: Optional[Any] = None, **kwargs) -> Any:
        return self.request("PUT", path, json=json, **kwargs)

    def delete(self, path: str, **kwargs) -> Any:
        return self.request("DELETE", path, **kwargs)

    def close(self):
        """Close the underlying HTTP session."""
        if hasattr(self._session, "close"):
            self._session.close()
