# scraper2_hj3415/app/adapters/out/dart/dart_http_client.py
from __future__ import annotations

import asyncio
from typing import Any

import httpx
from scraper2_hj3415.app.dart.adapters.out.dto.dart_list_page_dto import (
    DartListPageDto,
)
from scraper2_hj3415.app.dart.adapters.out.errors import (
    DartApiResponseError,
    DartConnectionError,
)
from scraper2_hj3415.app.dart.ports.dart.dart_client_port import DartClientPort


class DartHttpClient(DartClientPort):
    """
    OpenDART list.json 1페이지 조회 어댑터.
    전체 페이지 순회는 usecase 책임이다.
    """
    _api_key: str
    _timeout: float
    _page_count: int
    _max_retries: int
    _user_agent: str

    BASE_URL:str = "https://opendart.fss.or.kr/api/list.json"

    def __init__(
        self,
        *,
        api_key: str,
        timeout: float = 30.0,
        page_count: int = 100,
        max_retries: int = 3,
        user_agent: str = "Mozilla/5.0",
    ) -> None:
        self._api_key = api_key
        self._timeout = timeout
        self._page_count = page_count
        self._max_retries = max_retries
        self._user_agent = user_agent

    async def list_reports(
        self,
        *,
        sdate: str | None,
        edate: str | None,
        corp_code: str | None,
        page_no: int,
    ) -> DartListPageDto:
        params = self._build_params(
            sdate=sdate,
            edate=edate,
            corp_code=corp_code,
            page_no=page_no,
        )

        payload = await self._get_json(params=params)
        page = DartListPageDto.from_dict(payload)

        if page.status == "013":
            return page

        if page.status != "000":
            raise DartApiResponseError(
                f"OpenDART error status={page.status}, message={page.message}"
            )

        return page

    def _build_params(
        self,
        *,
        sdate: str | None,
        edate: str | None,
        corp_code: str | None,
        page_no: int,
    ) -> dict[str, str | int]:
        params: dict[str, str | int] = {
            "crtfc_key": self._api_key,
            "last_reprt_at": "N",
            "page_no": page_no,
            "page_count": self._page_count,
        }
        if sdate:
            params["bgn_de"] = sdate
        if edate:
            params["end_de"] = edate
        if corp_code:
            params["corp_code"] = corp_code
        return params

    async def _get_json(self, *, params: dict[str, str | int]) -> dict[str, Any]:
        headers = {"User-Agent": self._user_agent}

        for attempt in range(1, self._max_retries + 1):
            try:
                async with httpx.AsyncClient(timeout=self._timeout) as client:
                    response = await client.get(
                        self.BASE_URL,
                        params=params,
                        headers=headers,
                    )
                    _ = response.raise_for_status()
                    data = response.json()
                    if not isinstance(data, dict):
                        raise DartConnectionError(
                            "OpenDART 응답 JSON 형식이 올바르지 않습니다."
                        )
                    return data

            except (httpx.HTTPError, httpx.TimeoutException) as exc:
                if attempt >= self._max_retries:
                    raise DartConnectionError(str(exc)) from exc
                await asyncio.sleep(1.0)

        raise DartConnectionError("알 수 없는 OpenDART 연결 오류")
