from __future__ import annotations

import json
from urllib.parse import urlparse

import httpx

from .errors import (
    AuthenticationError,
    ConflictError,
    ForbiddenError,
    NotFoundError,
    RateLimitError,
    ServerError,
    ValidationError,
    WhatsrbCloudError,
)

VERSION = "0.1.0"
API_PREFIX = "/api/v1"


class Connection:
    def __init__(self, *, api_key: str, base_url: str, timeout: int):
        self._api_key = api_key
        self._base_url = base_url
        self._timeout = timeout

    def __repr__(self) -> str:
        return f"<Connection base_url={self._base_url!r} api_key=[FILTERED]>"

    def get(self, path: str) -> dict | None:
        return self._request("GET", path)

    def post(self, path: str, body: dict | None = None) -> dict | None:
        return self._request("POST", path, body)

    def patch(self, path: str, body: dict | None = None) -> dict | None:
        return self._request("PATCH", path, body)

    def delete(self, path: str) -> dict | None:
        return self._request("DELETE", path)

    def _request(self, method: str, path: str, body: dict | None = None) -> dict | None:
        url = f"{self._base_url}{API_PREFIX}{path}"
        self._enforce_https(url)

        headers = {
            "Authorization": f"Bearer {self._api_key}",
            "Content-Type": "application/json",
            "Accept": "application/json",
            "User-Agent": f"whatsrb-cloud-python/{VERSION}",
        }

        with httpx.Client(timeout=self._timeout) as client:
            response = client.request(
                method,
                url,
                headers=headers,
                content=json.dumps(body) if body else None,
            )

        return self._handle_response(response)

    def _enforce_https(self, url: str) -> None:
        parsed = urlparse(url)
        if parsed.scheme != "https" and parsed.hostname not in ("localhost", "127.0.0.1"):
            raise ValueError(f"Only HTTPS connections are allowed (got {parsed.scheme}://{parsed.hostname})")

    def _handle_response(self, response: httpx.Response) -> dict | None:
        body = self._parse_body(response.text)

        if response.is_success:
            return body

        self._raise_error(response.status_code, body, response)

    def _parse_body(self, raw: str) -> dict | None:
        if not raw:
            return None
        try:
            return json.loads(raw)
        except json.JSONDecodeError:
            return None

    def _raise_error(self, status: int, body: dict | None, response: httpx.Response) -> None:
        message = self._error_message(body)

        match status:
            case 401:
                raise AuthenticationError(message, status=status, body=body)
            case 403:
                raise ForbiddenError(message, status=status, body=body)
            case 404:
                raise NotFoundError(message, status=status, body=body)
            case 409:
                raise ConflictError(message, status=status, body=body)
            case 422:
                raise ValidationError(message, status=status, body=body)
            case 429:
                retry_after_header = response.headers.get("Retry-After")
                retry_after = int(retry_after_header) if retry_after_header else None
                raise RateLimitError(message, status=status, body=body, retry_after=retry_after)
            case s if 500 <= s < 600:
                raise ServerError(message, status=status, body=body)
            case _:
                raise WhatsrbCloudError(message, status=status, body=body)

    def _error_message(self, body: dict | None) -> str:
        if not isinstance(body, dict):
            return "Unknown error"
        return body.get("error") or body.get("message") or "Unknown error"
