#!/usr/bin/env python3
# Copyright 2004-present Facebook. All Rights Reserved.

import base64
import json
import pathlib
from collections import deque
from typing import Any, Dict, List, Tuple

from ..abc import Subscription


class RecordSubscription:
    """A wrapper class for recording E2 messages from aiomsa.abc.Subscription
    types. When passed to the RecordSubscription constructor, instances of
    aiomsa.abc.Subscription are wrapped and the returned RecordSubscription
    instance may be used identically to the original Subscription instance. The
    RecordSubscription instance has additional functionality: the ability to
    dump the entire history of E2 messages received to a dict or JSON string.

    Messages are stored in a FIFO queue, and old messages are dequeued when the
    queue reaches maxlen.

    Args:
        sub: An instance of a aiomsa.abc.Subscription derived class.
        maxlen: The maximum number of E2 messages stored.
    """

    ATTRS = [
        "_messages",
        "_sub",
    ]

    def __init__(self, sub: Subscription, maxlen: int = 200) -> None:
        self._sub = sub
        self._messages = deque([], maxlen)

    async def __anext__(self) -> Tuple[bytes, bytes]:
        ret = await self._sub.__anext__()
        self._record(*ret)
        return ret

    def __aiter__(self) -> "RecordSubscription":
        return self

    def _record(self, header: bytes, message: bytes) -> None:
        """Save an E2 message.

        Args:
            header: E2 message header
            message: E2 message body
        """
        self._messages.append(
            [
                {
                    "header": base64.b64encode(header).decode("ascii"),
                    "message": base64.b64encode(message).decode("ascii"),
                }
            ]
        )

    def to_dict(self) -> Dict[str, Any]:
        """Dump recorded messages to a dict comprehensible to aiomsa.mock.

        Returns:
            A dict containing a "node" object comprehensible to aiomsa.mock.e2.
        """
        return {
            "e2_node_id": self.id,
            "streams": [{"messages": list(self._messages)}],
        }

    def dump(self) -> str:
        """Dump recorded messages to a JSON comprehensible to aiomsa.mock.

        Returns:
            A JSON string of a "node" object comprehensible to aiomsa.mock.e2.
        """
        json_str = json.dumps(self.to_dict())
        self._messages.clear()
        return json_str

    def __getattr__(self, attr: str) -> Any:
        if attr not in self.ATTRS:
            return getattr(self._sub, attr)
        else:
            return self.__dict__[attr]

    def __setattr__(self, attr: str, val: Any) -> None:
        if attr not in self.ATTRS:
            return setattr(self._sub, attr, val)
        else:
            self.__dict__[attr] = val


def generate_staticfile(filenames: List[str], output: str) -> None:
    """Join multiple JSONs dumped from RecordSubscription instances to a
    staticfile for aiomsa.mock.e2.static.

    Args:
        filenames: JSONs generated by RecordSubscription.dump
        output: Output file name for JSON created by the joined files.
    """
    staticdict = {"nodes": []}
    for f in filenames:
        node = json.loads(pathlib.Path(f).read_text())
        staticdict["nodes"] += [node]
    with open(output, "w") as f:
        f.write(json.dumps(staticdict))
