"""KargoJet Partner API v1 - zero-dependency Python client (Python 3.10+)."""

from __future__ import annotations

import json
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request, urlopen


class KargoJetApiError(RuntimeError):
    def __init__(self, status: int, body: object) -> None:
        detail = body.get("detail") if isinstance(body, dict) else None
        super().__init__(detail or f"KargoJet API HTTP {status}")
        self.status = status
        self.body = body


class KargoJetClient:
    def __init__(
        self,
        api_token: str,
        base_url: str = "https://api.kargojet.com/partner-api/v1",
    ) -> None:
        if not api_token:
            raise ValueError("api_token is required")
        self.api_token = api_token
        self.base_url = base_url.rstrip("/")

    def request(
        self,
        path: str,
        *,
        method: str = "GET",
        body: dict | None = None,
        idempotency_key: str | None = None,
    ) -> object:
        headers = {
            "Accept": "application/json",
            "Authorization": f"Bearer {self.api_token}",
        }
        data = None
        if body is not None:
            headers["Content-Type"] = "application/json"
            data = json.dumps(body).encode("utf-8")
        if idempotency_key:
            headers["Idempotency-Key"] = idempotency_key

        request = Request(
            f"{self.base_url}{path}",
            data=data,
            headers=headers,
            method=method,
        )
        try:
            with urlopen(request, timeout=30) as response:
                raw = response.read().decode("utf-8")
                return json.loads(raw) if raw else None
        except HTTPError as error:
            raw = error.read().decode("utf-8")
            try:
                parsed = json.loads(raw) if raw else None
            except json.JSONDecodeError:
                parsed = {"raw": raw}
            raise KargoJetApiError(error.code, parsed) from error

    def create_shipment(self, payload: dict, idempotency_key: str | None = None) -> object:
        return self.request(
            "/shipments",
            method="POST",
            body=payload,
            idempotency_key=idempotency_key,
        )

    def list_shipments(
        self,
        *,
        q: str | None = None,
        status: str | None = None,
        limit: int = 100,
        offset: int = 0,
    ) -> object:
        params = {"limit": limit, "offset": offset}
        if q:
            params["q"] = q
        if status:
            params["status"] = status
        return self.request(f"/shipments?{urlencode(params)}")

    def get_shipment(self, shipment_id: str) -> object:
        return self.request(f"/shipments/{shipment_id}")

    def list_shipment_packages(self, shipment_id: str) -> object:
        return self.request(f"/shipments/{shipment_id}/packages")

    def update_shipment_packages(
        self,
        shipment_id: str,
        packages: list[dict],
        idempotency_key: str | None = None,
    ) -> object:
        return self.request(
            f"/shipments/{shipment_id}/packages",
            method="PUT",
            body={"packages": packages},
            idempotency_key=idempotency_key,
        )

    def get_shipment_package_status_history(
        self,
        shipment_id: str,
        package_id: str,
    ) -> object:
        return self.request(
            f"/shipments/{shipment_id}/packages/{package_id}/status-history"
        )

    def list_shipment_labels(
        self,
        shipment_id: str,
        package_id: str | None = None,
    ) -> object:
        query = f"?package_id={package_id}" if package_id else ""
        return self.request(f"/shipments/{shipment_id}/labels{query}")

    def quote_shipment(self, shipment_id: str) -> object:
        return self.request(
            "/shipments/quote",
            method="POST",
            body={"shipment_id": shipment_id},
        )

    def choose_carrier(self, payload: dict, idempotency_key: str | None = None) -> object:
        return self.request(
            "/shipments/choose-carrier",
            method="POST",
            body=payload,
            idempotency_key=idempotency_key,
        )

    def submit_shipment(self, shipment_id: str, idempotency_key: str | None = None) -> object:
        return self.request(
            f"/shipments/{shipment_id}/submit",
            method="POST",
            idempotency_key=idempotency_key,
        )

    def create_label(self, shipment_id: str) -> object:
        return self.request(f"/shipments/{shipment_id}/labels", method="POST")
