from __future__ import annotations

import json
import urllib.error
import urllib.parse
import urllib.request
from typing import Any, Dict, Iterable, List

from src.errors import UserError


class PipedriveClient:
    def __init__(self, api_token: str, base_url: str) -> None:
        self._api_token = api_token
        self._base_url = base_url.rstrip("/")

    def iter_deals(self, *, limit: int = 500) -> Iterable[Dict[str, Any]]:
        yield from self._iter_collection("/deals", limit=limit)

    def list_deal_fields(self, *, limit: int = 500) -> List[Dict[str, Any]]:
        return list(self._iter_collection("/dealFields", limit=limit))

    def _iter_collection(self, path: str, *, limit: int) -> Iterable[Dict[str, Any]]:
        start = 0
        while True:
            resp = self._get_json(path, params={"start": start, "limit": limit})
            data = resp.get("data") or []
            for item in data:
                if isinstance(item, dict):
                    yield item
            pagination = _pagination(resp)
            if not pagination.get("more_items_in_collection"):
                return
            start = int(pagination.get("next_start") or (start + limit))

    def _get_json(self, path: str, params: Dict[str, Any]) -> Dict[str, Any]:
        url = _build_url(self._base_url, path, params, api_token=self._api_token)
        try:
            with urllib.request.urlopen(url, timeout=20) as resp:
                body = resp.read()
                payload = json.loads(body.decode("utf-8"))
        except urllib.error.HTTPError as exc:
            raise UserError(_http_error_message(exc)) from exc
        except urllib.error.URLError as exc:
            raise UserError(f"Network error while calling Pipedrive: {exc.reason}") from exc
        except json.JSONDecodeError as exc:
            raise UserError(f"Invalid JSON from Pipedrive: {exc.msg}") from exc

        if payload.get("success") is not True:
            err = payload.get("error") or payload.get("error_info") or "unknown error"
            raise UserError(f"Pipedrive API error: {err}")
        return payload


def _build_url(base_url: str, path: str, params: Dict[str, Any], *, api_token: str) -> str:
    merged = dict(params)
    merged["api_token"] = api_token
    query = urllib.parse.urlencode(merged)
    return f"{base_url}{path}?{query}"


def _pagination(resp: Dict[str, Any]) -> Dict[str, Any]:
    additional = resp.get("additional_data") or {}
    if not isinstance(additional, dict):
        return {}
    pagination = additional.get("pagination") or {}
    return pagination if isinstance(pagination, dict) else {}


def _http_error_message(exc: urllib.error.HTTPError) -> str:
    status = getattr(exc, "code", None)
    reason = getattr(exc, "reason", None) or "HTTP error"
    return f"Pipedrive HTTP error: {status} {reason}"
