# app/src/integrations/exchange/rest_client.py

from __future__ import annotations

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

from src.core.config import load_settings
from src.integrations.exchange.exceptions import (
    ExchangeConnectionError,
    ExchangeResponseError,
)


class ExchangeRestClient:
    def __init__(self) -> None:
        self.settings = load_settings()
        if not self.settings.exchange_base_url:
            raise ExchangeConnectionError("EXCHANGE_BASE_URL is empty.")
        self.base_url = self.settings.exchange_base_url.rstrip("/")
        self.timeout = self.settings.exchange_timeout_sec

    def get_payload(
        self,
        path: str,
        params: dict[str, str] | None = None,
        headers: dict[str, str] | None = None,
    ) -> object:
        query = f"?{urlencode(params)}" if params else ""
        url = f"{self.base_url}{path}{query}"

        request_headers = {
            "Accept": "application/json",
            "User-Agent": "dzentra-bot/2.0.0",
        }

        if headers:
            request_headers.update(headers)

        request = Request(
            url=url,
            method="GET",
            headers=request_headers,
        )

        try:
            with urlopen(request, timeout=self.timeout) as response:
                status = getattr(response, "status", 200)
                body = response.read().decode("utf-8")
        except HTTPError as exc:
            error_body = ""
            try:
                error_body = exc.read().decode("utf-8")
            except Exception:
                pass

            message = f"HTTP {exc.code} from exchange: {exc.reason}"
            if error_body:
                message += f" | body: {error_body}"

            raise ExchangeResponseError(message) from exc
        except URLError as exc:
            raise ExchangeConnectionError(
                f"Network error while calling exchange: {exc.reason}"
            ) from exc
        except TimeoutError as exc:
            raise ExchangeConnectionError("Timeout while calling exchange.") from exc

        if status != 200:
            raise ExchangeResponseError(f"Unexpected HTTP status: {status}")

        try:
            return json.loads(body)
        except json.JSONDecodeError as exc:
            raise ExchangeResponseError("Exchange returned non-JSON response.") from exc
        
    def get_json(
        self,
        path: str,
        params: dict[str, str] | None = None,
        headers: dict[str, str] | None = None,
    ) -> dict:
        query = f"?{urlencode(params)}" if params else ""
        url = f"{self.base_url}{path}{query}"

        request_headers = {
            "Accept": "application/json",
            "User-Agent": "dzentra-bot/2.0.0",
        }

        if headers:
            request_headers.update(headers)

        request = Request(
            url=url,
            method="GET",
            headers=request_headers,
        )

        try:
            with urlopen(request, timeout=self.timeout) as response:
                status = getattr(response, "status", 200)
                body = response.read().decode("utf-8")
        except HTTPError as exc:
            error_body = ""
            try:
                error_body = exc.read().decode("utf-8")
            except Exception:
                pass

            message = f"HTTP {exc.code} from exchange: {exc.reason}"
            if error_body:
                message += f" | body: {error_body}"

            raise ExchangeResponseError(message) from exc
        except URLError as exc:
            raise ExchangeConnectionError(
                f"Network error while calling exchange: {exc.reason}"
            ) from exc
        except TimeoutError as exc:
            raise ExchangeConnectionError("Timeout while calling exchange.") from exc

        if status != 200:
            raise ExchangeResponseError(f"Unexpected HTTP status: {status}")

        try:
            payload = json.loads(body)
        except json.JSONDecodeError as exc:
            raise ExchangeResponseError("Exchange returned non-JSON response.") from exc

        if not isinstance(payload, dict):
            raise ExchangeResponseError("Exchange response is not a JSON object.")

        return payload