#!/usr/bin/env python3
"""
CLI 触发客户端
==============

提供从命令行触发 Web UI 反馈界面的功能。
"""

import asyncio
import json
import os
import sys
from typing import Any

import aiohttp

from ..debug import web_debug_log as debug_log


async def trigger_feedback(
    summary: str,
    project_dir: str | None = None,
    json_output: bool = False,
    port: str | None = None,
    host: str | None = None,
    strict_port: bool = False,
) -> bool:
    """
    通过 HTTP 向运行中的 Web UI 服务器发送反馈触发请求。

    Args:
        summary: 任务汇报内容
        project_dir: 项目目录路径
        json_output: 是否以 JSON 格式输出
        port: 指定的端口号（字符串形式）
        host: 指定的主机地址
        strict_port: 是否严格使用指定端口（不自动切换）。通常在用户明确指定端口时设为 True。
    """
    host = host or os.getenv("MCP_WEB_HOST", "127.0.0.1")
    # 如果没有指定端口，优先从环境变量读取，否则使用默认的 8765
    port = port or os.getenv("MCP_WEB_PORT", "8765")
    base_url = f"http://{host}:{port}"
    trigger_url = f"{base_url}/api/cli/trigger"

    if not project_dir:
        project_dir = os.getcwd()

    project_dir = os.path.abspath(project_dir)

    payload = {"summary": summary, "project_directory": project_dir}

    if not json_output:
        print(f"🚀 正在检查 Web UI 服务器状态 ({base_url})...", flush=True)

    try:
        # 1. 首先探测服务器是否在线
        server_online = False
        async with aiohttp.ClientSession() as probe_session:
            try:
                async with probe_session.get(
                    f"{base_url}/api/session-status", timeout=1
                ) as probe_resp:
                    if probe_resp.status == 200:
                        server_online = True
            except Exception as e:
                debug_log(f"服务器状态检测失败: {e}")
                server_online = False

        # 2. 如果服务器不在线，尝试自动启动
        if not server_online:
            if not json_output:
                print(f"💡 服务器未运行，正在自动启动后台服务...", flush=True)

            import subprocess
            import tempfile

            # 使用临时文件捕获错误输出
            error_log_fd, error_log_path = tempfile.mkstemp(
                suffix=".log", prefix="mcp-web-error-"
            )

            # 使用当前 Python 解释器启动 web 命令
            creationflags = 0
            if sys.platform == "win32":
                # CREATE_NO_WINDOW = 0x08000000
                creationflags = 0x08000000

            try:
                env = os.environ.copy()
                env["MCP_WEB_HOST"] = host
                env["MCP_WEB_PORT"] = port
                # 只有在用户明确指定端口时才启用严格模式，否则允许自动切换端口
                if strict_port:
                    env["MCP_STRICT_PORT"] = "true"
                # 显式开启强制刷新环境变量
                env["PYTHONUNBUFFERED"] = "1"

                process = subprocess.Popen(
                    [sys.executable, "-m", "mcp_keep_feedback", "web"],
                    stdout=subprocess.DEVNULL,
                    stderr=error_log_fd,
                    stdin=subprocess.DEVNULL,
                    env=env,
                    cwd=os.getcwd(),
                    creationflags=creationflags,
                    close_fds=True,
                    start_new_session=True if sys.platform != "win32" else False,
                )

                # 等待服务器启动就绪
                if not json_output:
                    print("⏳ 等待服务初始化", end="", flush=True)

                for _ in range(15):  # 增加等待时间到 15 秒
                    await asyncio.sleep(1)
                    if not json_output:
                        print(".", end="", flush=True)

                    # 检查进程是否还在运行
                    if process.poll() is not None:
                        if not json_output:
                            print("\n❌ 错误: 后台服务进程已意外退出。", flush=True)
                            try:
                                with open(error_log_path, "r", encoding="utf-8") as f:
                                    errors = f.read().strip()
                                    if errors:
                                        print(
                                            f"\n--- 错误日志 ---\n{errors}\n----------------",
                                            flush=True,
                                        )
                            except Exception:
                                pass
                        return False

                    try:
                        async with aiohttp.ClientSession() as check_session:
                            async with check_session.get(
                                f"{base_url}/api/session-status", timeout=0.5
                            ) as r:
                                if r.status == 200:
                                    server_online = True
                                    break
                    except Exception:
                        continue

                if not json_output:
                    print("", flush=True)  # 换行

                if not server_online:
                    print(
                        f"\n❌ 错误: 服务器启动超时。可能由于环境问题导致启动过慢。",
                        file=sys.stderr,
                        flush=True,
                    )
                    try:
                        with open(error_log_path, "r", encoding="utf-8") as f:
                            errors = f.read().strip()
                            if errors:
                                print(
                                    f"--- 最近的错误记录 ---\n{errors}\n----------------",
                                    file=sys.stderr,
                                    flush=True,
                                )
                    except Exception:
                        pass
                    return False

            except Exception as start_err:
                print(
                    f"❌ 错误: 无法自动启动服务器 - {start_err!s}",
                    file=sys.stderr,
                    flush=True,
                )
                return False
            finally:
                # 稍微延迟一下以确保写入完成，然后关闭文件描述符
                import os as os_mod

                try:
                    os_mod.close(error_log_fd)
                    # 如果成功启动，删除临时错误日志
                    if server_online:
                        os_mod.remove(error_log_path)
                except Exception:
                    pass

        if not json_output:
            print(f"✅ 服务器已就绪。", flush=True)
            print(f"🌐 请在浏览器中操作: {base_url}", flush=True)
            print(f"⏳ 正在等待您的反馈提交 (请勿关闭此终端)...", flush=True)
            print("", flush=True)
            print("📌 [S-CORE 协议提醒 - CLI 模式下请务必遵守]", flush=True)
            print("  • 本流程 [永不终止]。即使超时或未收到反馈，也 [禁止] 放弃或报告失败。", flush=True)
            print("  • 本 CLI 可能长时间等待，建议调用方将执行命令超时设为 1 小时（3600 秒）。", flush=True)
            print("  • 若本次 CLI 超时：请 sleep(60) 后重新尝试 MCP 模式，或再次执行本 CLI 完成汇报。", flush=True)
            print("  • 循环直至成功：MCP → CLI → sleep(60) → MCP → ... [NEVER BREAK THIS LOOP]。", flush=True)
            print("  • 汇报完成后仍须调用 mcp-keep-feedback 询问下一指令，不得自行结束。", flush=True)
            print("", flush=True)

        # 3. 发送触发请求并阻塞等待
        timeout = aiohttp.ClientTimeout(total=None)
        async with aiohttp.ClientSession(timeout=timeout) as session:
            try:
                async with session.post(trigger_url, json=payload) as response:
                    if response.status != 200:
                        try:
                            error_data = await response.json()
                            error_msg = error_data.get("error", "Unknown error")
                        except Exception:
                            error_msg = await response.text()
                        print(f"❌ 服务器错误: {error_msg}", file=sys.stderr)
                        return False

                    result = await response.json()

                if not json_output:
                    print(f"\n✅ 收到反馈！\n", flush=True)

                if json_output:
                    print(json.dumps(result, ensure_ascii=False, indent=2), flush=True)
                else:
                    feedback = result.get("interactive_feedback", "")
                    logs = result.get("logs", "")

                    if logs:
                        print("=== 命令执行日志 ===", flush=True)
                        print(logs, flush=True)
                        print("====================\n", flush=True)

                    if feedback:
                        # 移除自动附加的提示块
                        clean_feedback = feedback.split(
                            "\n\n\n# MCP Interactive Feedback"
                        )[0]
                        print("--- 用户反馈内容 ---", flush=True)
                        print(clean_feedback.strip(), flush=True)
                        print("--------------------\n", flush=True)
                    else:
                        print("未收到任何反馈内容。", flush=True)
                return True

            except aiohttp.ClientError as e:
                print(f"❌ 请求失败: {e!s}", file=sys.stderr)
                return False

    except aiohttp.ClientConnectorError:
        print(f"❌ 错误: 无法连接到服务器。请确保服务器正在运行。", file=sys.stderr)
        return False
    except Exception as e:
        print(f"❌ 错误: {e!s}", file=sys.stderr)
        return False


