#!/usr/bin/env python3
"""DeepSeek 对话智能体 - 命令行入口"""

from __future__ import annotations

import argparse
import asyncio
import re
import sys
from pathlib import Path

# 将 backend 加入模块搜索路径
BACKEND_DIR = Path(__file__).resolve().parent / "backend"
sys.path.insert(0, str(BACKEND_DIR))

from agent.chat_agent import ChatAgent  # noqa: E402
from bootstrap import init_db  # noqa: E402
from config import settings  # noqa: E402
from database import SessionLocal  # noqa: E402
from memory.session_store import SessionStore  # noqa: E402
from presets.preset_store import PresetStore  # noqa: E402
from presets.template_utils import extract_variables  # noqa: E402

VARIABLE_PATTERN = re.compile(r"\{(\w+)\}")


def setup_console() -> None:
    if sys.platform == "win32":
        try:
            sys.stdout.reconfigure(encoding="utf-8")
            sys.stdin.reconfigure(encoding="utf-8")
        except Exception:
            pass


def check_api_key() -> None:
    if not settings.deepseek_api_key or settings.deepseek_api_key == "sk-your-key-here":
        print("错误: 请先在 .env 中配置有效的 DEEPSEEK_API_KEY")
        print(f"配置文件路径: {Path(__file__).resolve().parent / '.env'}")
        sys.exit(1)


async def stream_print(agent: ChatAgent, session_id: str, text: str, model: str | None = None) -> str:
    print("\n助手: ", end="", flush=True)
    full = ""
    async for chunk in agent.stream_reply(session_id, text, model=model):
        if chunk.startswith("\0"):
            continue
        full += chunk
        print(chunk, end="", flush=True)
    print("\n")
    return full


def print_presets(presets) -> None:
    if not presets:
        print("暂无预设问题，可在数据库中通过 API 或后续管理功能添加。")
        return
    print("\n预设问题列表:")
    print("-" * 50)
    for p in presets:
        vars_hint = f"  变量: {', '.join(extract_variables(p.prompt_template))}" if extract_variables(p.prompt_template) else ""
        print(f"  [{p.id}] {p.title} ({p.category}){vars_hint}")
    print("-" * 50)
    print("使用 /preset <id> 触发，例如: /preset daily_summary\n")


def collect_variables(template: str) -> dict[str, str]:
    variables: dict[str, str] = {}
    for name in extract_variables(template):
        value = input(f"  请输入 {name}: ").strip()
        while not value:
            value = input(f"  {name} 不能为空，请重新输入: ").strip()
        variables[name] = value
    return variables


def print_help() -> None:
    print(
        """
命令:
  /help              显示帮助
  /new               新建对话
  /presets           列出预设问题
  /preset <id>       使用预设提问
  /history           查看当前会话历史
  /quit 或 /exit     退出

直接输入文字即可与助手对话。
"""
    )


def print_history(store: SessionStore, session_id: str) -> None:
    messages = store.get_messages(session_id)
    if not messages:
        print("\n（暂无历史消息）\n")
        return
    print("\n--- 会话历史 ---")
    for msg in messages:
        label = "你" if msg.role == "user" else "助手"
        print(f"\n{label}: {msg.content}")
    print("\n----------------\n")


async def run_preset(
    agent: ChatAgent,
    preset_store: PresetStore,
    session_id: str,
    preset_id: str,
    variables: dict[str, str] | None = None,
) -> None:
    try:
        preset, prompt = preset_store.render_prompt(preset_id, variables or {})
    except LookupError:
        print(f"错误: 预设不存在 -> {preset_id}")
        return
    except ValueError as exc:
        print(f"错误: {exc}")
        return

    print(f"\n[预设: {preset.title}]")
    print(f"问题: {prompt}")
    await stream_print(agent, session_id, prompt, model=preset.model)


