from __future__ import annotations

import http.cookiejar
import json
import urllib.error
import urllib.parse
import urllib.request
from abc import ABC, abstractmethod

from juris_base.exceptions import JurisError, SessionError
from juris_base.models import JurisDocument, SearchResult

DEFAULT_UA = (
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
    "(KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36"
)


class BaseSessionClient(ABC):
    """Tier 2: Session cookie + XHR emulation. No CAPTCHA."""

    INIT_URL: str  # subclass must set
    user_agent: str = DEFAULT_UA
    timeout: int = 20

    def _make_opener(self) -> tuple[urllib.request.OpenerDirector, http.cookiejar.CookieJar]:
        jar = http.cookiejar.CookieJar()
        opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
        opener.addheaders = [("User-Agent", self.user_agent)]
        return opener, jar

    def _init_session(self, opener: urllib.request.OpenerDirector) -> None:
        """GET INIT_URL to establish session cookie."""
        try:
            opener.open(self.INIT_URL, timeout=self.timeout)
        except urllib.error.URLError as e:
            raise SessionError(f"Failed to init session: {e}") from e

    def _post_form(
        self,
        opener: urllib.request.OpenerDirector,
        url: str,
        data: dict,
        xhr: bool = False,
    ) -> dict | str:
        body = urllib.parse.urlencode(data).encode()
        headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded"}
        if xhr:
            headers["X-Requested-With"] = "XMLHttpRequest"
        req = urllib.request.Request(url, data=body, headers=headers)
        try:
            resp = opener.open(req, timeout=self.timeout)
            raw = resp.read().decode("utf-8", errors="replace")
            try:
                return json.loads(raw)
            except json.JSONDecodeError:
                return raw
        except urllib.error.URLError as e:
            raise JurisError(f"POST failed: {e}") from e

    def _get_json(
        self,
        opener: urllib.request.OpenerDirector,
        url: str,
        xhr: bool = False,
    ) -> dict:
        headers: dict[str, str] = {}
        if xhr:
            headers["X-Requested-With"] = "XMLHttpRequest"
        req = urllib.request.Request(url, headers=headers)
        try:
            resp = opener.open(req, timeout=self.timeout)
            return json.loads(resp.read().decode("utf-8"))
        except urllib.error.URLError as e:
            raise JurisError(f"GET failed: {e}") from e

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

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

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