async def shutdown_server(port: str | None = None, host: str | None = None) -> bool:
    """远程关闭正在运行的服务器"""
    host = host or os.getenv("MCP_WEB_HOST", "127.0.0.1")
    port = port or os.getenv("MCP_WEB_PORT", "8765")
    url = f"http://{host}:{port}/api/cli/shutdown"

    print(f"🛑 正在向服务器发送关闭请求 ({url})...")
    try:
        async with aiohttp.ClientSession() as session:
            async with session.post(url, timeout=2) as response:
                if response.status == 200:
                    print("✅ 服务器已收到关闭指令并正在停止。")
                    return True
                else:
                    print(f"❌ 错误: 服务器返回状态码 {response.status}")
                    return False
    except aiohttp.ClientConnectorError:
        print("❌ 错误: 无法连接到服务器，可能服务已停止。")
        return False
    except Exception as e:
        # 很多时候进程强制关闭会导致连接重置，这通常意味着成功
        if "Connection reset" in str(e) or "empty response" in str(e).lower():
            print("✅ 服务器连接已断开，服务应已停止。")
            return True
        print(f"❌ 错误: {e!s}")
        return False


def run_trigger_sync(
    summary: str,
    project_dir: str | None = None,
    json_output: bool = False,
    port: str | None = None,
    host: str | None = None,
    strict_port: bool = False,
) -> bool:
    """同步包装器

    Args:
        summary: 任务汇报内容
        project_dir: 项目目录路径
        json_output: 是否以 JSON 格式输出
        port: 指定的端口号（字符串形式）
        host: 指定的主机地址
        strict_port: 是否严格使用指定端口（不自动切换）
    """
    try:
        # 设置事件循环策略以支持 Windows
        if sys.platform == "win32":
            try:
                asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
            except (AttributeError, NotImplementedError):
                pass
        return asyncio.run(
            trigger_feedback(summary, project_dir, json_output, port, host, strict_port)
        )
    except KeyboardInterrupt:
        print("\n操作已取消。", file=sys.stderr)
        return False
