"""
MyAgent Tray Manager - 托盘管理器

功能:
  - 自动启动 MyAgent 服务
  - 显示服务运行状态 (绿色=运行, 红色=停止)
  - 启动/停止服务控制
  - 打开管理后台
  - 打开聊天界面
  - 开机自启设置 (Windows Registry)
  - 退出应用

参考: fund 项目的 tray_manager.py 实现
"""

import os
import sys
import time
import psutil
import threading
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Optional

# Detect if running under pythonw (no console)
IS_PYTHONW = (
    getattr(sys, 'frozen', False)  # pyinstaller
    or sys.executable.endswith('pythonw.exe')
    or sys.executable.endswith('pythonw3.exe')
    or not sys.stdout  # no console attached
)

try:
    import pystray
    from pystray import MenuItem as Item, Menu
    from PIL import Image, ImageDraw
except ImportError:
    _msg = "Error: Missing tray dependencies. Install with: pip install pystray Pillow psutil"
    print(_msg)
    try:
        with open(Path(__file__).parent / "tray_manager.log", "a", encoding="utf-8") as _f:
            _f.write(f"[{datetime.now()}] [FATAL] {_msg}\n")
    except Exception:
        pass
    try:
        import ctypes
        ctypes.windll.user32.MessageBoxW(0, _msg, "MyAgent 托盘管理器 - 错误", 0x10)
    except Exception:
        pass
    sys.exit(1)

# ============================================================
# Configuration
# ============================================================

APP_DIR = Path(__file__).parent.absolute()
MAIN_SCRIPT = APP_DIR / "main.py"
ICON_PATH = APP_DIR / "myagent_icon.png"  # AI/机器人风格图标
LOG_FILE = APP_DIR / "tray_manager.log"
SERVICE_LOG = APP_DIR / "service.log"

# MyAgent 服务端口 (从环境变量或默认值读取)
def _read_port_from_env() -> int:
    """读取 MYAGENT_PORT 环境变量或 .env 文件"""
    env_port = os.environ.get("MYAGENT_PORT", "").strip()
    if env_port:
        try:
            return int(env_port)
        except ValueError:
            pass
    try:
        env_path = APP_DIR / ".env"
        if env_path.exists():
            with open(env_path, "r", encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if line.startswith("MYAGENT_PORT="):
                        return int(line.split("=", 1)[1].strip())
    except Exception:
        pass
    return 8767  # 默认端口

SERVICE_PORT = _read_port_from_env()


def _get_python_exe() -> str:
    """获取 python.exe 路径 (即使托盘用 pythonw 运行，服务也需要用 python.exe)"""
    exe = sys.executable
    if exe.lower().endswith('pythonw.exe'):
        python_exe = exe[:-1]  # 去掉 'w' -> python.exe
        if Path(python_exe).exists():
            return python_exe
        parent = Path(exe).parent
        for name in ("python.exe", "python3.exe"):
            candidate = parent / name
            if candidate.exists():
                return str(candidate)
    return exe


def log(message: str, level: str = "INFO"):
    """记录日志到文件和屏幕"""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_msg = f"[{timestamp}] [{level}] {message}"
    if not IS_PYTHONW:
        try:
            print(log_msg)
        except Exception:
            pass
    try:
        with open(LOG_FILE, "a", encoding="utf-8") as f:
            f.write(log_msg + "\n")
    except Exception:
        pass


# ============================================================
# Icon Creation
# ============================================================

def _draw_icon_image(color: str = '#4CAF50', size: int = 64) -> Image.Image:
    """创建托盘图标 (带状态指示点)"""
    # 尝试加载自定义图标
    if ICON_PATH.exists():
        try:
            return Image.open(ICON_PATH).resize((size, size))
        except Exception:
            pass

    # 默认: 圆角矩形 + 状态点
    image = Image.new('RGBA', (size, size), (0, 0, 0, 0))
    draw = ImageDraw.Draw(image)

    # 背景圆角矩形 (深色)
    draw.rounded_rectangle([2, 2, size - 3, size - 3], radius=10, fill='#263238')

    # 中央状态点
    margin = size // 4
    draw.ellipse([margin, margin, size - margin, size - margin], fill=color)

    # 白色高光
    highlight_margin = size // 3
    draw.ellipse([highlight_margin, highlight_margin - 2,
                  highlight_margin + 8, highlight_margin + 6],
                 fill='#FFFFFF80')

    return image


def create_running_icon() -> Image.Image:
    """绿色图标 = 服务运行中"""
    return _draw_icon_image('#4CAF50')


def create_stopped_icon() -> Image.Image:
    """红色图标 = 服务已停止"""
    return _draw_icon_image('#F44336')


# ============================================================
# Service Management (MyAgent)
# ============================================================

def _is_myagent_process(proc) -> bool:
    """检查进程是否为 MyAgent 服务"""
    try:
        if not proc.info['name'] or 'python' not in proc.info['name'].lower():
            return False
        cmdline_list = proc.info['cmdline'] or []
        cmdline_str = ' '.join(cmdline_list)
        if 'main.py' not in cmdline_str:
            return False
        # 匹配项目目录
        app_dir_str = str(APP_DIR)
        app_dir_fwd = app_dir_str.replace('\\', '/')
        app_dir_bwd = app_dir_str.replace('/', '\\')
        return (app_dir_str in cmdline_str
                or app_dir_fwd in cmdline_str
                or app_dir_bwd in cmdline_str
                or (len(cmdline_list) >= 2
                    and Path(cmdline_list[-1]).name == 'main.py'
                    and str(APP_DIR) in str(proc.cwd())))
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        return False


def get_service_state() -> str:
    """检查服务是否运行。返回 'running' 或 'stopped'。"""
    try:
        for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'cwd']):
            try:
                if _is_myagent_process(proc):
                    return "running"
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                continue
    except Exception as e:
        log(f"检查服务状态失败: {e}", "ERROR")
    return "stopped"


