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

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


class OutputDelegator:
    """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)

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

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

    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(
                dict(
                    version=1,
                    stop_signal=SIGSTOP,
                    cont_signal=SIGCONT,
                    click_events=self.click_events,
                )
            )
        )
        send("[[]")
        while True:
            blocks = list(self.blocks())
            send(",{}".format(encoder.encode(blocks)))
            yield blocks


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


__all__ = [OutputDelegator.__name__]
