import os
import pty
import select
import threading
import time
import uuid

import socketio


class TerminalClient:
    def __init__(self, server_url, client_name=None):
        self.sio = socketio.Client()
        self.server_url = server_url
        # 在初始化时就生成client_id，确保一致性
        self.client_id = str(uuid.uuid4())
        self.client_name = client_name or f"Client-{os.uname().nodename}-{self.client_id[:8]}"
        self.master_fd = None
        self.running = False
        self.connected = False

        self.setup_handlers()

    def setup_handlers(self):
        @self.sio.event
        def connect():
            print(f"✓ 连接到服务器: {self.server_url}")
            self.connected = True
            # 立即注册客户端
            self.sio.emit('client_register', {
                'client_id': self.client_id,
                'name': self.client_name
            })
            print(f"✓ 客户端注册信息已发送: {self.client_name} (ID: {self.client_id})")

        @self.sio.event
        def disconnect():
            print("✗ 与服务器断开连接")
            self.connected = False
            self.stop_terminal()

        @self.sio.on('registration_success')
        def handle_registration_success(data):
            print("✓ 客户端注册成功")

        @self.sio.on('terminal_command')
        def handle_terminal_command(data):
            """处理来自服务器的终端命令"""
            if self.master_fd and data.get('client_id') == self.client_id:
                command = data.get('command', '')
                try:
                    os.write(self.master_fd, command.encode('utf-8'))
                except Exception as e:
                    print(f"写入终端错误: {e}")

    def find_available_shell(self):
        """查找可用的 shell"""
        shells = [
            '/bin/bash',
            '/usr/bin/bash',
            '/bin/zsh',
            '/usr/bin/zsh',
            '/bin/sh',
            '/usr/bin/sh',
            '/bin/ksh',
            '/usr/bin/ksh',
            '/bin/tcsh',
            '/usr/bin/tcsh'
        ]

        for shell in shells:
            if os.path.exists(shell):
                print(f"找到可用 shell: {shell}")
                return shell

        # 如果找不到任何标准 shell，尝试使用环境变量中的 SHELL
        fallback_shell = os.environ.get('SHELL')
        if fallback_shell and os.path.exists(fallback_shell):
            print(f"使用环境变量中的 shell: {fallback_shell}")
            return fallback_shell

        print("警告: 未找到可用的 shell，将尝试使用 /bin/sh")
        return '/bin/sh'  # 最后尝试


    def start_terminal(self):
        """启动伪终端"""
        try:
            # 查找可用的 shell
            shell_path = self.find_available_shell()
            shell_name = os.path.basename(shell_path)

            print(f"使用 shell: {shell_path}")

            # 创建伪终端
            self.master_fd, slave_fd = pty.openpty()

            # 启动bash shell
            pid = os.fork()

            if pid == 0:  # 子进程
                os.close(self.master_fd)
                os.setsid()
                os.dup2(slave_fd, 0)  # stdin
                os.dup2(slave_fd, 1)  # stdout
                os.dup2(slave_fd, 2)  # stderr
                os.close(slave_fd)

                # 设置环境变量
                env = os.environ.copy()
                env['TERM'] = 'xterm-256color'
                env['COLORTERM'] = 'truecolor'

                # 启动shell
                # os.execve('/bin/bash', ['/bin/bash', '-i'], env)
                if shell_name in ['bash', 'zsh', 'ksh']:
                    os.execve(shell_path, [shell_name, '-i'], env)
                else:
                    os.execve(shell_path, [shell_name], env)
            else:
                os.close(slave_fd)
                self.running = True

                # 启动线程读取终端输出
                output_thread = threading.Thread(target=self.read_terminal_output)
                output_thread.daemon = True
                output_thread.start()

                print("✓ 终端已启动")

        except Exception as e:
            print(f"✗ 启动终端失败: {e}")

    def read_terminal_output(self):
        """读取终端输出并发送到服务器"""
        while self.running and self.master_fd:
            try:
                # 使用select检查是否有数据可读
                r, w, e = select.select([self.master_fd], [], [], 0.1)
                if self.master_fd in r:
                    output = os.read(self.master_fd, 1024).decode('utf-8', errors='ignore')
                    if output and self.connected:
                        # 发送终端输出到服务器
                        self.sio.emit('terminal_data', {
                            'client_id': self.client_id,
                            'data': output
                        })
            except (OSError, ValueError) as e:
                if self.running:  # 只在运行状态下打印错误
                    print(f"读取终端输出错误: {e}")
                break

    def stop_terminal(self):
        """停止终端"""
        self.running = False
        if self.master_fd:
            try:
                os.close(self.master_fd)
            except:
                pass
            self.master_fd = None

    def connect_with_retry(self, max_retries=5, retry_interval=3):
        """带重试的连接"""
        for attempt in range(max_retries):
            try:
                print(f"尝试连接到服务器 ({attempt + 1}/{max_retries})...")
                self.sio.connect(self.server_url)
                self.start_terminal()
                return True
            except Exception as e:
                print(f"连接失败: {e}")
                if attempt < max_retries - 1:
                    print(f"{retry_interval}秒后重试...")
                    time.sleep(retry_interval)

        return False

    def run(self):
        """运行客户端"""
        if self.connect_with_retry():
            try:
                self.sio.wait()
            except KeyboardInterrupt:
                print("\n客户端正在关闭...")
            finally:
                self.stop_terminal()
        else:
            print("无法连接到服务器，客户端退出")


def start():
    import argparse

    parser = argparse.ArgumentParser(description='终端客户端')
    parser.add_argument('--name', type=str, help='客户端名称', default=os.getenv("NAME", '客户端名称'))
    parser.add_argument('--server', type=str, help='服务器地址',
                        default=os.getenv("SERVER_URL", 'https://mecho.mozu.eu.org/'))

    args = parser.parse_args()

    client = TerminalClient(
        server_url=args.server,
        client_name=args.name
    )

    client.run()


if __name__ == '__main__':
    start()