def get_service_pid() -> Optional[int]:
    """获取服务进程的 PID，未运行返回 None"""
    try:
        for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'cwd']):
            try:
                if _is_myagent_process(proc):
                    return proc.pid
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                continue
    except Exception:
        pass
    return None


def kill_service_processes() -> int:
    """终止所有 MyAgent 服务进程，返回杀死数量"""
    killed = 0
    for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'cwd']):
        try:
            if _is_myagent_process(proc):
                proc.kill()
                killed += 1
                log(f"终止进程 PID={proc.pid}")
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            continue
    if killed:
        log(f"终止了 {killed} 个服务进程")
    return killed


def start_service() -> bool:
    """启动 MyAgent 服务。返回是否成功。"""
    current_state = get_service_state()
    if current_state == "running":
        log("服务已在运行中，跳过启动")
        return True

    python_exe = _get_python_exe()
    log(f"正在启动 MyAgent 服务: {python_exe} {MAIN_SCRIPT}")

    try:
        # 打开服务日志文件
        log_fd = open(SERVICE_LOG, "w", encoding="utf-8")
        log_fd.write(f"--- MyAgent service started at {datetime.now()} ---\n")
        log_fd.flush()

        # 设置环境变量
        sub_env = os.environ.copy()
        sub_env["PYTHONIOENCODING"] = "utf-8"
        sub_env["PYTHONUTF8"] = "1"
        sub_env["MYAGENT_PORT"] = str(SERVICE_PORT)

        # 启动服务进程 (使用 CREATE_NEW_PROCESS_GROUP 便于后续终止)
        # [v1.48.2] 添加 --host mixed 参数，支持监听本地+局域网IPv4+公网IPv6（不监听公网IPv4）
        proc = subprocess.Popen(
            [python_exe, str(MAIN_SCRIPT), "--host", "mixed"],
            cwd=str(APP_DIR),
            env=sub_env,
            creationflags=(subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_NO_WINDOW)
            if os.name == 'nt' else 0,
            stdout=log_fd,
            stderr=subprocess.STDOUT,
        )
        log(f"服务进程已创建 (PID={proc.pid})")

        # 等待并验证启动
        time.sleep(3)

        # 检查进程是否还活着
        if proc.poll() is not None:
            exit_code = proc.returncode
            log_fd.close()
            error_msg = ""
            try:
                with open(SERVICE_LOG, "r", encoding="utf-8") as f:
                    error_msg = f.read()[-500:]
            except Exception:
                pass
            log(f"服务进程立即退出，退出码: {exit_code}", "ERROR")
            log(f"服务日志尾部: {error_msg}", "ERROR")
            return False

        if get_service_state() == "running":
            log(f"服务启动成功 (PID={proc.pid})")
            log_fd.close()
            return True
        else:
            # 进程存活但未检测到，检查端口
            time.sleep(2)
            if _check_port_listening():
                log(f"服务已启动并监听端口 {SERVICE_PORT} (PID={proc.pid})")
                log_fd.close()
                return True
            log("服务进程存活但未检测到运行状态", "WARN")
            log_fd.close()
            return True
    except Exception as e:
        log(f"启动服务失败: {e}", "ERROR")
        return False


def _check_port_listening() -> bool:
    """检查服务端口是否在监听"""
    try:
        import socket
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(1)
            result = s.connect_ex(('127.0.0.1', SERVICE_PORT))
            return result == 0
    except Exception:
        return False


