"""Tmux session/window manager.

Wraps all ``tmux`` CLI interactions: session creation, window
management, command sending, and live-window querying.

Every public method returns a value or raises -- no UI imports.
"""

from __future__ import annotations

import subprocess
from pathlib import Path


class TmuxManager:
    """Manages tmux sessions and windows for service isolation."""

    SESSION = "ai_lsc"

    def __init__(self, env: dict[str, str] | None = None) -> None:
        self.env = env

    # -- session lifecycle ------------------------------------------------

    def ensure_session(self) -> None:
        """Create the master session if it does not already exist."""
        subprocess.run(
            f"tmux has-session -t {self.SESSION} 2>/dev/null "
            f"|| tmux new-session -d -s {self.SESSION} -n 'Master' "
            f"2>/dev/null",
            shell=True,
        )

    def window_exists(self, window_name: str) -> bool:
        """Check if a named window exists in the session."""
        return (
            subprocess.run(
                f"tmux list-windows -t {self.SESSION} 2>/dev/null "
                f"| grep '{window_name}'",
                shell=True,
                capture_output=True,
            ).returncode
            == 0
        )

    def kill_window(self, window_name: str) -> None:
        """Safely kill a window (ignores errors if missing)."""
        subprocess.run(
            f"tmux kill-window -t {self.SESSION}:'{window_name}' 2>/dev/null",
            shell=True,
        )

    def create_window(self, window_name: str) -> None:
        """Create a new detached window inside the session.

        Retries up to 5 times with a short sleep when tmux reports
        an index collision (happens when concurrent launches race).
        """
        import time

        for attempt in range(5):
            proc = subprocess.run(
                f"tmux new-window -t {self.SESSION} -n '{window_name}' -d 2>&1",
                shell=True,
                capture_output=True,
                text=True,
            )
            if proc.returncode == 0:
                return
            # If window already exists with this name, that's fine too
            if self.window_exists(window_name):
                return
            # Index collision — wait and retry
            if "index" in proc.stderr and "in use" in proc.stderr:
                time.sleep(0.1 * (attempt + 1))
                continue
            break  # some other error, stop retrying

    def send_command(
        self,
        window_name: str,
        command: str,
        extra_env: str = "",
    ) -> None:
        """Send a command string to a window, optionally prepending env."""
        payload = f"{extra_env} {command}" if extra_env else command
        subprocess.run(
            f"tmux send-keys -t {self.SESSION}:'{window_name}' "
            f"'{payload}' C-m 2>/dev/null",
            shell=True,
        )

    # -- high-level service lifecycle -------------------------------------

    def launch_service(
        self,
        tool_id: str,
        command: str,
        log_file: str,
        dtach_bin: str | None = None,
        base_bin_dir: str = "",
    ) -> None:
        """Isolate a service in its own tmux window (optionally via dtach).

        Parameters
        ----------
        tool_id:
            Service identifier used as the window name.
        command:
            Shell command to run inside the window.
        log_file:
            Path where stdout/stderr should be redirected.
        dtach_bin:
            Path to dtach binary for persistent attach/detach.
        base_bin_dir:
            PATH colon-separated string to prepend.
        """
        window_name = f"{self.SESSION}::{tool_id}"
        track_cmd = f"({command}) > {log_file} 2>&1"

        wrapped = track_cmd
        if dtach_bin:
            socket_path = f"/tmp/{tool_id}.sock"
            wrapped = f"{dtach_bin} -n {socket_path} bash -c '{track_cmd}'"

        self.ensure_session()
        self.kill_window(window_name)
        self.create_window(window_name)

        env_exports = ""
        if base_bin_dir:
            env_exports = f"export PATH={base_bin_dir}:$PATH; "

        self.send_command(window_name, wrapped, extra_env=env_exports)

    def stop_service(self, tool_id: str) -> None:
        """Kill the tmux window for a service."""
        window_name = f"{self.SESSION}::{tool_id}"
        self.kill_window(window_name)

    def is_running(self, tool_id: str) -> bool:
        """Check whether the tmux window for *tool_id* is live."""
        return self.window_exists(f"{self.SESSION}::{tool_id}")

    def attach_cli(self, tool_id: str) -> str:
        """Return a shell fragment that attaches to the service window."""
        return f"tmux attach -t {self.SESSION}:'{self.SESSION}::{tool_id}' || "
