"""
Roneia Client - 主入口

启动命令:
  roneia
  roneia --server ws://192.168.1.100:8765/ws --token xxx
  roneia --config config.yaml
"""

import asyncio
import os
import sys
import logging
from pathlib import Path

# 包的根目录
PACKAGE_ROOT = Path(__file__).parent.parent

from .client import RoneiaClient
from .config import load_client_config, RoneiaClientConfig, merge_voice_config


def get_default_skill_dir() -> str:
    """获取默认 Skill 目录"""
    # 优先使用包内的 skills（确保找到正确的 skill 实现）
    package_skills = PACKAGE_ROOT / "skills"
    if package_skills.exists():
        return str(package_skills.resolve())

    # 其次使用当前目录的 skills
    local_skills = Path("./skills")
    if local_skills.exists():
        return str(local_skills.resolve())

    return str(package_skills)


def print_banner():
    """打印启动横幅"""
    print()
    print("=" * 60)
    print("           Roneia - 数字人助手")
    print("=" * 60)
    print()


def print_help():
    """打印帮助信息"""
    print("""
使用方法:
  roneia [选项]

选项:
  --config, -c FILE     配置文件路径
  --server URL          服务器 WebSocket URL
  --token TOKEN         认证令牌
  --skill-dir DIR       Skill 目录
  --no-gui              禁用 GUI 弹窗
  --debug               调试模式

示例:
  roneia --config config.yaml
  roneia --server ws://192.168.1.100:8765/ws --token mytoken

配置文件示例 (config.yaml):
```yaml
server:
  url: "ws://your-server:8765/ws"
  token: "your-token"
```
""")


async def handle_command(client: RoneiaClient, command: str, state: dict) -> None:
    """
    处理命令

    Args:
        client: 客户端实例
        command: 命令字符串
        state: 交互状态字典
    """
    parts = command.strip().split()
    cmd = parts[0].lower()
    args = parts[1:] if len(parts) > 1 else []

    if cmd == "/skills":
        await handle_skills_command(client, args)
    elif cmd == "/persona":
        await handle_persona_command(client, args)
    elif cmd == "/voice":
        await handle_voice_command(client, state)
    elif cmd == "/text":
        state["mode"] = "text"
        print("已切换到文字模式")
    elif cmd == "/status":
        print(f"连接状态: {'已连接' if client.is_connected else '未连接'}")
        print(f"客户端ID: {client.client_id}")
        print(f"当前模式: {'语音' if state.get('mode') == 'voice' else '文字'}")
        enabled = client.get_enabled_skills()
        print(f"已启用的 Skills: {', '.join(enabled) if enabled else '无'}")
    elif cmd == "/help":
        print_command_help()
    else:
        print(f"未知命令: {command}")
        print("输入 /help 查看帮助")


async def handle_voice_command(client: RoneiaClient, state: dict) -> None:
    """处理 /voice 命令，初始化语音模块并切换模式"""
    if state.get("voice_manager") is None:
        try:
            from .voice import VoiceManager
        except ImportError as e:
            print(f"[错误] 语音模块依赖未安装: {e}")
            print("请安装语音依赖: pip install -e \".[voice]\"")
            return

        # 1. 从 Server 拉取语音配置
        print("正在获取语音配置...")
        server_voice_config = await client.request_voice_config()

        # 2. 合并本地配置（本地优先）
        voice_config = merge_voice_config(server_voice_config, client.config.voice)

        if not voice_config.get("enabled", True):
            print("[提示] 语音功能已被 Server 禁用")
            return

        # 3. 延迟初始化 VoiceManager
        try:
            vm = VoiceManager.from_config(voice_config)
            await vm.initialize()
            state["voice_manager"] = vm
        except Exception as e:
            print(f"[错误] 语音模块初始化失败: {e}")
            return

    state["mode"] = "voice"
    print("已切换到语音模式（按 Ctrl+C 切回文字模式）")


def print_command_help():
    """打印命令帮助"""
    print("""
命令:
  /voice                    切换到语音模式
  /text                     切回文字模式
  /skills list              显示云端可用的 Skill
  /skills installed         显示本地已安装的 Skill
  /skills install <name>    安装 Skill
  /skills enable <name>     启用 Skill
  /skills disable <name>    禁用 Skill
  /skills remove <name>     删除本地 Skill
  /persona                  显示当前人设
  /persona set              设置新人设（多行输入）
  /persona reset            恢复默认人设
  /status                   显示连接状态
  /help                     显示帮助
  quit                      退出
""")


