"""Full-screen renderer for plugin terminal output."""

import sys
import termios
import tty
import shutil
import logging
from typing import Tuple, Optional, Any
from dataclasses import dataclass

from ..io.visual_effects import ColorPalette

logger = logging.getLogger(__name__)


@dataclass
class TerminalSnapshot:
    """Snapshot of terminal state for restoration."""
    termios_settings: Any = None
    cursor_visible: bool = True
    terminal_size: Tuple[int, int] = (80, 24)


class FullScreenRenderer:
    """Handles full-screen rendering with alternate buffer management.

    This class provides direct terminal control for plugins, including
    alternate buffer management, cursor control, and direct output.
    """

    def __init__(self):
        """Initialize the full-screen renderer."""
        self.active = False
        self.terminal_snapshot: Optional[TerminalSnapshot] = None
        self.terminal_width = 80
        self.terminal_height = 24

        logger.info("FullScreenRenderer initialized")

    def setup_terminal(self) -> bool:
        """Setup terminal for full-screen rendering.

        Returns:
            True if setup was successful, False otherwise.
        """
        try:
            # Get terminal size
            size = shutil.get_terminal_size()
            self.terminal_width = size.columns
            self.terminal_height = size.lines

            # Save current terminal state
            self.terminal_snapshot = TerminalSnapshot()
            # CRITICAL FIX: Don't set raw mode when running within main application
            # The main app already handles input properly via InputHandler
            if sys.stdin.isatty():
                self.terminal_snapshot.termios_settings = termios.tcgetattr(sys.stdin.fileno())
                # Skip tty.setraw() - let main application handle input

            # Enter alternate buffer and setup
            sys.stdout.write("\033[?1049h")  # Enter alternate buffer
            sys.stdout.flush()

            # Clear and setup alternate buffer
            self.clear_screen()
            self.hide_cursor()
            sys.stdout.flush()

            # Fill screen with spaces to ensure clean state
            for row in range(self.terminal_height):
                sys.stdout.write(f"\033[{row+1};1H{' ' * self.terminal_width}")
            sys.stdout.write("\033[H")  # Return to home
            sys.stdout.flush()

            self.active = True
            logger.info(f"Terminal setup complete: {self.terminal_width}x{self.terminal_height}")
            return True

        except Exception as e:
            logger.error(f"Failed to setup terminal: {e}")
            return False

    def restore_terminal(self) -> bool:
        """Restore terminal to original state.

        Returns:
            True if restoration was successful, False otherwise.
        """
        try:
            if not self.active:
                return True

            # Clear alternate buffer before exiting
            self.clear_screen()
            # Don't call show_cursor() - normal mode keeps cursor hidden anyway

            # Exit alternate buffer (automatically restores screen and cursor position)
            sys.stdout.write("\033[?1049l")  # Exit alternate buffer
            sys.stdout.flush()

            # CRITICAL FIX: Don't clear screen or move cursor - alternate buffer already restored everything
            # Removed: sys.stdout.write("\033[2J") and sys.stdout.write("\033[H")
            # The alternate buffer automatically restores the exact screen and cursor position

            # Restore terminal settings
            if (self.terminal_snapshot and
                self.terminal_snapshot.termios_settings and
                sys.stdin.isatty()):
                termios.tcsetattr(
                    sys.stdin.fileno(),
                    termios.TCSADRAIN,
                    self.terminal_snapshot.termios_settings
                )

            self.active = False
            self.terminal_snapshot = None
            logger.info("Terminal restored successfully")
            return True

        except Exception as e:
            logger.error(f"Failed to restore terminal: {e}")
            return False

    def clear_screen(self):
        """Clear the entire screen."""
        sys.stdout.write("\033[2J\033[H")
        sys.stdout.flush()

    def clear_line(self, row: int):
        """Clear a specific line.

        Args:
            row: Row number (1-based).
        """
        sys.stdout.write(f"\033[{row};1H\033[K")
        sys.stdout.flush()

    def move_cursor(self, x: int, y: int):
        """Move cursor to specific position.

        Args:
            x: Column position (0-based).
            y: Row position (0-based).
        """
        sys.stdout.write(f"\033[{y+1};{x+1}H")

    def hide_cursor(self):
        """Hide the cursor."""
        sys.stdout.write("\033[?25l")

    def show_cursor(self):
        """Show the cursor."""
        sys.stdout.write("\033[?25h")

    def write_raw(self, text: str):
        """Write raw text to terminal.

        Args:
            text: Text to write (can include ANSI codes).
        """
        sys.stdout.write(text)
        sys.stdout.flush()

    def write_at(self, x: int, y: int, text: str, color: str = ColorPalette.RESET):
        """Write text at specific position with optional color.

        Args:
            x: Column position (0-based).
            y: Row position (0-based).
            text: Text to write.
            color: ANSI color code.
        """
        if 0 <= x < self.terminal_width and 0 <= y < self.terminal_height:
            self.move_cursor(x, y)
            sys.stdout.write(f"{color}{text}{ColorPalette.RESET}")
            sys.stdout.flush()

    def draw_box(self, x: int, y: int, width: int, height: int,
                 border_color: str = ColorPalette.WHITE,
                 fill_color: str = ColorPalette.RESET):
        """Draw a box at the specified position.

        Args:
            x: Left column (0-based).
            y: Top row (0-based).
            width: Box width.
            height: Box height.
            border_color: Color for border.
            fill_color: Color for interior.
        """
        # Draw top border
        self.write_at(x, y, f"{border_color}╭{'─' * (width-2)}╮{ColorPalette.RESET}")

        # Draw sides and interior
        for row in range(1, height-1):
            self.write_at(x, y + row, f"{border_color}│{fill_color}{' ' * (width-2)}{border_color}│{ColorPalette.RESET}")

        # Draw bottom border
        if height > 1:
            self.write_at(x, y + height - 1, f"{border_color}╰{'─' * (width-2)}╯{ColorPalette.RESET}")

    def draw_line(self, x1: int, y1: int, x2: int, y2: int,
                  char: str = "─", color: str = ColorPalette.WHITE):
        """Draw a line between two points.

        Args:
            x1, y1: Start position.
            x2, y2: End position.
            char: Character to use for line.
            color: Line color.
        """
        # Simple horizontal/vertical line implementation
        if y1 == y2:  # Horizontal line
            start_x, end_x = min(x1, x2), max(x1, x2)
            line_text = char * (end_x - start_x + 1)
            self.write_at(start_x, y1, line_text, color)
        elif x1 == x2:  # Vertical line
            start_y, end_y = min(y1, y2), max(y1, y2)
            for y in range(start_y, end_y + 1):
                self.write_at(x1, y, char, color)

    def get_terminal_size(self) -> Tuple[int, int]:
        """Get current terminal size.

        Returns:
            Tuple of (width, height).
        """
        return (self.terminal_width, self.terminal_height)

    def is_active(self) -> bool:
        """Check if renderer is currently active.

        Returns:
            True if renderer is active, False otherwise.
        """
        return self.active

    def flush(self):
        """Flush output buffer."""
        sys.stdout.flush()