"""
Message overlay stage - Renders ntfy messages as an overlay on the buffer.

This stage provides message overlay capability for displaying ntfy.sh messages
as a centered panel with pink/magenta gradient, matching upstream/main aesthetics.
"""

import re
import time
from dataclasses import dataclass
from datetime import datetime

from engine import config
from engine.effects.legacy import vis_trunc
from sideline.pipeline.core import DataType, PipelineContext, Stage
from sideline.render.blocks import big_wrap
from sideline.render.gradient import msg_gradient


@dataclass
class MessageOverlayConfig:
    """Configuration for MessageOverlayStage."""

    enabled: bool = True
    display_secs: int = 30  # How long to display messages
    topic_url: str | None = None  # Ntfy topic URL (None = use config default)


class MessageOverlayStage(Stage):
    """Stage that renders ntfy message overlay on the buffer.

    Provides:
    - message.overlay capability (optional)
    - Renders centered panel with pink/magenta gradient
    - Shows title, body, timestamp, and remaining time
    """

    name = "message_overlay"
    category = "overlay"

    def __init__(
        self, config: MessageOverlayConfig | None = None, name: str = "message_overlay"
    ):
        self.config = config or MessageOverlayConfig()
        self._ntfy_poller = None
        self._msg_cache = (None, None)  # (cache_key, rendered_rows)

    @property
    def capabilities(self) -> set[str]:
        """Provides message overlay capability."""
        return {"message.overlay"} if self.config.enabled else set()

    @property
    def dependencies(self) -> set[str]:
        """Needs rendered buffer and camera transformation to overlay onto."""
        return {"render.output", "camera"}

    @property
    def inlet_types(self) -> set:
        return {DataType.TEXT_BUFFER}

    @property
    def outlet_types(self) -> set:
        return {DataType.TEXT_BUFFER}

    def init(self, ctx: PipelineContext) -> bool:
        """Initialize ntfy poller if topic URL is configured."""
        if not self.config.enabled:
            return True

        # Get or create ntfy poller
        topic_url = self.config.topic_url or config.NTFY_TOPIC
        if topic_url:
            from sideline.ntfy import NtfyPoller

            self._ntfy_poller = NtfyPoller(
                topic_url=topic_url,
                reconnect_delay=getattr(config, "NTFY_RECONNECT_DELAY", 5),
                display_secs=self.config.display_secs,
            )
            self._ntfy_poller.start()
            ctx.set("ntfy_poller", self._ntfy_poller)

        return True

    def process(self, data: list[str], ctx: PipelineContext) -> list[str]:
        """Render message overlay on the buffer."""
        if not self.config.enabled or not data:
            return data

        # Get active message from poller
        msg = None
        if self._ntfy_poller:
            msg = self._ntfy_poller.get_active_message()

        if msg is None:
            return data

        # Render overlay
        w = ctx.terminal_width if hasattr(ctx, "terminal_width") else 80
        h = ctx.terminal_height if hasattr(ctx, "terminal_height") else 24

        overlay, self._msg_cache = self._render_message_overlay(
            msg, w, h, self._msg_cache
        )

        # Composite overlay onto buffer
        result = list(data)
        for line in overlay:
            # Overlay uses ANSI cursor positioning, just append
            result.append(line)

        return result

    def _render_message_overlay(
        self,
        msg: tuple[str, str, float] | None,
        w: int,
        h: int,
        msg_cache: tuple,
    ) -> tuple[list[str], tuple]:
        """Render ntfy message overlay.

        Args:
            msg: (title, body, timestamp) or None
            w: terminal width
            h: terminal height
            msg_cache: (cache_key, rendered_rows) for caching

        Returns:
            (list of ANSI strings, updated cache)
        """
        overlay = []
        if msg is None:
            return overlay, msg_cache

        m_title, m_body, m_ts = msg
        display_text = m_body or m_title or "(empty)"
        display_text = re.sub(r"\s+", " ", display_text.upper())

        cache_key = (display_text, w)
        if msg_cache[0] != cache_key:
            msg_rows = big_wrap(display_text, w - 4)
            msg_cache = (cache_key, msg_rows)
        else:
            msg_rows = msg_cache[1]

        msg_rows = msg_gradient(msg_rows, (time.monotonic() * config.GRAD_SPEED) % 1.0)

        elapsed_s = int(time.monotonic() - m_ts)
        remaining = max(0, self.config.display_secs - elapsed_s)
        ts_str = datetime.now().strftime("%H:%M:%S")
        panel_h = len(msg_rows) + 2
        panel_top = max(0, (h - panel_h) // 2)

        row_idx = 0
        for mr in msg_rows:
            ln = vis_trunc(mr, w)
            overlay.append(f"\033[{panel_top + row_idx + 1};1H  {ln}\033[0m\033[K")
            row_idx += 1

        meta_parts = []
        if m_title and m_title != m_body:
            meta_parts.append(m_title)
        meta_parts.append(f"ntfy \u00b7 {ts_str} \u00b7 {remaining}s")
        meta = (
            "  " + " \u00b7 ".join(meta_parts)
            if len(meta_parts) > 1
            else "  " + meta_parts[0]
        )
        overlay.append(
            f"\033[{panel_top + row_idx + 1};1H\033[38;5;245m{meta}\033[0m\033[K"
        )
        row_idx += 1

        bar = "\u2500" * (w - 4)
        overlay.append(
            f"\033[{panel_top + row_idx + 1};1H  \033[2;38;5;37m{bar}\033[0m\033[K"
        )

        return overlay, msg_cache

    def cleanup(self) -> None:
        """Cleanup resources."""
        pass
