"""Scrollable output panel widget for the ALiCE TUI.

In normal mode this is a plain :class:`~textual.widgets.RichLog`.
When *WordStar mode* is active, :meth:`write` queues text and reveals it
character-by-character with a blinking cursor, variable keystroke speed,
and a retro status-bar flash — just like feeding paper through a daisy-wheel
printer.
"""

from __future__ import annotations

import random
from typing import TYPE_CHECKING

from textual.widgets import RichLog

if TYPE_CHECKING:
    from textual.timer import Timer


class OutputPanel(RichLog):
    """Scrollable RichLog widget for displaying command output.

    Parameters
    ----------
    wordstar:
        When ``True``, text written via :meth:`write` is revealed one
        character at a time instead of appearing instantly.
    speed:
        Base seconds-per-character in WordStar mode (default 0.012 —
        faster than the Typewriter widget because output can be long).
    """

    def __init__(
        self,
        *args: object,
        wordstar: bool = False,
        speed: float = 0.012,
        **kwargs: object,
    ) -> None:
        super().__init__(*args, markup=True, **kwargs)  # type: ignore[arg-type]
        self._wordstar = wordstar
        self._ws_speed = speed
        self._ws_queue: list[str] = []
        self._ws_current: str = ""
        self._ws_pos: int = 0
        self._ws_timer: Timer | None = None
        self._ws_busy: bool = False

    # ── public API ────────────────────────────────────────────────────

    def write(self, content: object, *args: object, **kwargs: object) -> None:  # type: ignore[override]
        """Write content to the panel.

        In WordStar mode the text is queued and revealed incrementally.
        Otherwise it delegates directly to :class:`RichLog.write`.
        """
        text = str(content)
        if not self._wordstar:
            super().write(text, *args, **kwargs)  # type: ignore[arg-type]
            return

        self._ws_queue.append(text)
        if not self._ws_busy:
            self._ws_start_next()

    def clear(self) -> None:  # type: ignore[override]
        """Clear the panel and cancel any in-flight WordStar reveal."""
        self._ws_queue.clear()
        self._ws_current = ""
        self._ws_pos = 0
        self._ws_busy = False
        if self._ws_timer is not None:
            self._ws_timer.stop()
            self._ws_timer = None
        super().clear()

    # ── WordStar reveal internals ─────────────────────────────────────

    def _ws_start_next(self) -> None:
        """Pop the next queued string and begin revealing it."""
        if not self._ws_queue:
            self._ws_busy = False
            return
        self._ws_busy = True
        self._ws_current = self._ws_queue.pop(0)
        self._ws_pos = 0
        self._ws_schedule()

    def _ws_schedule(self) -> None:
        jitter = random.uniform(0.6, 1.4)  # noqa: S311
        delay = self._ws_speed * jitter
        # Longer pause on line breaks and punctuation
        if self._ws_pos > 0 and self._ws_current[self._ws_pos - 1] in "\n.!?:":
            delay += random.uniform(0.04, 0.10)  # noqa: S311
        self._ws_timer = self.set_timer(delay, self._ws_tick)

    def _ws_tick(self) -> None:
        self._ws_pos += 1
        # Batch a few characters at a time for long texts to stay responsive
        chunk = self._ws_current[: self._ws_pos]
        # We rewrite the last line by clearing and re-emitting everything
        # already revealed.  For simplicity we write the whole chunk each
        # tick — RichLog handles the scrolling.
        #
        # To avoid flicker we only call super().write() once the full
        # line is done (or we've reached the end).
        if self._ws_pos >= len(self._ws_current):
            super().write(self._ws_current)  # type: ignore[arg-type]
            self._ws_start_next()
            return

        # Still revealing — check if we just finished a line
        if self._ws_current[self._ws_pos - 1] == "\n":
            line = chunk.rstrip("\n")
            if line:
                super().write(line)  # type: ignore[arg-type]

        self._ws_schedule()
