import sys
import threading
from contextlib import contextmanager
from pathlib import Path
from time import sleep

from terminal_bench.utils.logger import logger


class PollingLivestreamHandler:
    def __init__(self, path: Path | None = None, timeout: float = 0.1) -> None:
        self._path = path
        self._timeout = timeout
        self._running = True

        self._init_last_position()

    def _init_last_position(self) -> None:
        if self._path and self._path.exists():
            with self._path.open("rb") as file:
                file.seek(0, 2)  # Seek to end
                self._last_position = file.tell()
        else:
            self._last_position = 0

    def _print_new_content(self):
        if not self._path:
            return

        with self._path.open("rb") as file:
            file_size = file.seek(0, 2)  # Seek to end to get file size
            if file_size < self._last_position:
                self._last_position = 0

            file.seek(self._last_position)
            new_content = file.read()

            if new_content:
                sys.stdout.buffer.write(new_content)
                sys.stdout.flush()

            self._last_position = file.tell()

    def start(self) -> None:
        self._running = True

        while self._running:
            if self._path and self._path.exists():
                self._print_new_content()

            sleep(self._timeout)

    def stop(self) -> None:
        self._running = False

    def set_path(self, path: Path) -> None:
        self._path = path
        self._init_last_position()


class Livestreamer:
    def __init__(self, path: Path | None = None) -> None:
        self._path = path
        self._handler = PollingLivestreamHandler(self._path)
        self._thread = None
        self._logger = logger.getChild(__name__)

    def change_livestream_path(self, path: Path) -> None:
        self.stop()

        self._path = path
        self._handler.set_path(path)

        self.start()

    def start(self) -> None:
        self._thread = threading.Thread(target=self._handler.start, daemon=True)
        self._thread.start()

    def stop(self) -> None:
        if self._thread:
            self._handler.stop()
            self._thread.join(timeout=1.0)


@contextmanager
def livestream(path: Path):
    streamer = Livestreamer(path)
    try:
        streamer.start()
        yield
    finally:
        streamer.stop()