async def interactive_loop(session_id: str | None = None) -> None:
    init_db()
    db = SessionLocal()
    try:
        store = SessionStore(db)
        preset_store = PresetStore(db)
        agent = ChatAgent(db)

        if session_id:
            if not store.get_session(session_id):
                print(f"错误: 会话不存在 {session_id}")
                return
        else:
            session = store.create_session()
            session_id = session.id

        print("=" * 50)
        print("  DeepSeek 对话智能体（命令行版）")
        print(f"  模型: {settings.deepseek_model}")
        print(f"  会话: {session_id[:8]}...")
        print("=" * 50)
        print_help()

        while True:
            try:
                user_input = input("你: ").strip()
            except (EOFError, KeyboardInterrupt):
                print("\n再见！")
                break

            if not user_input:
                continue

            if user_input.startswith("/"):
                cmd_parts = user_input.split(maxsplit=1)
                cmd = cmd_parts[0].lower()
                arg = cmd_parts[1].strip() if len(cmd_parts) > 1 else ""

                if cmd in ("/quit", "/exit"):
                    print("再见！")
                    break
                if cmd == "/help":
                    print_help()
                    continue
                if cmd == "/new":
                    session = store.create_session()
                    session_id = session.id
                    print(f"\n已新建对话，会话 ID: {session_id[:8]}...\n")
                    continue
                if cmd == "/presets":
                    print_presets(preset_store.list_presets())
                    continue
                if cmd == "/history":
                    print_history(store, session_id)
                    continue
                if cmd == "/preset":
                    if not arg:
                        print("用法: /preset <预设ID>，先用 /presets 查看列表")
                        continue
                    preset = preset_store.get_preset(arg)
                    if not preset:
                        print(f"错误: 预设不存在 -> {arg}")
                        continue
                    variables = (
                        collect_variables(preset.prompt_template)
                        if extract_variables(preset.prompt_template)
                        else {}
                    )
                    await run_preset(agent, preset_store, session_id, arg, variables)
                    continue

                print(f"未知命令: {cmd}，输入 /help 查看帮助")
                continue

            try:
                await stream_print(agent, session_id, user_input)
            except Exception as exc:
                print(f"\n错误: {exc}\n")

    finally:
        db.close()


async def run_once(
    message: str | None,
    preset_id: str | None,
    variables: dict[str, str],
    session_id: str | None,
) -> None:
    init_db()
    db = SessionLocal()
    try:
        store = SessionStore(db)
        preset_store = PresetStore(db)
        agent = ChatAgent(db)

        if session_id and not store.get_session(session_id):
            print(f"错误: 会话不存在 {session_id}")
            sys.exit(1)

        if not session_id:
            session_id = store.create_session().id

        if preset_id:
            await run_preset(agent, preset_store, session_id, preset_id, variables)
        elif message:
            await stream_print(agent, session_id, message)
        else:
            print("错误: 请指定 --message 或 --preset")
            sys.exit(1)
    finally:
        db.close()


def parse_variables(items: list[str]) -> dict[str, str]:
    result: dict[str, str] = {}
    for item in items:
        if "=" not in item:
            print(f"错误: 变量格式应为 key=value，收到: {item}")
            sys.exit(1)
        key, value = item.split("=", 1)
        result[key.strip()] = value.strip()
    return result


def main() -> None:
    setup_console()

    parser = argparse.ArgumentParser(description="DeepSeek 对话智能体（命令行版）")
    parser.add_argument("-m", "--message", help="单次提问（非交互模式）")
    parser.add_argument("-p", "--preset", help="使用预设问题 ID")
    parser.add_argument(
        "-v", "--var", action="append", default=[], metavar="KEY=VALUE", help="预设变量"
    )
    parser.add_argument("-s", "--session", help="指定会话 ID（继续某次对话）")
    parser.add_argument("--list-presets", action="store_true", help="列出预设后退出")
    args = parser.parse_args()

    if args.list_presets:
        init_db()
        db = SessionLocal()
        try:
            print_presets(PresetStore(db).list_presets())
        finally:
            db.close()
        return

    check_api_key()

    if args.message or args.preset:
        asyncio.run(
            run_once(args.message, args.preset, parse_variables(args.var), args.session)
        )
    else:
        asyncio.run(interactive_loop(args.session))


if __name__ == "__main__":
    main()
