"""
ANSI terminal display backend.
"""

import os


class TerminalDisplay:
    """ANSI terminal display backend.

    Renders buffer to stdout using ANSI escape codes.
    Supports reuse - when reuse=True, skips re-initializing terminal state.
    Auto-detects terminal dimensions on init.
    """

    width: int = 80
    height: int = 24
    _initialized: bool = False

    def __init__(self, target_fps: float = 30.0):
        self.target_fps = target_fps
        self._frame_period = 1.0 / target_fps if target_fps > 0 else 0
        self._last_frame_time = 0.0
        self._cached_dimensions: tuple[int, int] | None = None

    def init(self, width: int, height: int, reuse: bool = False) -> None:
        """Initialize display with dimensions.

        If width/height are not provided (0/None), auto-detects terminal size.
        Otherwise uses provided dimensions or falls back to terminal size
        if the provided dimensions exceed terminal capacity.

        Args:
            width: Desired terminal width (0 = auto-detect)
            height: Desired terminal height (0 = auto-detect)
            reuse: If True, skip terminal re-initialization
        """
        from engine.terminal import CURSOR_OFF

        # Auto-detect terminal size (handle case where no terminal)
        try:
            term_size = os.get_terminal_size()
            term_width = term_size.columns
            term_height = term_size.lines
        except OSError:
            # No terminal available (e.g., in tests)
            term_width = width if width > 0 else 80
            term_height = height if height > 0 else 24

        # Use provided dimensions if valid, otherwise use terminal size
        if width > 0 and height > 0:
            self.width = min(width, term_width)
            self.height = min(height, term_height)
        else:
            self.width = term_width
            self.height = term_height

        if not reuse or not self._initialized:
            print(CURSOR_OFF, end="", flush=True)
            self._initialized = True

    def get_dimensions(self) -> tuple[int, int]:
        """Get current terminal dimensions.

        Returns cached dimensions to avoid querying terminal every frame,
        which can cause inconsistent results. Dimensions are only refreshed
        when they actually change.

        Returns:
            (width, height) in character cells
        """
        try:
            term_size = os.get_terminal_size()
            new_dims = (term_size.columns, term_size.lines)
        except OSError:
            new_dims = (self.width, self.height)

        # Only update cached dimensions if they actually changed
        if self._cached_dimensions is None or self._cached_dimensions != new_dims:
            self._cached_dimensions = new_dims
            self.width = new_dims[0]
            self.height = new_dims[1]

        return self._cached_dimensions

    def show(
        self, buffer: list[str], border: bool = False, positioning: str = "mixed"
    ) -> None:
        """Display buffer with optional border and positioning mode.

        Args:
            buffer: List of lines to display
            border: Whether to apply border
            positioning: Positioning mode - "mixed" (default), "absolute", or "relative"
        """
        import sys

        from engine.display import get_monitor, render_border

        # Note: Frame rate limiting is handled by the caller (e.g., FrameTimer).
        # This display renders every frame it receives.

        # Get metrics for border display
        fps = 0.0
        frame_time = 0.0
        monitor = get_monitor()
        if monitor:
            stats = monitor.get_stats()
            avg_ms = stats.get("pipeline", {}).get("avg_ms", 0) if stats else 0
            frame_count = stats.get("frame_count", 0) if stats else 0
            if avg_ms and frame_count > 0:
                fps = 1000.0 / avg_ms
                frame_time = avg_ms

        # Apply border if requested
        from engine.display import BorderMode

        if border and border != BorderMode.OFF:
            buffer = render_border(buffer, self.width, self.height, fps, frame_time)

        # Apply positioning based on mode
        if positioning == "absolute":
            # All lines should have cursor positioning codes
            # Join with newlines (cursor codes already in buffer)
            output = "\033[H\033[J" + "\n".join(buffer)
        elif positioning == "relative":
            # Remove cursor positioning codes (except colors) and join with newlines
            import re

            cleaned_buffer = []
            for line in buffer:
                # Remove cursor positioning codes but keep color codes
                # Pattern: \033[row;colH or \033[row;col;...H
                cleaned = re.sub(r"\033\[[0-9;]*H", "", line)
                cleaned_buffer.append(cleaned)
            output = "\033[H\033[J" + "\n".join(cleaned_buffer)
        else:  # mixed (default)
            # Current behavior: join with newlines
            # Effects that need absolute positioning have their own cursor codes
            output = "\033[H\033[J" + "\n".join(buffer)

        sys.stdout.buffer.write(output.encode())
        sys.stdout.flush()

    def clear(self) -> None:
        from engine.terminal import CLR

        print(CLR, end="", flush=True)

    def cleanup(self) -> None:
        from engine.terminal import CURSOR_ON

        print(CURSOR_ON, end="", flush=True)

    def is_quit_requested(self) -> bool:
        """Check if quit was requested (optional protocol method)."""
        return False

    def clear_quit_request(self) -> None:
        """Clear quit request (optional protocol method)."""
        pass
