from functools import cache, cached_property
from json import JSONEncoder
from signal import SIGCONT, SIGSTOP
from typing import IO, Any, Iterable, Iterator

from .block import Block
from .element import BaseElement
from .logging import logger


class OutputProcessor:
    """Assemble blocks generated by elements into a status line."""

    def __init__(self, elements: Iterable[BaseElement], click_events=False) -> None:
        self.elements = list(elements)
        self.click_events = bool(click_events)

    @cached_property
    def header(self) -> dict[str, Any]:
        return dict(
            version=1,
            stop_signal=SIGSTOP,
            cont_signal=SIGCONT,
            click_events=self.click_events,
        )

    def blocks(self) -> Iterator[Block]:
        """Yield blocks from every element."""
        element_blocks.cache_clear()
        for element in self.elements:
            yield from element_blocks(element)

    def lines(self) -> Iterator[list[Block]]:
        while True:
            yield list(self.blocks())

    def process(self, file: IO[str]) -> Iterator[list[Block]]:
        """Send status lines to output and yield the originating blocks."""

        def send(line: str) -> None:
            print(line, file=file, flush=True)

        encoder = Encoder()
        send(encoder.encode(self.header))
        send("[[]")
        for line in self.lines():
            send(",{}".format(encoder.encode(line)))
            yield line


@cache
def element_blocks(element: BaseElement) -> list[Block]:
    try:
        return list(element.blocks())
    except Exception:
        logger.exception(f"exception while getting blocks for {element}")
        return []


class Encoder(JSONEncoder):
    def default(self, block: Block):
        return block.as_dict()


__all__ = [OutputProcessor.__name__]
