from typing import Any, Dict, Iterable, List, Union, NamedTuple
import json

from .utils import compose

Deserialized = Union[Dict[str, Any], List[Dict[str, Any]]]


class Ok(NamedTuple):
    result: Any
    id: Any

    def __repr__(self) -> str:
        return f"Ok(result={self.result!r}, id={self.id!r})"


class Error(NamedTuple):
    code: int
    message: str
    data: Any
    id: Any

    def __repr__(self) -> str:
        return f"Error(code={self.code!r}, message={self.message!r}, data={self.data!r}, id={self.id!r})"


Response = Union[Ok, Error]


def to_result(response: Dict[str, Any]) -> Response:
    return (
        Ok(response["result"], response["id"])
        if "result" in response
        else Error(
            response["error"]["code"],
            response["error"]["message"],
            response["error"].get("data"),
            response["id"],
        )
    )


def parse(response: Deserialized) -> Union[Response, Iterable[Response]]:
    return (
        map(to_result, response) if isinstance(response, list) else to_result(response)
    )


parse_json = compose(parse, json.loads)
