# app/tools/dzengi_probe/rest_probe.py

from __future__ import annotations

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

from app.tools.dzengi_probe.config import DzengiProbeConfig
from app.tools.dzengi_probe.response_store import save_json


@dataclass(frozen=True)
class RestProbeResult:
    endpoint: str
    path: str
    output_file: str
    ok: bool
    error: str | None = None


class DzengiRestProbe:
    def __init__(self, config: DzengiProbeConfig) -> None:
        self.config = config

    def run_all(self) -> list[RestProbeResult]:
        return [
            self.probe_time(),
            self.probe_exchange_info(),
            self.probe_ticker24hr(),
            self.probe_depth(),
            self.probe_klines(),
            self.probe_agg_trades(),
        ]

    def probe_time(self) -> RestProbeResult:
        return self._get_and_save(
            endpoint="time",
            path=f"/api/{self.config.api_version}/time",
            params=None,
            output_file="rest/time/response.json",
        )

    def probe_exchange_info(self) -> RestProbeResult:
        return self._get_and_save(
            endpoint="exchangeInfo",
            path=f"/api/{self.config.api_version}/exchangeInfo",
            params=None,
            output_file="rest/exchangeInfo/all.json",
        )

    def probe_ticker24hr(self) -> RestProbeResult:
        return self._get_and_save(
            endpoint="ticker24hr",
            path=f"/api/{self.config.api_version}/ticker/24hr",
            params={"symbol": self.config.symbol},
            output_file=f"rest/ticker24hr/{self.config.symbol_file_name}.json",
        )

    def probe_depth(self) -> RestProbeResult:
        return self._get_and_save(
            endpoint="depth",
            path=f"/api/{self.config.api_version}/depth",
            params={
                "symbol": self.config.symbol,
                "limit": self.config.depth_limit,
            },
            output_file=f"rest/depth/{self.config.symbol_file_name}.json",
        )

    def probe_klines(self) -> RestProbeResult:
        return self._get_and_save(
            endpoint="klines",
            path=f"/api/{self.config.api_version}/klines",
            params={
                "symbol": self.config.symbol,
                "interval": self.config.interval,
                "limit": "10",
            },
            output_file=(
                f"rest/klines/"
                f"{self.config.symbol_file_name}_{self.config.interval}.json"
            ),
        )

    def probe_agg_trades(self) -> RestProbeResult:
        return self._get_and_save(
            endpoint="aggTrades",
            path=f"/api/{self.config.api_version}/aggTrades",
            params={
                "symbol": self.config.symbol,
                "limit": self.config.agg_trades_limit,
            },
            output_file=f"rest/aggTrades/{self.config.symbol_file_name}.json",
        )

    def _get_and_save(
        self,
        *,
        endpoint: str,
        path: str,
        params: dict[str, str] | None,
        output_file: str,
    ) -> RestProbeResult:
        url = self._build_url(path, params)

        request = Request(
            url=url,
            method="GET",
            headers={
                "Accept": "application/json",
                "User-Agent": "dzentra-dzengi-probe/1.0",
            },
        )

        try:
            with urlopen(request, timeout=self.config.timeout_seconds) as response:
                status = getattr(response, "status", 200)
                body = response.read().decode("utf-8")

            if status != 200:
                return RestProbeResult(
                    endpoint=endpoint,
                    path=path,
                    output_file=output_file,
                    ok=False,
                    error=f"Unexpected HTTP status: {status}",
                )

            payload = json.loads(body)
            output_path = self.config.output_dir / output_file
            save_json(output_path, payload)

            return RestProbeResult(
                endpoint=endpoint,
                path=path,
                output_file=str(output_path),
                ok=True,
            )

        except HTTPError as exc:
            error_body = ""
            try:
                error_body = exc.read().decode("utf-8")
            except Exception:
                pass

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

            return RestProbeResult(
                endpoint=endpoint,
                path=path,
                output_file=output_file,
                ok=False,
                error=message,
            )

        except URLError as exc:
            return RestProbeResult(
                endpoint=endpoint,
                path=path,
                output_file=output_file,
                ok=False,
                error=f"Network error: {exc.reason}",
            )

        except TimeoutError:
            return RestProbeResult(
                endpoint=endpoint,
                path=path,
                output_file=output_file,
                ok=False,
                error="Timeout while calling Dzengi API.",
            )

        except json.JSONDecodeError as exc:
            return RestProbeResult(
                endpoint=endpoint,
                path=path,
                output_file=output_file,
                ok=False,
                error=f"Non-JSON response: {exc}",
            )

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