"""zg_storage.rpc.__init__ module."""

from __future__ import annotations

import logging
import time
from dataclasses import dataclass
from typing import Any, Dict, Optional

import httpx


class RpcError(RuntimeError):
    """RpcError class.

    Purpose:
        SDK component type."""

    def __init__(self, message: str, code: Optional[int] = None, data: Any = None) -> None:
        super().__init__(message)
        self.code = code
        self.data = data


@dataclass
class RpcClient:
    """RpcClient class.

    Purpose:
        SDK component type."""

    url: str
    timeout: float = 30.0
    retries: int = 2
    retry_backoff: float = 0.5

    def _request_body(
        self, method: str, params: Optional[list] = None, request_id: int = 1
    ) -> Dict[str, Any]:
        return {
            "jsonrpc": "2.0",
            "id": request_id,
            "method": method,
            "params": params or [],
        }

    def call(self, method: str, params: Optional[list] = None) -> Any:
        """Perform a JSON-RPC call and return the result."""
        body = self._request_body(method, params)
        logger = logging.getLogger("zg_storage")
        summary = _summarize_params(params or [])
        if logger.isEnabledFor(logging.DEBUG):
            logger.debug("RPC call method=%s url=%s params=%s", method, self.url, summary)
        last_exc: Optional[Exception] = None
        max_attempts = max(1, self.retries + 1)
        for attempt in range(1, max_attempts + 1):
            try:
                try:
                    client_ctx = httpx.Client(timeout=self.timeout, trust_env=False)
                except TypeError:
                    client_ctx = httpx.Client(timeout=self.timeout)
                with client_ctx as client:
                    resp = client.post(self.url, json=body)
                resp.raise_for_status()
                payload = resp.json()
                if "error" in payload:
                    err = payload["error"]
                    raise RpcError(
                        err.get("message", "rpc error"),
                        err.get("code"),
                        err.get("data"),
                    )
                return payload.get("result")
            except httpx.TransportError as exc:
                last_exc = exc
                if method != "zgs_uploadSegmentsByTxSeq" or attempt >= max_attempts:
                    raise
                logger.warning(
                    "RPC transport error method=%s url=%s attempt=%s/%s err=%s",
                    method,
                    self.url,
                    attempt,
                    max_attempts,
                    exc,
                )
                time.sleep(self.retry_backoff * (2 ** (attempt - 1)))
        if last_exc:
            raise last_exc
        raise RuntimeError("RPC call failed without exception")


def _summarize_params(params: list) -> Any:
    """Summarize params for logging without dumping large payloads."""
    if not params:
        return []
    # Special-case upload segments for readability
    if isinstance(params, list) and len(params) == 2 and isinstance(params[0], list):
        segments = params[0]
        if segments and isinstance(segments[0], dict):
            first = segments[0]
            data = first.get("data", "")
            data_len = len(data) if isinstance(data, str) else None
            proof = first.get("proof", {}) if isinstance(first.get("proof"), dict) else {}
            lemma = proof.get("lemma", [])
            path = proof.get("path", [])
            return {
                "segments": len(segments),
                "first": {
                    "root": first.get("root"),
                    "index": first.get("index"),
                    "data_len": data_len,
                    "file_size": first.get("fileSize"),
                    "proof_lemma_len": len(lemma) if isinstance(lemma, list) else None,
                    "proof_path_len": len(path) if isinstance(path, list) else None,
                    "lemma_head": (lemma[0] if isinstance(lemma, list) and lemma else None),
                    "lemma_tail": (lemma[-1] if isinstance(lemma, list) and lemma else None),
                },
                "tx_seq": params[1],
            }
    # Default: avoid dumping huge blobs
    summarized = []
    for p in params:
        if isinstance(p, (str, int, float, bool)) or p is None:
            summarized.append(p)
        elif isinstance(p, list):
            summarized.append(f"list(len={len(p)})")
        elif isinstance(p, dict):
            summarized.append({k: "..." for k in list(p.keys())[:5]})
        else:
            summarized.append(type(p).__name__)
    return summarized
