"""无 Textual 时的对话：单轮 ``--noui``，或多轮传统行模式 ``--console``（仅 stdin/stdout）。"""

from __future__ import annotations

import asyncio
import os
from collections.abc import Callable
from typing import TYPE_CHECKING

import typer

if TYPE_CHECKING:
    from aicd.runtime import RuntimeContext

from aicd.tui_prefs import MAIN_CHAT_CANONICAL_MODES


async def shutdown_ctx_after_cli(ctx: RuntimeContext) -> None:
    try:
        await ctx.shutdown_graceful()
    except Exception:
        pass
    try:
        ctx.session_log.close()
    except Exception:
        pass


async def one_chat_turn(
    ctx: RuntimeContext,
    user_message: str,
    *,
    emit: Callable[[str], None] | None = None,
    main_chat_mode_override: str | None = None,
) -> tuple[int, str | None]:
    """
    执行一轮 ``agent.chat``；工具事件输出到 ``emit``（若给定）否则 ``typer.echo``。
    成功返回 ``(0, reply)``，失败 ``(1, None)``。不负责进程级 shutdown。
    """

    def _out(msg: str, *, err: bool = False) -> None:
        if emit is not None:
            emit(msg)
        elif err:
            typer.echo(msg, err=True)
        else:
            typer.echo(msg)

    q = ctx.bus.subscribe()

    async def _tool_pump() -> None:
        try:
            while True:
                ev = await q.get()
                if ev.get("topic") != "agent":
                    continue
                if ev.get("event") == "phase":
                    _out(f"[phase {ev.get('phase', '')}] {ev.get('detail', '')}")
                elif ev.get("event") == "tool":
                    name = ev.get("name", "")
                    prev = ev.get("preview", "")
                    _out(f"[tool {name}] {prev}")
                elif ev.get("event") == "log":
                    line = str(ev.get("line", "")).strip()
                    if line:
                        _out(line)
        except asyncio.CancelledError:
            raise
        finally:
            ctx.bus.unsubscribe(q)

    pump = asyncio.create_task(_tool_pump())
    prev_mode: str | None = None
    if (
        main_chat_mode_override
        and main_chat_mode_override in MAIN_CHAT_CANONICAL_MODES
    ):
        prev_mode = ctx.agent.main_chat_mode
        ctx.agent.assign_main_chat_mode_canonical(main_chat_mode_override)
    try:
        reply = await ctx.agent.chat(user_message)
    except asyncio.CancelledError:
        pump.cancel()
        try:
            await pump
        except asyncio.CancelledError:
            pass
        raise
    except Exception as e:  # noqa: BLE001
        pump.cancel()
        try:
            await pump
        except asyncio.CancelledError:
            pass
        _out(f"错误: {e}", err=True)
        return 1, None
    else:
        pump.cancel()
        try:
            await pump
        except asyncio.CancelledError:
            pass
        return 0, reply
    finally:
        if prev_mode is not None:
            ctx.agent.assign_main_chat_mode_canonical(prev_mode)


async def run_noui_ai_turn(ctx: RuntimeContext, user_message: str) -> int:
    """
    执行一轮 ``agent.chat``；期间把总线 ``agent`` 工具事件打到 stdout；
    结束后打印最终助手文本，再优雅关闭运行时。
    """
    code, reply = await one_chat_turn(ctx, user_message)
    try:
        if code == 0 and reply is not None:
            typer.echo(reply)
    finally:
        await shutdown_ctx_after_cli(ctx)
    return code


def console_mode_requested_explicit(console_flag: bool) -> bool:
    """是否启用传统控制台多轮对话（``--console`` 或环境变量）。"""
    if console_flag:
        return True
    v = (os.environ.get("AICD_CONSOLE") or "").strip().lower()
    return v in ("1", "true", "yes", "on")


async def run_console_chat_loop(
    ctx: RuntimeContext,
    bootstrap: str | None,
) -> int:
    """
    多轮纯文本对话：无全屏、无框线字符，仅适合 legacy conhost。

    多行输入：逐行输入，**单独一个空行**（连续两次回车）表示本条用户消息结束；**``/exit``** 退出。
    """
    typer.echo(
        "[aicd] Console mode: plain text only (no Textual). "
        "Tool lines print as [tool …]. "
        "Multi-line: type lines, then an empty line to send. /exit to quit."
    )
    first = (bootstrap or "").strip()
    if first:
        code, reply = await one_chat_turn(ctx, first)
        if code != 0:
            await shutdown_ctx_after_cli(ctx)
            return code
        if reply is not None:
            typer.echo(f"\n--- assistant ---\n{reply}\n")

    while True:
        typer.echo(
            "--- you --- ( /exit to quit; after your lines, send an empty line to run )"
        )
        lines: list[str] = []
        try:
            while True:
                try:
                    line = input()
                except EOFError:
                    typer.echo("\n[aicd] bye.")
                    await shutdown_ctx_after_cli(ctx)
                    return 0
                if line.strip() == "/exit":
                    typer.echo("[aicd] bye.")
                    await shutdown_ctx_after_cli(ctx)
                    return 0
                if line == "":
                    break
                lines.append(line)
        except KeyboardInterrupt:
            typer.echo("\n[aicd] bye.")
            await shutdown_ctx_after_cli(ctx)
            return 0

        msg = "\n".join(lines).strip()
        if not msg:
            continue
        code, reply = await one_chat_turn(ctx, msg)
        if code != 0:
            await shutdown_ctx_after_cli(ctx)
            return code
        if reply is not None:
            typer.echo(f"\n--- assistant ---\n{reply}\n")