def stop_service() -> bool:
    """停止服务。返回是否成功终止进程。"""
    log("正在停止 MyAgent 服务...")
    killed = kill_service_processes()
    if killed:
        time.sleep(1)
        return True
    else:
        log("未找到运行中的服务")
        return False


def restart_service() -> bool:
    """重启服务"""
    log("正在重启 MyAgent 服务...")
    stop_service()
    time.sleep(1)
    return start_service()


def _get_chrome_path() -> Optional[str]:
    """获取 Chrome 浏览器路径"""
    # Windows 常见 Chrome 安装路径
    chrome_paths = [
        r"C:\Program Files\Google\Chrome\Application\chrome.exe",
        r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
        os.path.expandvars(r"%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe"),
        os.path.expandvars(r"%PROGRAMFILES%\Google\Chrome\Application\chrome.exe"),
    ]
    for path in chrome_paths:
        if Path(path).exists():
            return path
    return None


def open_url_in_chrome(url: str):
    """在 Chrome 中打开 URL（复用现有窗口，打开新标签页）"""
    chrome_path = _get_chrome_path()
    if chrome_path:
        # 使用 subprocess 启动 Chrome
        # 如果 Chrome 已运行，会在现有窗口中打开新标签页
        # 如果 Chrome 未运行，会启动新窗口
        try:
            subprocess.Popen(
                [chrome_path, url],
                creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
            )
            log(f"在 Chrome 中打开: {url}")
            return True
        except Exception as e:
            log(f"Chrome 启动失败: {e}", "ERROR")
    
    # Chrome 未找到，使用默认浏览器
    import webbrowser
    log(f"Chrome 未找到，使用默认浏览器: {url}")
    webbrowser.open(url)


def open_dashboard():
    """打开管理后台"""
    url = f"http://localhost:{SERVICE_PORT}/ui/"
    log(f"打开管理后台: {url}")
    open_url_in_chrome(url)


def open_chat():
    """打开聊天界面"""
    url = f"http://localhost:{SERVICE_PORT}/ui/chat/chat_container.html"
    log(f"打开聊天界面: {url}")
    open_url_in_chrome(url)


# ============================================================
# Auto-start on Boot (Windows Registry)
# ============================================================

def check_autostart() -> bool:
    """检查是否已启用开机自启"""
    if os.name != 'nt':
        return False
    try:
        import winreg
        key_path = r"Software\Microsoft\Windows\CurrentVersion\Run"
        app_name = "MyAgent"
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_READ)
        try:
            winreg.QueryValueEx(key, app_name)
            winreg.CloseKey(key)
            return True
        except WindowsError:
            winreg.CloseKey(key)
            return False
    except Exception:
        return False


def toggle_autostart() -> bool:
    """切换开机自启状态。返回新状态 (True=已启用)。"""
    if os.name != 'nt':
        log("开机自启仅支持 Windows", "WARN")
        return False

    import winreg
    key_path = r"Software\Microsoft\Windows\CurrentVersion\Run"
    app_name = "MyAgent"

    try:
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_ALL_ACCESS)
        try:
            # 值存在 -> 删除 (禁用)
            winreg.QueryValueEx(key, app_name)
            winreg.DeleteValue(key, app_name)
            winreg.CloseKey(key)
            log("开机自启已禁用")
            return False
        except WindowsError:
            # 值不存在 -> 添加 (启用)
            script_path = str(APP_DIR / "start.bat")
            winreg.SetValueEx(key, app_name, 0, winreg.REG_SZ, script_path)
            winreg.CloseKey(key)
            log("开机自启已启用")
            return True
    except Exception as e:
        log(f"设置开机自启失败: {e}", "ERROR")
        return False


# ============================================================
# Tray Manager Class
# ============================================================

