from __future__ import annotations

import re
import urllib.error
import urllib.parse
import urllib.request
from abc import ABC, abstractmethod

from juris_base.exceptions import JurisError
from juris_base.models import JurisDocument
from juris_base.utils.aspnet import extract_tokens

DEFAULT_UA = "Mozilla/5.0 (compatible; juris-mcp/1.0)"


class BaseAspNetClient(ABC):
    """Tier 3: ASP.NET WebForms with ViewState/EventValidation tokens."""

    SEARCH_URL: str  # subclass must set
    user_agent: str = DEFAULT_UA
    timeout: int = 30

    def _fetch(self, url: str, data: dict | None = None, cookies: str = "") -> tuple[str, str]:
        headers = {"User-Agent": self.user_agent}
        if cookies:
            headers["Cookie"] = cookies
        if data is not None:
            body = urllib.parse.urlencode(data).encode("utf-8")
            headers["Content-Type"] = "application/x-www-form-urlencoded"
            req = urllib.request.Request(url, data=body, headers=headers)
        else:
            req = urllib.request.Request(url, headers=headers)
        try:
            resp = urllib.request.urlopen(req, timeout=self.timeout)
            html = resp.read().decode("utf-8", errors="replace")
            raw_cookies = resp.headers.get("Set-Cookie", "")
            cookie_str = "; ".join(
                c.split(";")[0] for c in raw_cookies.split(", ")
                if "=" in c.split(";")[0]
            ) if raw_cookies else cookies
            return html, cookie_str
        except urllib.error.HTTPError as e:
            raise JurisError(f"HTTP {e.code} from {url}") from e
        except urllib.error.URLError as e:
            raise JurisError(f"Connection error: {e.reason}") from e

    def _extract_tokens(self, html: str) -> dict[str, str]:
        return extract_tokens(html)

    def _get_total(self, html: str) -> int:
        m = re.search(r"Resultado:\s*\d+\s*de\s*(\d+)", html)
        return int(m.group(1)) if m else 0

    @abstractmethod
    def search(self, query: str, **filters) -> dict:
        ...

    @abstractmethod
    def get(self, id: str) -> JurisDocument:
        ...

    def download(self, id: str) -> bytes:
        raise NotImplementedError
