"""Tmux 会话封装器。"""

import subprocess
from pathlib import Path
from typing import Final, Optional

from butler.session.state import PtyState, SessionMode


class TmuxWrapper:
    """tmux 会话封装器，处理底层 PTY 交互。"""

    INTERACTIVE_PROCESSES: Final[frozenset[str]] = frozenset([
        "vim", "nvim", "vi",
        "top", "htop", "btop",
        "less", "more", "man",
        "claude", "aider",
        "python", "node", "irb", "ipython",
    ])

    SPECIAL_KEYS: Final[dict[str, str]] = {
        "C-c": "C-c", "C-d": "C-d", "C-z": "C-z",
        "C-a": "C-a", "C-e": "C-e", "C-u": "C-u",
        "C-k": "C-k", "C-w": "C-w", "C-r": "C-r",
        "Esc": "Escape", "Tab": "Tab",
        "Up": "Up", "Down": "Down", "Left": "Left", "Right": "Right",
        "Enter": "Enter", "Space": "Space",
    }

    def __init__(self, session_prefix: str = "butler_"):
        self.session_prefix = session_prefix

    def create_session(self, user_id: str, seq: int = 1) -> str:
        """为用户创建独立 tmux session。"""
        session_name = f"{self.session_prefix}{user_id}"
        if seq > 1:
            session_name = f"{self.session_prefix}{user_id}_{seq}"
        subprocess.run(
            ["tmux", "new-session", "-d", "-s", session_name],
            check=True,
            capture_output=True,
        )
        return session_name

    def session_exists(self, session_name: str) -> bool:
        """检查 session 是否存在。"""
        result = subprocess.run(
            ["tmux", "has-session", "-t", session_name],
            capture_output=True,
        )
        return result.returncode == 0

    def kill_session(self, session_name: str) -> None:
        """销毁 session。"""
        subprocess.run(
            ["tmux", "kill-session", "-t", session_name],
            capture_output=True,
        )

    def get_or_create_session(self, user_id: str, seq: int = 1) -> str:
        """获取或创建用户 session。"""
        session_name = f"{self.session_prefix}{user_id}"
        if seq > 1:
            session_name = f"{self.session_prefix}{user_id}_{seq}"
        if not self.session_exists(session_name):
            return self.create_session(user_id, seq)
        return session_name

    def send_keys(self, session_name: str, keys: str, enter: bool = True) -> None:
        """向 session 发送按键。"""
        if keys in self.SPECIAL_KEYS:
            tmux_key = self.SPECIAL_KEYS[keys]
            subprocess.run(
                ["tmux", "send-keys", "-t", session_name, tmux_key],
                capture_output=True,
            )
        else:
            subprocess.run(
                ["tmux", "send-keys", "-l", "-t", session_name, keys],
                capture_output=True,
            )
            if enter:
                subprocess.run(
                    ["tmux", "send-keys", "-t", session_name, "Enter"],
                    capture_output=True,
                )

    def send_raw(self, session_name: str, data: str) -> None:
        """发送原始数据（不含 Enter）。"""
        subprocess.run(
            ["tmux", "send-keys", "-l", "-t", session_name, data],
            capture_output=True,
        )

    def capture_pane(self, session_name: str, as_ansi: bool = False, start_line: int = -100) -> str:
        """抓取终端屏幕内容。"""
        cmd = [
            "tmux", "capture-pane",
            "-t", session_name,
            "-p",
            "-S", str(start_line),
        ]
        if as_ansi:
            cmd.append("-e")
        result = subprocess.run(cmd, capture_output=True, text=True)
        return result.stdout

    def get_active_process(self, session_name: str) -> Optional[str]:
        """获取 tmux 窗口中的活跃进程名。"""
        result = subprocess.run(
            ["tmux", "list-panes", "-t", session_name, "-F", "#{pane_pid}"],
            capture_output=True,
            text=True,
        )
        pane_pids = result.stdout.strip().split("\n")
        if not pane_pids or not pane_pids[0]:
            return None

        pane_pid = pane_pids[0]

        try:
            ps_result = subprocess.run(
                ["ps", "--ppid", pane_pid, "-o", "comm=", "--no-headers"],
                capture_output=True,
                text=True,
            )
            processes = ps_result.stdout.strip().split("\n")

            for proc in processes:
                proc = proc.strip()
                if proc and proc not in ("bash", "zsh", "sh", "fish"):
                    return proc
        except Exception:
            pass

        return None

    def get_pty_state(self, session_name: str) -> PtyState:
        """检测 PTY 是否在等待输入。"""
        result = subprocess.run(
            ["tmux", "list-panes", "-t", session_name, "-F", "#{pane_pid}"],
            capture_output=True,
            text=True,
        )
        pane_pids = result.stdout.strip().split("\n")
        if not pane_pids or not pane_pids[0]:
            return PtyState.UNKNOWN

        pane_pid = pane_pids[0]

        try:
            stat_path = Path(f"/proc/{pane_pid}/stat")
            if not stat_path.exists():
                return PtyState.UNKNOWN

            stat_content = stat_path.read_text()
            parts = stat_content.split()
            if len(parts) < 3:
                return PtyState.UNKNOWN

            state = parts[2]

            children_path = Path(f"/proc/{pane_pid}/task/{pane_pid}/children")
            if children_path.exists():
                children = children_path.read_text().strip().split()
                for child_pid in children:
                    if child_pid:
                        child_stat = Path(f"/proc/{child_pid}/stat")
                        if child_stat.exists():
                            child_state = child_stat.read_text().split()[2]
                            if child_state == "R":
                                return PtyState.RUNNING

            if state in ("S", "D"):
                return PtyState.IDLE
            elif state == "T":
                return PtyState.BLOCKED
            else:
                return PtyState.RUNNING

        except (FileNotFoundError, PermissionError, ProcessLookupError):
            return PtyState.UNKNOWN

    def detect_mode(self, session_name: str) -> SessionMode:
        """混合模式检测。"""
        active_proc = self.get_active_process(session_name)

        if active_proc and active_proc in self.INTERACTIVE_PROCESSES:
            return SessionMode.INTERACTIVE

        return SessionMode.NATURAL_LANGUAGE
