"""Tmux integration plugin for managing and viewing tmux sessions.

Provides commands to:
- Create new tmux sessions with commands
- View live tmux session output in alt buffer
- List active sessions
- Kill sessions
"""

import asyncio
import logging
import shutil
import subprocess
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional

from kollabor_tui.visual_effects import AgnosterSegment

logger = logging.getLogger(__name__)


@dataclass
class TmuxSession:
    """Represents a managed tmux session."""

    name: str
    command: str
    tmux_cmd: callable  # Function to build tmux commands with correct socket
    created_at: datetime = field(default_factory=datetime.now)
    pid: Optional[int] = None

    def is_alive(self) -> bool:
        """Check if the tmux session is still running."""
        try:
            result = subprocess.run(
                self.tmux_cmd("has-session", "-t", self.name),
                capture_output=True,
                check=False,
            )
            return result.returncode == 0
        except (subprocess.SubprocessError, FileNotFoundError, OSError):
            logger.exception("Failed to check tmux session '%s' status", self.name)
            return False


class TmuxPlugin:
    """Plugin for tmux session management and live viewing."""

    def __init__(self, name: str = "tmux", event_bus=None, renderer=None, config=None):
        """Initialize the tmux plugin.

        Args:
            name: Plugin name.
            event_bus: Event bus for hook registration.
            renderer: Terminal renderer.
            config: Configuration manager.
        """
        self.name = name
        self.version = "1.0.0"
        self.description = "Manage and view tmux sessions"
        self.enabled = True

        self.sessions: Dict[str, TmuxSession] = {}
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        self.command_registry = None
        self.input_handler = None
        self._current_session: Optional[str] = None  # Currently viewing session
        self._last_arrow_time: float = 0.0  # For double-arrow detection
        self._last_arrow_dir: Optional[str] = None  # "Left" or "Right"
        self._double_arrow_threshold: float = 0.3  # seconds

        self.logger = logger

    def _tmux_cmd(self, *args: str) -> List[str]:
        """Build a tmux command with optional socket name.

        If use_separate_server is enabled (default), uses a dedicated
        kollabor tmux server named after the project folder.
        Otherwise uses the user's main tmux server.

        Configuration lookup (with backward compatibility):
        - Tries new location first: terminal.use_separate_server, terminal.tmux_server
        - Falls back to old location: plugins.tmux.use_separate_server, plugins.tmux.socket_name
        - Defaults to project folder name when not configured (matches agent orchestrator)
        """
        # Default to project folder name for socket (matches agent orchestrator)
        project_socket = Path.cwd().name

        if not self.config:
            # No config available, use defaults
            use_separate = True
            socket_name = project_socket
        else:
            # Try new location first (terminal.*)
            use_separate = self.config.get("terminal.use_separate_server")
            socket_name = self.config.get("terminal.tmux_server")

            # Fall back to old location (plugins.tmux.*) if new location not set
            if use_separate is None:
                use_separate = self.config.get("plugins.tmux.use_separate_server", True)
            if socket_name is None:
                # Config may have socket_name: null (explicit None), so fallback to project folder
                socket_name = (
                    self.config.get("plugins.tmux.socket_name") or project_socket
                )

        # Final fallback - if socket_name is still None after all lookups, use project folder
        if not socket_name:
            socket_name = project_socket

        if use_separate:
            return ["tmux", "-L", socket_name] + list(args)
        return ["tmux"] + list(args)

    @staticmethod
    def get_default_config() -> Dict[str, Any]:
        """Get default configuration for tmux plugin.

        Provides both new (terminal.*) and legacy (plugins.tmux.*) config keys
        for backward compatibility.
        """
        return {
            # New unified terminal config (preferred)
            "terminal": {
                "timeout": 90,  # Default timeout for terminal commands
                "tmux_server": None,  # Socket name (None = use project folder name, matches agent orchestrator)
                "use_separate_server": True,  # Use dedicated tmux server
                "kill_on_shutdown": False,  # Kill sessions on app shutdown
                "capture_lines": 200,  # Number of lines to capture from pane history
                "refresh_rate": 2.0,  # Live view refresh rate in seconds
            },
            # Legacy plugin-specific config (backward compatibility)
            "plugins": {
                "tmux": {
                    "enabled": True,
                    "show_status": True,
                    "refresh_rate": 0.5,  # Live view refresh rate in seconds
                    "capture_lines": 200,  # Number of lines to capture from pane history
                    "use_separate_server": True,  # Use dedicated tmux server
                    "socket_name": None,  # Socket name (None = use project folder name)
                }
            },
        }

    async def initialize(self, event_bus, config, **kwargs) -> None:
        """Initialize the plugin.

        Args:
            event_bus: Application event bus.
            config: Configuration manager.
            **kwargs: Additional parameters including command_registry.
        """
        try:
            self.event_bus = event_bus
            self.config = config
            self.command_registry = kwargs.get("command_registry")
            self.input_handler = kwargs.get("input_handler")
            self.renderer = kwargs.get("renderer")

            # Check if tmux is available
            if not self._check_tmux_available():
                self.logger.warning("tmux not found in PATH - tmux plugin disabled")
                self.enabled = False
                return

            # Register commands
            if self.command_registry:
                self._register_commands()

            # Discover existing sessions
            self._discover_existing_sessions()

            # Register status widget (new widget system)
            await self._register_status_widget()

            self.logger.info("Tmux plugin initialized successfully")

        except Exception as e:
            self.logger.error(f"Error initializing tmux plugin: {e}")
            raise

    def _check_tmux_available(self) -> bool:
        """Check if tmux is available on the system."""
        return shutil.which("tmux") is not None

    def _register_commands(self):
        """Register tmux commands with the command registry."""
        from kollabor_events.models import (
            CommandCategory,
            CommandDefinition,
            CommandMode,
            SubcommandInfo,
        )

        # /terminal - manage tmux sessions
        terminal_cmd = CommandDefinition(
            name="terminal",
            description="Manage terminal sessions (new/view/list/kill)",
            handler=self._handle_tmux_command,
            plugin_name=self.name,
            category=CommandCategory.CUSTOM,  # Plugin command
            mode=CommandMode.INSTANT,
            aliases=["term", "tmux", "t"],
            icon="[>_]",
            subcommands=[
                SubcommandInfo("new", "<name> <cmd>", "Create session running command"),
                SubcommandInfo("view", "[name]", "Live view session (default)"),
                SubcommandInfo("list", "", "List all sessions"),
                SubcommandInfo("kill", "<name>", "Kill a session"),
                SubcommandInfo("attach", "<name>", "Attach (exits kollabor)"),
            ],
        )
        self.command_registry.register_command(terminal_cmd)

        self.logger.info("Tmux commands registered")

    async def _handle_tmux_command(self, command) -> "CommandResult":
        """Handle /tmux command with subcommands.

        Usage:
            /tmux new <command> <session_name>  - Create new session
            /tmux view <session_name>           - Live view session
            /tmux list                          - List sessions
            /tmux kill <session_name>           - Kill session
            /tmux attach <session_name>         - Attach to session (exits kollabor)
        """
        from kollabor_events.models import CommandResult

        args = command.args if command.args else []

        if not args:
            # No args - go directly to view mode
            return await self._handle_view_session([])

        subcommand = args[0].lower()

        if subcommand == "new":
            return await self._handle_new_session(args[1:])
        elif subcommand == "view":
            return await self._handle_view_session(args[1:])
        elif subcommand == "list" or subcommand == "ls":
            return await self._handle_list_sessions()
        elif subcommand == "kill":
            return await self._handle_kill_session(args[1:])
        elif subcommand == "attach":
            return await self._handle_attach_session(args[1:])
        elif subcommand == "help" or subcommand == "--help" or subcommand == "-h":
            return CommandResult(
                success=True, message=self._get_help_text(), display_type="info"
            )
        else:
            return CommandResult(
                success=False,
                message=f"Unknown subcommand: {subcommand}\n\n{self._get_help_text()}",
                display_type="error",
            )

    def _get_help_text(self) -> str:
        """Get help text for terminal command."""
        return """Terminal Session Manager

By default, sessions use a dedicated tmux server (configurable).

Usage:
  /terminal new <name> <command>  Create new session running command
  /terminal view [name]           Live view session (</> to cycle)
  /terminal list                  List all sessions
  /terminal kill <name>           Kill a session
  /terminal attach <name>         Attach to session (leaves kollabor)

Examples:
  /terminal new myserver python -m http.server 8080
  /terminal new logs tail -f /var/log/syslog
  /terminal view
  /terminal kill myserver

To attach directly (with default separate server):
  tmux -L kollabor attach -t <session_name>

To use your main tmux instead, disable "Use Separate Server" in config.

Aliases: /t, /term, /tmux"""

    async def _handle_new_session(self, args: List[str]) -> "CommandResult":
        """Create a new tmux session."""
        from kollabor_events.models import CommandResult

        if len(args) < 1:
            return CommandResult(
                success=False,
                message="Usage: /terminal new <session_name> [command]",
                display_type="error",
            )

        session_name = args[0]
        command = " ".join(args[1:]) if len(args) > 1 else None

        # Check if session already exists
        if session_name in self.sessions and self.sessions[session_name].is_alive():
            return CommandResult(
                success=False,
                message=f"Session '{session_name}' already exists",
                display_type="error",
            )

        try:
            # Create detached tmux session with user's default shell
            # (tmux uses $SHELL or user's login shell by default)
            # Pass KOLLABOR_ROOT_SOCKET so nested kollab instances register on parent's socket
            import os

            root_socket = os.environ.get("KOLLABOR_ROOT_SOCKET", Path.cwd().name)
            result = subprocess.run(
                self._tmux_cmd(
                    "new-session",
                    "-d",
                    "-s",
                    session_name,
                    "-e",
                    f"KOLLABOR_ROOT_SOCKET={root_socket}",
                ),
                capture_output=True,
                text=True,
            )

            if result.returncode != 0:
                return CommandResult(
                    success=False,
                    message=f"Failed to create session: {result.stderr}",
                    display_type="error",
                )

            # Send the command to the new session (if provided)
            if command:
                send_result = subprocess.run(
                    self._tmux_cmd("send-keys", "-t", session_name, command, "Enter"),
                    capture_output=True,
                    text=True,
                )

                if send_result.returncode != 0:
                    self.logger.warning(
                        f"Session created but failed to send command: {send_result.stderr}"
                    )

            # Track the session
            self.sessions[session_name] = TmuxSession(
                name=session_name, command=command or "bash", tmux_cmd=self._tmux_cmd
            )

            msg = f"Created session '{session_name}'"
            if command:
                msg += f" running: {command}"
            return CommandResult(success=True, message=msg, display_type="success")

        except Exception as e:
            return CommandResult(
                success=False,
                message=f"Error creating session: {e}",
                display_type="error",
            )

    async def _handle_view_session(self, args: List[str]) -> "CommandResult":
        """View a tmux session live in alt buffer."""
        from kollabor_events.models import CommandResult

        # If no args, get first available session
        if not args:
            sessions = self._get_all_tmux_sessions()
            if not sessions:
                return CommandResult(
                    success=False,
                    message="No tmux sessions found. Use '/tmux new <name> <command>' to create one.",
                    display_type="error",
                )
            session_name = sessions[0]
        else:
            session_name = args[0]
            # Check if session exists (in tmux, not just our tracking)
            if not self._session_exists(session_name):
                return CommandResult(
                    success=False,
                    message=f"Session '{session_name}' not found",
                    display_type="error",
                )

        # Emit live modal trigger event
        if self.event_bus:
            from kollabor_events.models import EventType
            from kollabor_tui.modals.live_modal_renderer import LiveModalConfig

            # Get terminal height for viewport calculation
            terminal_height = 40  # Default fallback
            if self.renderer and hasattr(self.renderer, "terminal_state"):
                _, terminal_height = self.renderer.terminal_state.get_size()
            # Capture enough lines to fill the modal content area
            # Modal height is roughly terminal_height - 4, minus borders/header/footer
            # Use terminal_height - 6 to be safe
            viewport_lines = max(10, terminal_height - 8)

            # Content generator - uses self._current_session for dynamic switching
            async def get_tmux_content() -> List[str]:
                # Show session name as header line
                sessions = self._get_all_tmux_sessions()
                session_idx = (
                    sessions.index(self._current_session) + 1
                    if self._current_session in sessions
                    else 1
                )
                header = f"[Session: {self._current_session}] ({session_idx}/{len(sessions)})"
                # Capture only the newest lines that fit in viewport
                content = self._capture_tmux_pane(
                    self._current_session, max_lines=viewport_lines
                )
                return [header, "─" * len(header)] + content

            # Input callback for passthrough
            async def handle_input(key_press) -> bool:
                if key_press.name == "Escape":
                    return True  # Exit

                # Key8776 (≈) kills the current session
                if key_press.code == 8776:
                    try:
                        subprocess.run(
                            self._tmux_cmd("kill-session", "-t", self._current_session),
                            capture_output=True,
                            text=True,
                            timeout=2,
                        )
                        logger.info(f"Killed tmux session: {self._current_session}")
                        return True  # Exit modal after killing session
                    except Exception as e:
                        logger.error(
                            f"Failed to kill tmux session {self._current_session}: {e}"
                        )
                        return False

                # Use Opt+Right (Alt+ArrowRight) and Opt+Left (Alt+ArrowLeft) to cycle sessions
                # Support both Alt+Arrow and Alt+letter sequences

                if key_press.name == "Alt+ArrowRight":
                    new_session = self._cycle_session(forward=True)
                    if new_session:
                        self._current_session = new_session
                    return False
                elif key_press.name == "Alt+ArrowLeft":
                    new_session = self._cycle_session(forward=False)
                    if new_session:
                        self._current_session = new_session
                    return False
                # Also support Alt+f and Alt+b for terminals that use these sequences
                elif key_press.name == "Alt+f":
                    new_session = self._cycle_session(forward=True)
                    if new_session:
                        self._current_session = new_session
                    return False
                elif key_press.name == "Alt+b":
                    new_session = self._cycle_session(forward=False)
                    if new_session:
                        self._current_session = new_session
                    return False

                # Opt+Up/Down for page up/down
                if key_press.name == "Alt+ArrowUp":
                    self._send_keys_to_tmux(self._current_session, "PageUp")
                    return False
                elif key_press.name == "Alt+ArrowDown":
                    self._send_keys_to_tmux(self._current_session, "PageDown")
                    return False

                # Only forward keys if we have an active session
                if self._current_session is None:
                    return False

                # Forward arrow keys to tmux
                if key_press.name in ("ArrowLeft", "ArrowRight"):
                    self._send_keys_to_tmux(
                        self._current_session, key_press.name.replace("Arrow", "")
                    )
                    return False

                # Handle Ctrl+C - send to tmux (interrupt)
                if key_press.char and ord(key_press.char) == 3:
                    self._send_keys_to_tmux(self._current_session, "C-c")
                    return False

                # Map special keys to tmux key names
                key_map = {
                    "Enter": "Enter",
                    "Backspace": "BSpace",
                    "Tab": "Tab",
                    "ArrowUp": "Up",
                    "ArrowDown": "Down",
                    "Home": "Home",
                    "End": "End",
                    "PageUp": "PageUp",
                    "PageDown": "PageDown",
                    "Delete": "DC",
                }

                # Forward special keys
                if key_press.name in key_map:
                    self._send_keys_to_tmux(
                        self._current_session, key_map[key_press.name]
                    )
                # Forward regular character keys
                elif key_press.char:
                    self._send_keys_to_tmux(self._current_session, key_press.char)

                return False

            # Configure the live modal with streaming-friendly refresh rate
            config = LiveModalConfig(
                title="terminal",
                footer="Esc: exit | Opt+Left/Right: cycle | Opt+Up/Down: page | Opt+x: kill",
                refresh_rate=self.config.get(
                    "plugins.tmux.refresh_rate", 2.0
                ),  # 2 seconds - much slower
                passthrough_input=True,
            )

            # Store current session
            self._current_session = session_name

            # Emit event to trigger live modal (input handler will handle it)
            await self.event_bus.emit_with_hooks(
                EventType.LIVE_MODAL_TRIGGER,
                {
                    "content_generator": get_tmux_content,
                    "config": config,
                    "input_callback": handle_input,
                },
                "live_modal",
            )

            return CommandResult(
                success=True,
                message="",  # No message needed, modal takes over
                display_type="none",
            )
        else:
            return CommandResult(
                success=False,
                message="Live view not available - event bus not configured",
                display_type="error",
            )

    def _capture_tmux_pane(
        self, session_name: str, max_lines: Optional[int] = None
    ) -> List[str]:
        """Capture current content of a tmux pane.

        Args:
            session_name: Name of the tmux session
            max_lines: Maximum lines to return. Returns newest content from bottom.

        Returns:
            List of cleaned content lines from newest to oldest.
        """
        try:
            # Capture more lines than we need, then take from the end
            capture_lines = (
                self.config.get("plugins.tmux.capture_lines", 500)
                if self.config
                else 500
            )
            # Capture from the end of the pane history
            result = subprocess.run(
                self._tmux_cmd(
                    "capture-pane",
                    "-p",
                    "-e",
                    "-S",
                    f"-{capture_lines}",
                    "-t",
                    session_name,
                ),
                capture_output=True,
                text=True,
                timeout=2,
            )
            if result.returncode == 0:
                import re

                lines = result.stdout.rstrip("\n").split("\n")

                # Strip ANSI codes but keep all lines including empty ones
                cleaned = []
                for line in lines:
                    line = re.sub(r"\x1b\[[0-9;]*m", "", line)  # Remove ANSI colors
                    line = line.rstrip()
                    cleaned.append(line)

                # Remove trailing empty lines from the END (not beginning)
                while cleaned and not cleaned[-1]:
                    cleaned.pop()

                # Return only the newest lines that fit in viewport (from the end)
                if max_lines and len(cleaned) > max_lines:
                    return cleaned[-max_lines:]
                return cleaned
            else:
                return [f"Error capturing pane: {result.stderr}"]
        except subprocess.TimeoutExpired:
            return [f"Session capture timed out - session might be unresponsive"]
        except Exception as e:
            return [f"Error: {e}"]

    def _send_keys_to_tmux(self, session_name: str, keys: str):
        """Send keys to a tmux session."""
        try:
            subprocess.run(
                self._tmux_cmd("send-keys", "-t", session_name, keys),
                capture_output=True,
            )
        except Exception as e:
            self.logger.error(f"Error sending keys to tmux: {e}")

    async def _handle_list_sessions(self) -> "CommandResult":
        """List all tmux sessions (both managed and discovered)."""
        from kollabor_events.models import CommandResult

        # Discover all existing tmux sessions
        all_sessions = self._get_all_tmux_sessions()

        if not all_sessions:
            return CommandResult(
                success=True,
                message="No tmux sessions found. Use '/terminal new <name> <command>' to create one.",
                display_type="info",
            )

        # Build list of sessions, one per line
        lines = ["Terminal Sessions:"]
        for session_name in all_sessions:
            # Check if it's a managed session
            if session_name in self.sessions:
                session = self.sessions[session_name]
                status = "[MANAGED]" if session.is_alive() else "[MANAGED-DEAD]"
                lines.append(f"{status} {session_name}")
            else:
                lines.append(f"[EXTERNAL] {session_name}")

        message = "\n".join(lines)

        return CommandResult(success=True, message=message, display_type="info")

    async def _handle_kill_session(self, args: List[str]) -> "CommandResult":
        """Kill a tmux session."""
        from kollabor_events.models import CommandResult

        if not args:
            return CommandResult(
                success=False,
                message="Usage: /terminal kill <session_name>",
                display_type="error",
            )

        session_name = args[0]

        try:
            result = subprocess.run(
                self._tmux_cmd("kill-session", "-t", session_name),
                capture_output=True,
                text=True,
            )

            if result.returncode == 0:
                # Remove from tracking
                if session_name in self.sessions:
                    del self.sessions[session_name]
                return CommandResult(
                    success=True,
                    message=f"Killed session '{session_name}'",
                    display_type="info",
                )
            else:
                return CommandResult(
                    success=False,
                    message=f"Failed to kill session: {result.stderr}",
                    display_type="error",
                )

        except Exception as e:
            return CommandResult(
                success=False,
                message=f"Error killing session: {e}",
                display_type="error",
            )

    async def _handle_attach_session(self, args: List[str]) -> "CommandResult":
        """Attach to a tmux session (exits kollabor)."""
        from kollabor_events.models import CommandResult

        if not args:
            return CommandResult(
                success=False,
                message="Usage: /terminal attach <session_name>",
                display_type="error",
            )

        session_name = args[0]

        if not self._session_exists(session_name):
            return CommandResult(
                success=False,
                message=f"Session '{session_name}' not found",
                display_type="error",
            )

        # Build attach command based on whether using separate server
        use_separate = (
            self.config.get("plugins.tmux.use_separate_server", True)
            if self.config
            else True
        )
        project_socket = Path.cwd().name
        socket_name = (
            self.config.get("plugins.tmux.socket_name", project_socket)
            if self.config
            else project_socket
        )

        if use_separate:
            attach_cmd = f"tmux -L {socket_name} attach -t {session_name}"
        else:
            attach_cmd = f"tmux attach -t {session_name}"

        return CommandResult(
            success=True,
            message=f"To attach to '{session_name}', exit kollabor and run:\n  {attach_cmd}",
            display_type="info",
        )

    def _session_exists(self, session_name: str) -> bool:
        """Check if a tmux session exists."""
        try:
            result = subprocess.run(
                self._tmux_cmd("has-session", "-t", session_name), capture_output=True
            )
            return result.returncode == 0
        except Exception:
            return False

    def _cycle_session(self, forward: bool = True) -> Optional[str]:
        """Cycle to next/previous tmux session.

        Args:
            forward: True for next session, False for previous.

        Returns:
            New session name, or None if no other sessions.
        """
        sessions = self._get_all_tmux_sessions()
        if len(sessions) <= 1:
            return None

        try:
            current_idx = sessions.index(self._current_session)
            if forward:
                new_idx = (current_idx + 1) % len(sessions)
            else:
                new_idx = (current_idx - 1) % len(sessions)
            return sessions[new_idx]
        except ValueError:
            # Current session not in list, return first
            return sessions[0] if sessions else None

    def _refresh_session_status(self):
        """Refresh status of all tracked sessions."""
        dead_sessions = []
        for name, session in self.sessions.items():
            if not session.is_alive():
                dead_sessions.append(name)

        # Optionally remove dead sessions from tracking
        # For now, keep them but show as [DEAD]

    def _get_all_tmux_sessions(self) -> List[str]:
        """Get list of all existing tmux sessions."""
        try:
            result = subprocess.run(
                self._tmux_cmd("list-sessions", "-F", "#{session_name}"),
                capture_output=True,
                text=True,
                check=True,
            )
            if result.returncode == 0:
                # Parse output: each line is a session name
                sessions = []
                for line in result.stdout.strip().split("\n"):
                    if line:
                        sessions.append(line)
                return sessions
            return []
        except subprocess.CalledProcessError:
            # No sessions or tmux not running
            return []
        except Exception as e:
            self.logger.error(f"Error listing tmux sessions: {e}")
            return []

    def _discover_existing_sessions(self):
        """Discover existing tmux sessions (optional bootstrap)."""
        try:
            result = subprocess.run(
                self._tmux_cmd("list-sessions", "-F", "#{session_name}"),
                capture_output=True,
                text=True,
            )
            if result.returncode == 0:
                existing = result.stdout.strip().split("\n")
                self.logger.debug(f"Found existing tmux sessions: {existing}")
        except Exception:
            pass

    async def _register_status_widget(self) -> None:
        """Register tmux widget with the new widget system."""
        try:
            # Get widget API from app
            if hasattr(self, "app") and self.app:
                widget_api = self.app.get_widget_api()
                if widget_api:
                    widget_api.register_widget(
                        id="tmux-sessions",
                        name="Tmux Sessions",
                        description="Active tmux session count and names",
                        render_fn=self._render_tmux_widget,
                        default_width="auto",
                        min_width=8,
                    )
                    logger.info("Registered tmux-sessions status widget")
                else:
                    logger.debug("Widget API not available")
            else:
                logger.debug("App reference not available for widget registration")
        except Exception as e:
            logger.error(f"Failed to register tmux widget: {e}")

    def _render_tmux_widget(self, width: int, context) -> str:
        """Render tmux sessions widget content.

        Args:
            width: Available width in characters
            context: Widget context with services

        Returns:
            Rendered widget string
        """
        from kollabor_tui.design_system import T

        def _fg(text: str, color: tuple) -> str:
            r, g, b = color
            return f"\033[38;2;{r};{g};{b}m{text}\033[39m"

        if not self.enabled:
            return _fg("tmux: off", T().text_dim)

        session_count = len(self.sessions)
        if session_count == 0:
            return _fg("0 tmux", T().text_dim)

        # Show count and up to 2 session names
        session_names = list(self.sessions.keys())[:2]
        names_text = " ".join(session_names)

        if session_count > 2:
            return (
                _fg(f"{session_count} tmux", T().text)
                + " "
                + _fg(names_text, T().text_dim)
                + _fg(f" +{session_count - 2}", T().text_dim)
            )
        else:
            return (
                _fg(f"{session_count} tmux", T().text)
                + " "
                + _fg(names_text, T().text_dim)
            )

    def _get_tmux_sessions_content(self) -> List[str]:
        """Get tmux sessions content for status view (agnoster style)."""
        try:
            seg = AgnosterSegment()

            if not self.enabled:
                seg.add_neutral("Tmux: Disabled", "dark")
                return [seg.render()]

            # Get all tmux sessions
            all_sessions = self._get_all_tmux_sessions()
            session_count = len(all_sessions)

            if session_count == 0:
                seg.add_lime("Tmux", "dark")
                seg.add_neutral("No sessions", "mid")
                return [seg.render()]

            # Build agnoster bar: Tmux | count | session names | hint
            seg.add_lime("Tmux", "dark")
            seg.add_cyan(f"{session_count} active", "dark")

            # Show first few session names
            max_show = 3
            if all_sessions:
                names = all_sessions[:max_show]
                names_str = " | ".join(names)
                if session_count > max_show:
                    names_str += f" +{session_count - max_show}"
                seg.add_lime(names_str)

            seg.add_neutral("/terminal", "mid")

            return [seg.render()]

        except Exception as e:
            logger.error(f"Error getting tmux sessions content: {e}")
            seg = AgnosterSegment()
            seg.add_neutral("Tmux: Error", "dark")
            return [seg.render()]

    def get_status_line(self) -> Dict[str, List[str]]:
        """Get status line (no longer used - using status view instead)."""
        # Return empty - we use the dedicated status view now
        return {"A": [], "B": [], "C": []}

    # ===== Public API for ToolExecutor Integration =====

    async def execute_foreground(
        self, command: str, timeout: int = 90, cwd: str = None
    ) -> Dict[str, Any]:
        """Execute command in temporary tmux session, capture output, destroy session.

        Uses remain-on-exit to detect when command completes.

        Args:
            command: Shell command to execute
            timeout: Timeout in seconds
            cwd: Working directory (optional)

        Returns:
            Dict with: success, output, error, exit_code
        """
        import random
        import string
        import time

        # Generate temp session name
        random_suffix = "".join(
            random.choices(string.ascii_lowercase + string.digits, k=8)
        )
        session_name = f"kollab-temp-{random_suffix}"

        try:
            # Create session with remain-on-exit so pane stays after command completes
            # Run the command directly (not in a shell that stays open)
            create_cmd = self._tmux_cmd(
                "new-session",
                "-d",
                "-s",
                session_name,
                "-x",
                "200",
                "-y",
                "50",  # Set reasonable size
            )
            if cwd and cwd.strip():
                create_cmd.extend(["-c", cwd])

            result = subprocess.run(
                create_cmd, capture_output=True, text=True, timeout=5
            )
            if result.returncode != 0:
                return {
                    "success": False,
                    "output": "",
                    "error": f"Failed to create session: {result.stderr}",
                    "exit_code": result.returncode,
                }

            # Set remain-on-exit so pane becomes "dead" when command finishes
            subprocess.run(
                self._tmux_cmd(
                    "set-option", "-t", session_name, "remain-on-exit", "on"
                ),
                capture_output=True,
                timeout=5,
            )

            # Send command wrapped to exit the shell when done
            # This makes the pane "dead" when command completes
            wrapped_cmd = f"{command}; exit $?"
            subprocess.run(
                self._tmux_cmd("send-keys", "-t", session_name, wrapped_cmd, "Enter"),
                capture_output=True,
                timeout=5,
            )

            # Poll for completion (check if pane is dead)
            start_time = time.time()
            while time.time() - start_time < timeout:
                # Check if pane is dead (command completed)
                check_result = subprocess.run(
                    self._tmux_cmd(
                        "list-panes", "-t", session_name, "-F", "#{pane_dead}"
                    ),
                    capture_output=True,
                    text=True,
                    timeout=2,
                )

                if check_result.returncode == 0:
                    pane_dead = check_result.stdout.strip()
                    if pane_dead == "1":
                        # Command completed
                        break

                await asyncio.sleep(0.1)  # Check more frequently
            else:
                # Timeout - kill session and return error
                subprocess.run(
                    self._tmux_cmd("kill-session", "-t", session_name),
                    capture_output=True,
                    timeout=5,
                )
                return {
                    "success": False,
                    "output": "",
                    "error": f"Command timed out after {timeout}s",
                    "exit_code": -1,
                }

            # Capture output and clean it up
            output_lines = self._capture_tmux_pane(session_name, max_lines=None)
            output = self._clean_foreground_output(output_lines, command)

            # Get exit code from pane (pane_dead_status contains exit code)
            exit_code_result = subprocess.run(
                self._tmux_cmd(
                    "display-message", "-t", session_name, "-p", "#{pane_dead_status}"
                ),
                capture_output=True,
                text=True,
                timeout=2,
            )
            exit_code = 0
            if exit_code_result.returncode == 0:
                status_str = exit_code_result.stdout.strip()
                if status_str:
                    try:
                        exit_code = int(status_str)
                    except ValueError:
                        exit_code = 0

            # Kill session
            subprocess.run(
                self._tmux_cmd("kill-session", "-t", session_name),
                capture_output=True,
                timeout=5,
            )

            return {
                "success": exit_code == 0,
                "output": output,
                "error": (
                    "" if exit_code == 0 else f"Command exited with code {exit_code}"
                ),
                "exit_code": exit_code,
            }

        except subprocess.TimeoutExpired:
            # Clean up session
            try:
                subprocess.run(
                    self._tmux_cmd("kill-session", "-t", session_name),
                    capture_output=True,
                    timeout=5,
                )
            except Exception:
                pass
            return {
                "success": False,
                "output": "",
                "error": "Operation timed out",
                "exit_code": -1,
            }
        except Exception as e:
            self.logger.error(f"Error in execute_foreground: {e}")
            # Clean up session
            try:
                subprocess.run(
                    self._tmux_cmd("kill-session", "-t", session_name),
                    capture_output=True,
                    timeout=5,
                )
            except Exception:
                pass
            return {"success": False, "output": "", "error": str(e), "exit_code": -1}

    async def execute_background(
        self, command: str, name: str = None, timeout: str = None, cwd: str = None
    ) -> Dict[str, Any]:
        """Execute command in persistent tmux session.

        Args:
            command: Shell command to execute
            name: Session name (auto-generated if None)
            timeout: Timeout string like "30s", "5m", "1h" (optional)
            cwd: Working directory (optional)

        Returns:
            Dict with: success, session_name, message
        """
        # Generate name if not provided
        if not name:
            name = self._generate_session_name(command)

        # Check collision
        if self._session_exists(name):
            return {
                "success": False,
                "session_name": name,
                "message": f"Session '{name}' already exists",
            }

        try:
            # Create session
            create_cmd = self._tmux_cmd("new-session", "-d", "-s", name)
            if cwd and cwd.strip():
                create_cmd.extend(["-c", cwd])

            result = subprocess.run(
                create_cmd, capture_output=True, text=True, timeout=5
            )
            if result.returncode != 0:
                return {
                    "success": False,
                    "session_name": name,
                    "message": f"Failed to create session: {result.stderr}",
                }

            # Send command
            subprocess.run(
                self._tmux_cmd("send-keys", "-t", name, command, "Enter"),
                capture_output=True,
                timeout=5,
            )

            # Add to registry
            self.sessions[name] = TmuxSession(
                name=name, command=command, tmux_cmd=self._tmux_cmd
            )

            # Schedule auto-kill if timeout provided
            if timeout:
                timeout_seconds = self._parse_timeout(timeout)
                if timeout_seconds > 0:
                    asyncio.create_task(self._auto_kill_after(name, timeout_seconds))

            return {
                "success": True,
                "session_name": name,
                "message": f"Background session '{name}' started",
            }

        except Exception as e:
            self.logger.error(f"Error in execute_background: {e}")
            return {"success": False, "session_name": name, "message": str(e)}

    async def get_session_status(self, name: str) -> Dict[str, Any]:
        """Get status of background session.

        Args:
            name: Session name (use "*" for all sessions)

        Returns:
            Dict with session status information
        """
        if name == "*":
            # List all sessions
            all_sessions = self._get_all_tmux_sessions()
            sessions_info = []
            for session_name in all_sessions:
                if session_name in self.sessions:
                    session = self.sessions[session_name]
                    sessions_info.append(
                        {
                            "name": session_name,
                            "command": session.command,
                            "created_at": session.created_at.isoformat(),
                            "status": "alive" if session.is_alive() else "dead",
                        }
                    )
                else:
                    sessions_info.append(
                        {
                            "name": session_name,
                            "command": "unknown",
                            "created_at": "unknown",
                            "status": "external",
                        }
                    )
            return {"success": True, "sessions": sessions_info}
        else:
            # Single session status
            if name in self.sessions:
                session = self.sessions[name]
                return {
                    "success": True,
                    "name": name,
                    "command": session.command,
                    "created_at": session.created_at.isoformat(),
                    "status": "alive" if session.is_alive() else "dead",
                }
            elif self._session_exists(name):
                return {
                    "success": True,
                    "name": name,
                    "command": "unknown",
                    "created_at": "unknown",
                    "status": "external",
                }
            else:
                return {"success": False, "message": f"Session '{name}' not found"}

    async def capture_session_output(
        self, name: str, lines: int = 50
    ) -> Dict[str, Any]:
        """Capture output from background session.

        Args:
            name: Session name
            lines: Number of lines to capture

        Returns:
            Dict with: success, output (list of lines)
        """
        if not self._session_exists(name):
            return {
                "success": False,
                "output": [],
                "message": f"Session '{name}' not found",
            }

        try:
            output_lines = self._capture_tmux_pane(name, max_lines=lines)
            return {"success": True, "output": output_lines}
        except Exception as e:
            self.logger.error(f"Error capturing session output: {e}")
            return {"success": False, "output": [], "message": str(e)}

    async def kill_background_session(
        self, name: str, signal: str = "SIGTERM"
    ) -> Dict[str, Any]:
        """Kill background session.

        Args:
            name: Session name (use "*" to kill all managed sessions)
            signal: Signal to send (default: SIGTERM)

        Returns:
            Dict with: success, message
        """
        if name == "*":
            # Kill all managed sessions
            killed = []
            failed = []
            for session_name in list(self.sessions.keys()):
                try:
                    result = subprocess.run(
                        self._tmux_cmd("kill-session", "-t", session_name),
                        capture_output=True,
                        text=True,
                        timeout=5,
                    )
                    if result.returncode == 0:
                        killed.append(session_name)
                        del self.sessions[session_name]
                    else:
                        failed.append(session_name)
                except Exception as e:
                    self.logger.error(f"Error killing session {session_name}: {e}")
                    failed.append(session_name)

            if failed:
                return {
                    "success": False,
                    "message": f"Killed {len(killed)} sessions, failed: {', '.join(failed)}",
                }
            else:
                return {"success": True, "message": f"Killed {len(killed)} sessions"}
        else:
            # Kill single session
            try:
                result = subprocess.run(
                    self._tmux_cmd("kill-session", "-t", name),
                    capture_output=True,
                    text=True,
                    timeout=5,
                )

                if result.returncode == 0:
                    if name in self.sessions:
                        del self.sessions[name]
                    return {"success": True, "message": f"Killed session '{name}'"}
                else:
                    return {
                        "success": False,
                        "message": f"Failed to kill session: {result.stderr}",
                    }
            except Exception as e:
                self.logger.error(f"Error killing session: {e}")
                return {"success": False, "message": str(e)}

    # ===== Helper Methods =====

    def _generate_session_name(self, command: str) -> str:
        """Generate session name from command.

        Pattern: {sanitized_cmd[:8]}-{timestamp}
        Example: "npm run dev" -> "npm-run-d-123456"
        """
        import re
        import time

        # Sanitize command
        sanitized = re.sub(r"[^a-zA-Z0-9]", "-", command)[:8]
        # Get timestamp
        timestamp = str(int(time.time()))[-6:]

        return f"{sanitized}-{timestamp}"

    def _parse_timeout(self, timeout_str: str) -> int:
        """Parse timeout string to seconds.

        Examples: "30s" -> 30, "5m" -> 300, "1h" -> 3600
        """
        import re

        match = re.match(r"^(\d+)([smh]?)$", timeout_str.lower())
        if not match:
            self.logger.warning(f"Invalid timeout format: {timeout_str}")
            return 0

        value = int(match.group(1))
        unit = match.group(2) or "s"

        if unit == "s":
            return value
        elif unit == "m":
            return value * 60
        elif unit == "h":
            return value * 3600
        else:
            return 0

    def _clean_foreground_output(self, lines: List[str], command: str) -> str:
        """Clean up foreground execution output.

        Removes:
        - Shell prompts
        - Command echo
        - Empty lines at start/end
        - "Pane is dead" message

        Args:
            lines: Raw output lines from tmux pane
            command: The command that was executed

        Returns:
            Cleaned output string
        """
        if not lines:
            return ""

        cleaned = []
        wrapped_cmd = f"{command}; exit $?"

        for line in lines:
            # Skip "Pane is dead" message
            if line.startswith("Pane is dead"):
                continue

            # Skip the wrapped command if it appears at start
            if line.strip() == wrapped_cmd or line.strip() == command:
                continue

            # Skip prompt lines (common prompt patterns)
            # Look for common prompt indicators
            if any(indicator in line for indicator in ["❯", "➜", "$ ", "> ", "# "]):
                # This might be a prompt line, check if it contains our command
                if wrapped_cmd in line or command in line:
                    continue

            # Skip lines that look like shell decorations (powerline, starship, etc.)
            # These often contain special unicode characters
            if "\ue0a0" in line or "\ue718" in line or "📦" in line:
                continue

            cleaned.append(line)

        # Strip leading/trailing empty lines
        while cleaned and not cleaned[0].strip():
            cleaned.pop(0)
        while cleaned and not cleaned[-1].strip():
            cleaned.pop()

        return "\n".join(cleaned)

    async def _auto_kill_after(self, session_name: str, seconds: int):
        """Schedule auto-kill task for session."""
        try:
            await asyncio.sleep(seconds)
            # Check if session still exists
            if self._session_exists(session_name):
                self.logger.info(
                    f"Auto-killing session '{session_name}' after {seconds}s timeout"
                )
                await self.kill_background_session(session_name)
        except asyncio.CancelledError:
            # Task was cancelled (normal during shutdown)
            pass
        except Exception as e:
            self.logger.error(f"Error in auto-kill task for {session_name}: {e}")

    async def shutdown(self) -> None:
        """Shutdown the plugin."""
        try:
            # Clear session tracking
            self._current_session = None
            self.logger.info("Tmux plugin shutdown completed")

        except Exception as e:
            self.logger.error(f"Error shutting down tmux plugin: {e}")

    async def register_hooks(self) -> None:
        """Register event hooks."""
        # Could register hooks for live modal input handling
        pass

    @staticmethod
    def get_config_widgets() -> Optional[Dict[str, Any]]:
        """Get configuration widgets for the config modal."""
        return {
            "title": "Terminal Settings",
            "widgets": [
                {
                    "type": "checkbox",
                    "label": "Show Status",
                    "config_path": "plugins.tmux.show_status",
                    "help": "Show tmux session count in status bar",
                },
                {
                    "type": "slider",
                    "label": "Refresh Rate (ms)",
                    "config_path": "plugins.tmux.refresh_rate_ms",
                    "min_value": 50,
                    "max_value": 1000,
                    "step": 50,
                    "help": "Live view refresh rate in milliseconds",
                },
                {
                    "type": "slider",
                    "label": "Capture Lines",
                    "config_path": "plugins.tmux.capture_lines",
                    "min_value": 10,
                    "max_value": 1000,
                    "step": 10,
                    "help": "Number of lines to capture from pane history",
                },
                {
                    "type": "checkbox",
                    "label": "Use Separate Server",
                    "config_path": "plugins.tmux.use_separate_server",
                    "help": "Use dedicated tmux server (isolated from your main tmux)",
                },
                {
                    "type": "text_input",
                    "label": "Socket Name",
                    "config_path": "plugins.tmux.socket_name",
                    "help": "Socket name for separate tmux server (default: kollabor)",
                },
            ],
        }