async def handle_persona_command(client: RoneiaClient, args: list) -> None:
    """
    处理 /persona 命令

    Args:
        client: 客户端实例
        args: 命令参数
    """
    if not args:
        # /persona - 显示当前人设
        print("正在获取当前人设...")
        content = await client.get_persona()
        if content is not None:
            print("\n当前人设:")
            print("-" * 40)
            print(content)
            print("-" * 40)
        else:
            print("[错误] 获取人设失败")
        return

    subcmd = args[0].lower()

    if subcmd == "set":
        # /persona set - 多行输入模式
        print("请输入新的人设（输入空行结束）:")
        lines = []
        while True:
            try:
                line = input("> ")
                if not line:
                    break
                lines.append(line)
            except EOFError:
                break

        if not lines:
            print("未输入内容，取消操作")
            return

        content = "\n".join(lines)
        if await client.update_persona(content):
            print("人设已更新！")
        else:
            print("[错误] 更新人设失败")

    elif subcmd == "reset":
        # /persona reset - 恢复默认人设（由 Server 端提供默认值）
        if await client.reset_persona():
            print("已恢复默认人设")
        else:
            print("[错误] 恢复默认人设失败")

    else:
        print(f"未知子命令: {subcmd}")
        print("用法: /persona [set|reset]")


async def handle_skills_command(client: RoneiaClient, args: list) -> None:
    """
    处理 /skills 命令

    Args:
        client: 客户端实例
        args: 命令参数
    """
    if not args:
        # 默认显示帮助
        print("用法: /skills <子命令> [参数]")
        print("子命令: list, installed, install, enable, disable, remove")
        return

    subcmd = args[0].lower()

    if subcmd == "list":
        # 显示云端可用的 Skill
        print("正在获取云端 Skill 列表...")
        skills = await client.request_skill_list()
        if not skills:
            print("未获取到 Skill 列表")
            return

        print(f"\n云端可用的 Skills ({len(skills)} 个):")
        print("-" * 50)
        for skill in skills:
            name = skill.get("name", "")
            title = skill.get("title", "")
            version = skill.get("version", "")
            desc = skill.get("description", "")
            icon = skill.get("icon", "")
            status = ""
            if client.is_skill_enabled(name):
                status = " [已启用]"
            elif client.is_skill_installed(name):
                status = " [已安装]"
            print(f"  {icon} {name} v{version} - {title}{status}")
            print(f"      {desc}")
        print("-" * 50)

    elif subcmd == "installed":
        # 显示本地已安装的 Skill
        skills = client.get_installed_skills()
        if not skills:
            print("未安装任何 Skill")
            return

        print(f"\n已安装的 Skills ({len(skills)} 个):")
        print("-" * 50)
        for skill in skills:
            name = skill.get("name", "")
            title = skill.get("title", "")
            version = skill.get("version", "")
            enabled = skill.get("enabled", False)
            status = "[已启用]" if enabled else "[已禁用]"
            print(f"  {name} v{version} - {title} {status}")
        print("-" * 50)

    elif subcmd == "install":
        # 安装 Skill
        if len(args) < 2:
            print("用法: /skills install <name>")
            return

        name = args[1]

        # 先获取 Skill 列表
        skills = client.get_registry_skills()
        if not skills:
            print("正在获取云端 Skill 列表...")
            skills = await client.request_skill_list()

        # 查找 Skill
        skill_info = None
        for skill in skills:
            if skill.get("name") == name:
                skill_info = skill
                break

        if not skill_info:
            print(f"未找到 Skill: {name}")
            return

        # 安装
        if client.install_skill(skill_info):
            print(f"已安装 Skill: {name}")
            print(f"使用 '/skills enable {name}' 启用此 Skill")
        else:
            print(f"安装失败: {name}")

    elif subcmd == "enable":
        # 启用 Skill
        if len(args) < 2:
            print("用法: /skills enable <name>")
            return

        name = args[1]

        if not client.is_skill_installed(name):
            print(f"Skill 未安装: {name}")
            print(f"请先使用 '/skills install {name}' 安装")
            return

        # 启用前检查依赖
        from .dep_manager import DependencyManager
        dm = DependencyManager()
        missing = dm.check_missing(name)

        if missing:
            print(f"\n{name} 需要以下依赖:")
            for pkg in missing:
                print(f"  - {pkg}")
            try:
                loop = asyncio.get_event_loop()
                answer = await loop.run_in_executor(
                    None,
                    lambda: input("\n是否立即安装? [Y/n] > ").strip().lower()
                )
            except EOFError:
                answer = "n"
            if answer in ("", "y", "yes"):
                if dm.install_packages(missing):
                    dm.reload_skill_module(name)
                    print("依赖安装完成")
                else:
                    print("安装失败，请手动安装后重试:")
                    print(f"  pip install {' '.join(missing)}")
                    return
            else:
                print("已跳过依赖安装（部分功能可能不可用）")

        if client.enable_skill(name):
            print(f"已启用 Skill: {name}")
            # 同步到服务端
            if await client.sync_enabled_skills():
                print("已同步到服务端")
            else:
                print("同步到服务端失败")

            # 当启用 web_search 时，拉取搜索配置并注入到模块
            if name == "web_search":
                search_config = await client.request_search_config()
                if search_config:
                    from roneia_core.builtin_skills import web_search
                    web_search.configure(search_config)
                    provider = search_config.get("provider", "auto")
                    has_tavily_key = bool(search_config.get("tavily_api_key", ""))
                    if has_tavily_key:
                        print(f"搜索配置已加载 (provider={provider}, Tavily 已配置)")
                    else:
                        print(f"搜索配置已加载 (provider={provider}, 使用 DuckDuckGo)")
        else:
            print(f"启用失败: {name}")

    elif subcmd == "disable":
        # 禁用 Skill
        if len(args) < 2:
            print("用法: /skills disable <name>")
            return

        name = args[1]

        if client.disable_skill(name):
            print(f"已禁用 Skill: {name}")
            # 同步到服务端
            if await client.sync_enabled_skills():
                print("已同步到服务端")
            else:
                print("同步到服务端失败")
        else:
            print(f"禁用失败: {name}")

    elif subcmd == "remove":
        # 删除 Skill
        if len(args) < 2:
            print("用法: /skills remove <name>")
            return

        name = args[1]

        if client.remove_skill(name):
            print(f"已删除 Skill: {name}")
            # 同步到服务端
            if await client.sync_enabled_skills():
                print("已同步到服务端")
        else:
            print(f"删除失败: {name}")

    else:
        print(f"未知子命令: {subcmd}")
        print("子命令: list, installed, install, enable, disable, remove")