class TrayManager:
    """MyAgent 托盘管理器"""

    def __init__(self):
        self.icon: Optional[pystray.Icon] = None
        self.state: str = "stopped"  # "running" 或 "stopped"
        self.running: bool = True
        self._autostart_enabled: bool = check_autostart()

    # ------ Menu Construction ------

    def _status_text(self) -> str:
        """动态状态文本"""
        if self.state == "running":
            pid = get_service_pid()
            pid_info = f" (PID: {pid})" if pid else ""
            return f"运行状态: ● 已运行{pid_info}"
        else:
            return "运行状态: ○ 已停止"

    def _autostart_text(self) -> str:
        """动态开机自启文本"""
        if self._autostart_enabled:
            return "开机自启: ✓ 已启用"
        else:
            return "开机自启:   未启用"

    def create_menu(self) -> Menu:
        """创建右键菜单"""
        return Menu(
            # ---- 状态 (只读) ----
            Item(lambda *args: self._status_text(), lambda *args: None, enabled=False),

            Menu.SEPARATOR,

            # ---- 开机自启切换 ----
            Item(lambda *args: self._autostart_text(), self._on_toggle_autostart),

            Menu.SEPARATOR,

            # ---- 服务控制 ----
            Item("启动服务", self._on_start),
            Item("停止服务", self._on_stop),

            Menu.SEPARATOR,

            # ---- 打开界面 ----
            Item("管理后台", self._on_open_dashboard),
            Item("聊天界面", self._on_open_chat),

            Menu.SEPARATOR,

            # ---- 退出 ----
            Item("退出", self._on_exit),
        )

    # ------ Menu Handlers ------

    def _on_start(self, icon=None, item=None):
        """启动服务"""
        if self.state == "running":
            log("服务已在运行中")
            return
        if start_service():
            self._update_status()

    def _on_stop(self, icon=None, item=None):
        """停止服务"""
        if self.state == "stopped":
            log("服务已停止")
            return
        if stop_service():
            self._update_status()

    def _on_open_dashboard(self, icon=None, item=None):
        """打开管理后台"""
        if self.state != "running":
            log("服务未运行，正在启动...")
            start_service()
            self._update_status()
            time.sleep(2)
        open_dashboard()

    def _on_open_chat(self, icon=None, item=None):
        """打开聊天界面"""
        if self.state != "running":
            log("服务未运行，正在启动...")
            start_service()
            self._update_status()
            time.sleep(2)
        open_chat()

    def _on_toggle_autostart(self, icon=None, item=None):
        """切换开机自启"""
        self._autostart_enabled = toggle_autostart()
        self._refresh_icon()

    def _on_exit(self, icon=None, item=None):
        """退出托盘管理器"""
        log("正在退出: 停止服务并关闭托盘...")
        stop_service()
        self.running = False
        if self.icon:
            self.icon.visible = False
            self.icon.stop()

    # ------ Status & Icon Updates ------

    def _update_status(self):
        """轮询服务状态并更新图标/提示"""
        self.state = get_service_state()
        self._refresh_icon()

    def _refresh_icon(self):
        """更新图标、提示和菜单文本"""
        if not self.icon:
            return

        # 更新提示
        if self.state == "running":
            pid = get_service_pid()
            pid_info = f" (PID: {pid})" if pid else ""
            self.icon.title = f"MyAgent - 运行中{pid_info}"
            self.icon.icon = create_running_icon()
        else:
            self.icon.title = "MyAgent - 已停止"
            self.icon.icon = create_stopped_icon()

        # 更新开机自启状态
        self._autostart_enabled = check_autostart()

        # 刷新菜单
        self.icon.menu = self.create_menu()

    # ------ Background Monitor ------

    def _status_monitor(self):
        """后台线程: 每 3 秒检查一次服务状态"""
        while self.running:
            try:
                self._update_status()
            except Exception as e:
                log(f"状态监控错误: {e}", "ERROR")
            time.sleep(3)

    # ------ Main Run ------

    def run(self):
        """运行托盘管理器 (阻塞)"""
        log("=" * 50)
        log("MyAgent 托盘管理器启动...")
        log(f"项目目录: {APP_DIR}")
        log(f"服务脚本: {MAIN_SCRIPT}")
        log(f"服务端口: {SERVICE_PORT}")

        # 创建初始图标
        image = create_stopped_icon()
        menu = self.create_menu()

        self.icon = pystray.Icon(
            "myagent",
            image,
            "MyAgent - 启动中...",
            menu
        )

        # 自动启动服务
        log("正在自动启动 MyAgent 服务...")
        start_service()
        self._update_status()

        # 启动后台状态监控线程
        monitor_thread = threading.Thread(target=self._status_monitor, daemon=True)
        monitor_thread.start()

        # 运行托盘图标循环 (阻塞)
        log("托盘图标已激活，右键菜单控制服务")
        try:
            self.icon.run()
        except Exception as e:
            log(f"托盘图标错误: {e}", "ERROR")


# ============================================================
# Entry Point
# ============================================================

def main():
    """主入口"""
    try:
        if os.name != 'nt':
            log("警告: 此托盘管理器为 Windows 设计")
            log("某些功能 (开机自启、进程检测) 在其他平台可能不可用")

        manager = TrayManager()
        manager.run()
    except KeyboardInterrupt:
        log("用户中断")
    except Exception as e:
        import traceback
        tb = traceback.format_exc()
        log(f"致命错误: {e}\n{tb}", "ERROR")
        if IS_PYTHONW and os.name == 'nt':
            try:
                import ctypes
                ctypes.windll.user32.MessageBoxW(
                    0,
                    f"MyAgent 托盘管理器崩溃:\n\n{e}\n\n详见 tray_manager.log",
                    "MyAgent - 致命错误",
                    0x10
                )
            except Exception:
                pass


if __name__ == "__main__":
    main()
