"""Server-side scan quota check via the Oncecheck API."""

from __future__ import annotations

import json
import urllib.error
import urllib.request
from typing import TypedDict

from .. import __version__

WEB_APP_URL = "https://www.oncecheck.com"


class QuotaResult(TypedDict):
    allowed: bool
    plan: str
    used: int
    limit: int


class _PostRedirectHandler(urllib.request.HTTPRedirectHandler):
    """Follow 307/308 redirects while preserving the POST method and body."""

    def redirect_request(
        self, req, fp, code, msg, headers, newurl  # noqa: ANN001
    ):
        if code in (307, 308):
            new_req = urllib.request.Request(
                newurl,
                data=req.data,
                method=req.get_method(),
                headers=dict(req.headers),
            )
            return new_req
        return super().redirect_request(req, fp, code, msg, headers, newurl)


_opener = urllib.request.build_opener(_PostRedirectHandler)


def _call_scan_api(access_token: str, record: bool = False) -> QuotaResult:
    """Call the scan-check API. Set record=True to increment the counter."""
    url = f"{WEB_APP_URL}/api/scan-check"
    payload = json.dumps({"record": record}).encode()
    req = urllib.request.Request(
        url,
        data=payload,
        method="POST",
        headers={
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json",
            "User-Agent": f"oncecheck-cli/{__version__}",
        },
    )

    try:
        with _opener.open(req, timeout=10) as resp:
            return json.loads(resp.read())
    except urllib.error.HTTPError as exc:
        if exc.code == 401:
            raise PermissionError("Token expired or invalid. Run `oncecheck login` to re-authenticate.")
        body = exc.read().decode(errors="replace")
        raise ConnectionError(f"Scan check failed ({exc.code}): {body}")
    except (urllib.error.URLError, TimeoutError, OSError) as exc:
        raise ConnectionError(f"Could not reach Oncecheck server: {exc}")


def check_scan_quota(access_token: str) -> QuotaResult:
    """Check scan quota without incrementing the counter."""
    return _call_scan_api(access_token, record=False)


def record_scan(access_token: str) -> QuotaResult:
    """Record a completed scan (increments the counter)."""
    return _call_scan_api(access_token, record=True)