async def interactive_mode(client: RoneiaClient):
    """
    交互模式

    支持文字模式和语音模式。
    """
    import signal

    print("\n" + "-" * 60)
    print("交互模式 - 输入任务，输入 'quit' 退出，/voice 切换语音")
    print("-" * 60)

    state = {"mode": "text", "voice_manager": None, "voice_interrupted": False}

    # 自定义 Ctrl+C 处理：语音模式下设置标志并停止，文字模式下正常中断
    def sigint_handler(sig, frame):
        if state["mode"] == "voice" and state.get("voice_manager"):
            state["voice_interrupted"] = True
            state["voice_manager"].request_stop()
        else:
            raise KeyboardInterrupt

    signal.signal(signal.SIGINT, sigint_handler)

    while client.is_connected:
        try:
            if state["mode"] == "text":
                # === 文字模式 ===
                loop = asyncio.get_event_loop()
                user_input = await loop.run_in_executor(
                    None,
                    lambda: input("\n你: ").strip()
                )

                if not user_input:
                    continue

                if user_input.lower() in ("quit", "exit", "退出"):
                    break

                if user_input.startswith("/"):
                    await handle_command(client, user_input, state)
                    continue

                # 发送任务
                print("\nRoneia: ", end="", flush=True)
                response = await client.send_task(user_input)
                if response:
                    print(response)
                else:
                    print("[无响应或超时]")

            else:
                # === 语音模式 ===
                vm = state["voice_manager"]
                if vm is None:
                    state["mode"] = "text"
                    continue

                state["voice_interrupted"] = False

                user_text = await vm.listen()

                if state["voice_interrupted"]:
                    print("\n\n切换到文字模式")
                    state["mode"] = "text"
                    continue

                if not user_text:
                    continue

                # 发送到 Server
                print("\nRoneia: ", end="", flush=True)
                response = await client.send_task(user_text)

                if state["voice_interrupted"]:
                    print("\n\n切换到文字模式")
                    state["mode"] = "text"
                    continue

                if response:
                    print(response)
                    # TTS 播放
                    await vm.speak(response)
                else:
                    print("[无响应或超时]")

                if state["voice_interrupted"]:
                    print("\n\n切换到文字模式")
                    state["mode"] = "text"

        except KeyboardInterrupt:
            print("\n\n检测到中断...")
            break
        except EOFError:
            break
        except Exception as e:
            print(f"\n[错误] {e}")

    # 清理语音资源
    if state.get("voice_manager"):
        await state["voice_manager"].shutdown()


