"""
Tmux Controller for SoraCLI Daemon Mode.

Manages tmux sessions for background weather animation rendering.
"""

import os
import shutil
import subprocess
import sys
from dataclasses import dataclass
from typing import List, Optional, Tuple


@dataclass
class TmuxSession:
    """Represents a tmux session."""
    name: str
    windows: int
    created: str
    attached: bool


@dataclass
class TmuxPane:
    """Represents a tmux pane."""
    index: int
    width: int
    height: int
    active: bool
    command: str


class TmuxError(Exception):
    """Exception raised for tmux-related errors."""
    pass


class TmuxController:
    """
    Controller for tmux operations.
    
    Manages creating, configuring, and controlling tmux sessions
    for background SoraCLI animations.
    """
    
    SESSION_NAME = "sora_bg"
    ANIMATION_PANE = 0
    SHELL_PANE = 1
    
    def __init__(self):
        self.tmux_path = self._find_tmux()
    
    def _find_tmux(self) -> Optional[str]:
        """Find tmux executable path."""
        return shutil.which("tmux")
    
    def is_tmux_available(self) -> bool:
        """Check if tmux is installed and available."""
        return self.tmux_path is not None
    
    def _run_tmux(self, args: List[str], capture: bool = True) -> Tuple[int, str, str]:
        """
        Run a tmux command.
        
        Args:
            args: Command arguments
            capture: Whether to capture output
        
        Returns:
            Tuple of (return_code, stdout, stderr)
        """
        if not self.is_tmux_available():
            raise TmuxError("tmux is not installed. Install with: apt install tmux (Linux) or brew install tmux (macOS)")
        
        cmd = [self.tmux_path] + args
        
        try:
            result = subprocess.run(
                cmd,
                capture_output=capture,
                text=True,
                timeout=30
            )
            return result.returncode, result.stdout, result.stderr
        except subprocess.TimeoutExpired:
            raise TmuxError(f"tmux command timed out: {' '.join(args)}")
        except Exception as e:
            raise TmuxError(f"Failed to run tmux command: {e}")
    
    def session_exists(self, session_name: Optional[str] = None) -> bool:
        """Check if a tmux session exists."""
        session_name = session_name or self.SESSION_NAME
        
        try:
            code, stdout, _ = self._run_tmux(["has-session", "-t", session_name])
            return code == 0
        except TmuxError:
            return False
    
    def list_sessions(self) -> List[TmuxSession]:
        """List all tmux sessions."""
        try:
            code, stdout, _ = self._run_tmux([
                "list-sessions", "-F",
                "#{session_name}|#{session_windows}|#{session_created}|#{session_attached}"
            ])
            
            if code != 0:
                return []
            
            sessions = []
            for line in stdout.strip().split('\n'):
                if line:
                    parts = line.split('|')
                    if len(parts) >= 4:
                        sessions.append(TmuxSession(
                            name=parts[0],
                            windows=int(parts[1]),
                            created=parts[2],
                            attached=parts[3] == '1'
                        ))
            return sessions
        except TmuxError:
            return []
    
    def create_session(self, session_name: Optional[str] = None) -> bool:
        """
        Create a new tmux session for SoraCLI.
        
        Creates a session with two panes:
        - Pane 0: Animation pane (top/left)
        - Pane 1: Shell pane (bottom/right)
        
        Returns:
            True if session created successfully
        """
        session_name = session_name or self.SESSION_NAME
        
        if self.session_exists(session_name):
            raise TmuxError(f"Session '{session_name}' already exists")
        
        # Create detached session
        code, _, stderr = self._run_tmux([
            "new-session",
            "-d",  # Detached
            "-s", session_name,
            "-x", "120",  # Width
            "-y", "40",   # Height
        ])
        
        if code != 0:
            raise TmuxError(f"Failed to create session: {stderr}")
        
        return True
    
    def setup_split_layout(self, session_name: Optional[str] = None, 
                           horizontal: bool = True,
                           animation_size: int = 5) -> bool:
        """
        Set up split pane layout for animation and shell.
        
        Layout:
          ┌────────────────────┐
          │ Animation (5 lines)│  Pane 0
          ├────────────────────┤
          │ Shell (remaining)  │  Pane 1
          └────────────────────┘
        
        Args:
            session_name: Session name
            horizontal: If True, split horizontally (top/bottom)
            animation_size: Number of lines for animation pane (default: 5)
        
        Returns:
            True if layout setup successfully
        """
        session_name = session_name or self.SESSION_NAME
        
        if not self.session_exists(session_name):
            raise TmuxError(f"Session '{session_name}' does not exist")
        
        # Split vertically - new pane at bottom for shell
        code, _, stderr = self._run_tmux([
            "split-window",
            "-v",  # Vertical split (top/bottom)
            "-t", f"{session_name}:0",
        ])
        
        if code != 0:
            raise TmuxError(f"Failed to split window: {stderr}")
        
        # Now: pane 0 = top (original), pane 1 = bottom (new)
        # Resize pane 0 to be small (animation bar)
        self._run_tmux([
            "resize-pane",
            "-t", f"{session_name}:0.0",
            "-y", str(animation_size)
        ])
        
        if code != 0:
            raise TmuxError(f"Failed to split window: {stderr}")
        
        return True
    
    def send_command_to_pane(self, command: str, pane: int = 0,
                             session_name: Optional[str] = None) -> bool:
        """
        Send a command to a specific pane.
        
        Args:
            command: Command to execute
            pane: Pane index (0 for animation, 1 for shell)
            session_name: Session name
        
        Returns:
            True if command sent successfully
        """
        session_name = session_name or self.SESSION_NAME
        target = f"{session_name}:0.{pane}"
        
        code, _, stderr = self._run_tmux([
            "send-keys",
            "-t", target,
            command,
            "Enter"
        ])
        
        if code != 0:
            raise TmuxError(f"Failed to send command: {stderr}")
        
        return True
    
    def start_animation(self, location: Optional[str] = None,
                        theme: Optional[str] = None,
                        session_name: Optional[str] = None,
                        api_key: Optional[str] = None) -> bool:
        """
        Start the SoraCLI animation in the animation pane.
        
        Args:
            location: Weather location
            theme: Theme name
            session_name: Session name
            api_key: OpenWeatherMap API key
        
        Returns:
            True if animation started
        """
        session_name = session_name or self.SESSION_NAME
        
        # Build the soracli command - use demo mode for compact bar
        # Demo mode adapts to terminal size automatically
        cmd_parts = ["python3", "-m", "soracli", "demo", "--rain"]
        if theme:
            cmd_parts.extend(["--theme", theme])
        
        command = " ".join(cmd_parts)
        
        return self.send_command_to_pane(command, pane=self.ANIMATION_PANE, session_name=session_name)
    
    def kill_session(self, session_name: Optional[str] = None) -> bool:
        """
        Kill a tmux session.
        
        Args:
            session_name: Session name to kill
        
        Returns:
            True if session killed successfully
        """
        session_name = session_name or self.SESSION_NAME
        
        if not self.session_exists(session_name):
            return True  # Already doesn't exist
        
        code, _, stderr = self._run_tmux(["kill-session", "-t", session_name])
        
        if code != 0:
            raise TmuxError(f"Failed to kill session: {stderr}")
        
        return True
    
    def attach_session(self, session_name: Optional[str] = None) -> None:
        """
        Attach to a tmux session (interactive).
        
        Args:
            session_name: Session name to attach
        """
        session_name = session_name or self.SESSION_NAME
        
        if not self.session_exists(session_name):
            raise TmuxError(f"Session '{session_name}' does not exist")
        
        # Use os.execvp to replace current process
        os.execvp(self.tmux_path, [self.tmux_path, "attach-session", "-t", session_name])
    
    def configure_transparency(self, session_name: Optional[str] = None,
                               bg_opacity: float = 0.8) -> bool:
        """
        Configure terminal transparency settings for the session.
        
        Note: Actual transparency depends on terminal emulator support.
        This sets up tmux options that work with transparent terminals.
        
        Args:
            session_name: Session name
            bg_opacity: Background opacity (0.0 to 1.0)
        
        Returns:
            True if configured successfully
        """
        session_name = session_name or self.SESSION_NAME
        
        # Set tmux options for transparency support
        options = [
            ("set-option", "-g", "status-style", "bg=default"),
            ("set-option", "-g", "window-style", "bg=default"),
            ("set-option", "-g", "window-active-style", "bg=default"),
            ("set-option", "-g", "pane-border-style", "fg=colour240,bg=default"),
            ("set-option", "-g", "pane-active-border-style", "fg=colour51,bg=default"),
        ]
        
        for opt in options:
            self._run_tmux(list(opt))
        
        return True
    
    def set_pane_title(self, title: str, pane: int = 0,
                       session_name: Optional[str] = None) -> bool:
        """
        Set the title for a pane.
        
        Args:
            title: Pane title
            pane: Pane index
            session_name: Session name
        
        Returns:
            True if title set successfully
        """
        session_name = session_name or self.SESSION_NAME
        target = f"{session_name}:0.{pane}"
        
        # Set pane title using escape sequence
        escape_seq = f"\\033]2;{title}\\033\\\\"
        self._run_tmux(["send-keys", "-t", target, f"printf '{escape_seq}'", "Enter"])
        
        return True
    
    def get_session_info(self, session_name: Optional[str] = None) -> Optional[dict]:
        """
        Get detailed information about a session.
        
        Args:
            session_name: Session name
        
        Returns:
            Dictionary with session info or None
        """
        session_name = session_name or self.SESSION_NAME
        
        if not self.session_exists(session_name):
            return None
        
        # Get session info
        code, stdout, _ = self._run_tmux([
            "display-message", "-t", session_name, "-p",
            "#{session_name}|#{session_windows}|#{session_width}|#{session_height}|#{session_attached}"
        ])
        
        if code != 0:
            return None
        
        parts = stdout.strip().split('|')
        if len(parts) < 5:
            return None
        
        # Get pane info
        code, pane_stdout, _ = self._run_tmux([
            "list-panes", "-t", session_name, "-F",
            "#{pane_index}|#{pane_width}|#{pane_height}|#{pane_active}|#{pane_current_command}"
        ])
        
        panes = []
        if code == 0:
            for line in pane_stdout.strip().split('\n'):
                if line:
                    pane_parts = line.split('|')
                    if len(pane_parts) >= 5:
                        panes.append(TmuxPane(
                            index=int(pane_parts[0]),
                            width=int(pane_parts[1]),
                            height=int(pane_parts[2]),
                            active=pane_parts[3] == '1',
                            command=pane_parts[4]
                        ))
        
        return {
            'name': parts[0],
            'windows': int(parts[1]),
            'width': int(parts[2]),
            'height': int(parts[3]),
            'attached': parts[4] == '1',
            'panes': panes
        }
    
    def resize_pane(self, pane: int, width: Optional[int] = None,
                    height: Optional[int] = None,
                    session_name: Optional[str] = None) -> bool:
        """
        Resize a pane.
        
        Args:
            pane: Pane index
            width: New width (optional)
            height: New height (optional)
            session_name: Session name
        
        Returns:
            True if resized successfully
        """
        session_name = session_name or self.SESSION_NAME
        target = f"{session_name}:0.{pane}"
        
        if width:
            self._run_tmux(["resize-pane", "-t", target, "-x", str(width)])
        if height:
            self._run_tmux(["resize-pane", "-t", target, "-y", str(height)])
        
        return True
