"""HTTP client for JusCABA public API."""

import asyncio
import json
from datetime import datetime, timezone
from urllib.parse import quote

import httpx

from .config import BASE_URL, REQUEST_DELAY, REQUEST_TIMEOUT


def epoch_ms_to_iso(epoch_ms: int | None) -> str | None:
    """Convert epoch milliseconds to ISO 8601 string."""
    if epoch_ms is None:
        return None
    return datetime.fromtimestamp(epoch_ms / 1000, tz=timezone.utc).isoformat()


class JusCABAClient:
    """Async HTTP client for JusCABA judicial API with rate limiting."""

    def __init__(self):
        self._client = httpx.AsyncClient(timeout=REQUEST_TIMEOUT)
        self._last_request: float = 0

    async def _throttle(self):
        now = asyncio.get_event_loop().time()
        elapsed = now - self._last_request
        if elapsed < REQUEST_DELAY:
            await asyncio.sleep(REQUEST_DELAY - elapsed)
        self._last_request = asyncio.get_event_loop().time()

    async def get(self, endpoint: str, params: dict | None = None) -> dict | bytes:
        """GET request. Returns parsed JSON or raw bytes for binary responses."""
        await self._throttle()
        url = f"{BASE_URL}/{endpoint}"
        try:
            resp = await self._client.get(url, params=params)
            resp.raise_for_status()
            # Detect PDF by magic bytes (server lies about content-type)
            if resp.content[:5] == b"%PDF-":
                return resp.content
            content_type = resp.headers.get("content-type", "")
            if "application/pdf" in content_type or "octet-stream" in content_type:
                return resp.content
            if resp.status_code == 204:
                return {}
            text = resp.text.strip()
            if not text:
                return {}
            try:
                return resp.json()
            except (json.JSONDecodeError, UnicodeDecodeError):
                return {"raw": text}
        except httpx.HTTPStatusError as e:
            return {"error": f"HTTP {e.response.status_code}", "url": str(url)}
        except httpx.RequestError as e:
            return {"error": str(e), "url": str(url)}

    async def post_form(self, endpoint: str, data: str) -> dict:
        """POST with form-urlencoded data."""
        await self._throttle()
        url = f"{BASE_URL}/{endpoint}"
        try:
            resp = await self._client.post(
                url,
                content=data,
                headers={"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
            )
            resp.raise_for_status()
            return resp.json()
        except httpx.HTTPStatusError as e:
            return {"error": f"HTTP {e.response.status_code}", "url": str(url)}
        except httpx.RequestError as e:
            return {"error": str(e), "url": str(url)}

    # --- High-level API methods ---

    async def search(self, name: str, page: int = 0, size: int = 10) -> dict:
        """Search cases by person name."""
        filter_json = json.dumps({"identificador": name})
        info = json.dumps({
            "filter": filter_json,
            "tipoBusqueda": "PAR",
            "page": page,
            "size": size,
        })
        data = f"info={quote(info)}"
        return await self.post_form("expedientes/lista", data)

    async def case_header(self, exp_id: int) -> dict:
        """Get case header (CUIJ, carátula, estado)."""
        result = await self.get("expedientes/encabezado", {"expId": exp_id})
        if isinstance(result, dict) and "fechaInicio" in result:
            result["fechaInicio"] = epoch_ms_to_iso(result["fechaInicio"])
        return result

    async def case_details(self, exp_id: int) -> dict:
        """Get case full details (tribunal, objeto, ubicación, monto)."""
        result = await self.get("expedientes/ficha", {"expId": exp_id})
        if isinstance(result, dict) and "fechaInicio" in result:
            result["fechaInicio"] = epoch_ms_to_iso(result["fechaInicio"])
        return result

    async def case_fuero(self, exp_id: int) -> str:
        """Get case fuero (jurisdiction type)."""
        result = await self.get(
            "expedientes/fuero",
            {"expId": exp_id, "accesoMinisterios": "false"},
        )
        if isinstance(result, dict):
            return result.get("raw", str(result))
        return str(result)

    async def proceedings(
        self,
        exp_id: int,
        page: int = 0,
        size: int = 10,
        cedulas: bool = True,
        escritos: bool = True,
        despachos: bool = True,
        notas: bool = True,
    ) -> dict:
        """Get case proceedings (actuaciones)."""
        filtro = json.dumps({
            "cedulas": cedulas,
            "escritos": escritos,
            "despachos": despachos,
            "notas": notas,
            "expId": exp_id,
            "accesoMinisterios": False,
            "fechaNotificacionDesde": None,
            "fechaNotificacionHasta": None,
        })
        result = await self.get(
            "expedientes/actuaciones",
            {"filtro": filtro, "page": page, "size": size},
        )
        # Convert timestamps in content items
        if isinstance(result, dict) and "content" in result:
            for item in result["content"]:
                if "fechaFirma" in item:
                    item["fechaFirma"] = epoch_ms_to_iso(item["fechaFirma"])
                if "fechaPublicacion" in item:
                    item["fechaPublicacion"] = epoch_ms_to_iso(item["fechaPublicacion"])
        return result

    async def parties(self, exp_id: int, page: int = 0, size: int = 10) -> dict:
        """Get case parties (partes)."""
        return await self.get(
            "expedientes/partes",
            {"expId": exp_id, "accesoMinisterios": "false", "page": page, "size": size},
        )

    async def related_cases(self, exp_id: int, page: int = 0, size: int = 10) -> dict:
        """Get related cases."""
        return await self.get(
            "expedientes/relacionados",
            {"expId": exp_id, "accesoMinisterios": "false", "page": page, "size": size},
        )

    async def last_action(self, exp_id: int) -> dict:
        """Get last action on a case."""
        result = await self.get("expedientes/ultimaAccion", {"expId": exp_id})
        if isinstance(result, dict):
            action = result.get("ultimaAccion", {})
            if action and "fecha" in action:
                action["fecha"] = epoch_ms_to_iso(action["fecha"])
        return result

    async def has_sentence(self, exp_id: int) -> bool | dict:
        """Check if case has a sentence."""
        return await self.get("expedientes/tieneSentencia", {"expId": exp_id})

    async def list_attachments(self, act_id: int, exp_id: int) -> dict:
        """List attachments for a proceeding."""
        result = await self.get(
            "expedientes/actuaciones/adjuntos",
            {"actId": act_id, "expId": exp_id, "accesoMinisterios": "false"},
        )
        if isinstance(result, dict) and "adjuntos" in result:
            for adj in result["adjuntos"]:
                if "fecha" in adj:
                    adj["fecha"] = epoch_ms_to_iso(adj["fecha"])
        return result

    async def download_pdf(
        self, aac_id: int, exp_id: int, act_id: int, es_cedula: bool = False
    ) -> bytes | dict:
        """Download PDF attachment. Returns bytes on success, dict on error."""
        filter_json = json.dumps({
            "aacId": aac_id,
            "expId": exp_id,
            "actId": act_id,
            "ministerios": False,
            "esCedula": es_cedula,
        })
        return await self.get(
            "expedientes/actuaciones/adjuntoPdf",
            {"filter": filter_json},
        )

    async def close(self):
        await self._client.aclose()