async def main_async(args):
    """异步主函数"""
    # 加载配置
    config = load_client_config(args.config)

    # 命令行参数覆盖配置
    if args.server:
        config.server.url = args.server
    if args.token:
        config.server.token = args.token
    if args.skill_dir:
        config.skills.directory = args.skill_dir
    else:
        # 使用默认 skill 目录
        config.skills.directory = get_default_skill_dir()
    if args.no_gui:
        config.permissions.use_gui = False

    # 显示配置
    print(f"服务器: {config.server.url}")
    print(f"认证: {'已配置' if config.server.token else '未配置'}")
    print(f"Skill 目录: {config.skills.directory}")
    print("-" * 60)

    # 创建客户端
    client = RoneiaClient(config)

    # 注册回调
    async def on_connected():
        print("\n[已连接到服务器]")
        skills = client.get_loaded_skills()
        print(f"已加载 {len(skills)} 个本地 Skills: {', '.join(skills)}")
        if not skills:
            print(f"[调试] Skill 目录: {config.skills.directory}")
            from pathlib import Path
            skill_dir = Path(config.skills.directory)
            if skill_dir.exists():
                print(f"[调试] 目录存在，内容: {[d.name for d in skill_dir.iterdir() if d.is_dir()]}")
            else:
                print(f"[调试] 目录不存在: {skill_dir.resolve()}")

        # 如果 web_search 已启用，自动拉取搜索配置
        enabled_skills = client.get_enabled_skills()
        if "web_search" in enabled_skills:
            search_config = await client.request_search_config()
            if search_config:
                from roneia_core.builtin_skills import web_search
                web_search.configure(search_config)

    async def on_disconnected():
        print("\n[与服务器断开连接]")

    async def on_response(text):
        # 响应已在 interactive_mode 中打印
        pass

    client.on_connected(on_connected)
    client.on_disconnected(on_disconnected)
    client.on_response(on_response)

    # 连接
    print("正在连接服务器...")
    if not await client.connect():
        print("[错误] 无法连接到服务器")
        print("\n请检查:")
        print("  1. 服务器地址是否正确")
        print("  2. 服务器是否正在运行")
        print("  3. 认证令牌是否正确")
        return 1

    # 交互模式
    try:
        await interactive_mode(client)
    except Exception as e:
        print(f"\n[错误] {e}")
    finally:
        await client.disconnect()

    print("\n再见！")
    return 0


def main():
    """主入口"""
    import argparse

    parser = argparse.ArgumentParser(
        description="Roneia - 数字人助手",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )

    subparsers = parser.add_subparsers(dest="command")
    subparsers.add_parser("setup", help="运行初始配置向导")

    parser.add_argument("--config", "-c", help="配置文件路径")
    parser.add_argument("--server", help="服务器 WebSocket URL")
    parser.add_argument("--token", help="认证令牌")
    parser.add_argument("--skill-dir", help="Skill 目录")
    parser.add_argument("--no-gui", action="store_true", help="禁用 GUI 弹窗")
    parser.add_argument("--debug", action="store_true", help="调试模式")

    args = parser.parse_args()

    if args.command == "setup":
        from .setup_wizard import run_setup
        return run_setup()

    print_banner()

    # 设置日志
    log_level = logging.DEBUG if args.debug else logging.WARNING
    logging.basicConfig(
        level=log_level,
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    )

    # 检查配置
    if not args.config and not args.server:
        # 检查默认配置文件
        home_config = os.path.expanduser("~/.roneia/config.yaml")
        default_configs = ["./config.yaml", "./roneia.yaml", home_config]
        for cfg in default_configs:
            if Path(cfg).exists():
                args.config = cfg
                print(f"[配置] 使用: {cfg}")
                break

        if not args.config and not os.getenv("RONEIA_SERVER_URL"):
            print("[提示] 未指定服务器")
            print("\n快速开始:")
            print("  roneia setup                              # 一键配置向导")
            print("\n或手动指定:")
            print("  roneia --server ws://your-server:8765/ws --token xxx")
            print("  roneia --config config.yaml")
            return 1

    # 运行
    # 抑制退出时 asyncio "Task was destroyed but it is pending" 警告
    # （input() 在线程池中阻塞，无法被 asyncio 优雅取消）
    import warnings
    warnings.filterwarnings("ignore", message=".*Task was destroyed.*")
    logging.getLogger("asyncio").setLevel(logging.CRITICAL)

    try:
        return asyncio.run(main_async(args))
    except KeyboardInterrupt:
        print("\n\n程序被中断")
        return 0


if __name__ == "__main__":
    sys.exit(main())
