from __future__ import annotations

import json
import urllib.error
import urllib.request
from dataclasses import dataclass
from typing import Any, Dict

from src.errors import UserError


@dataclass(frozen=True)
class PublishResponse:
    status_code: int
    payload: Dict[str, Any]


class GbpClient:
    def __init__(self, *, base_url: str, account_id: str, access_token: str) -> None:
        self._base_url = str(base_url).rstrip("/")
        self._account_id = str(account_id).strip()
        self._access_token = str(access_token).strip()

    def create_local_post(self, *, location_id: str, summary: str) -> PublishResponse:
        url = f"{self._base_url}/accounts/{self._account_id}/locations/{location_id}/localPosts"
        body = json.dumps({"summary": str(summary)}, ensure_ascii=True).encode("utf-8")
        req = urllib.request.Request(url, data=body, method="POST")
        req.add_header("Authorization", f"Bearer {self._access_token}")
        req.add_header("Content-Type", "application/json")
        req.add_header("Accept", "application/json")

        try:
            with urllib.request.urlopen(req, timeout=30) as resp:
                raw = resp.read().decode("utf-8")
                payload = json.loads(raw) if raw.strip() else {}
                if not isinstance(payload, dict):
                    raise UserError("GBP API returned non-object JSON.")
                return PublishResponse(
                    status_code=int(getattr(resp, "status", 200)), payload=payload
                )
        except urllib.error.HTTPError as exc:
            msg = _read_http_error_body(exc)
            raise UserError(f"GBP HTTP error: {exc.code} {exc.reason} ({msg})") from exc
        except urllib.error.URLError as exc:
            raise UserError(f"Network error while calling GBP: {exc.reason}") from exc
        except json.JSONDecodeError as exc:
            raise UserError(f"Invalid JSON from GBP: {exc.msg}") from exc


def _read_http_error_body(exc: urllib.error.HTTPError) -> str:
    try:
        body = exc.read().decode("utf-8", errors="replace")
    except Exception:
        return "no body"
    text = (body or "").strip()
    return text[:300] if text else "no body"
