"""Targeted tests for talk prompt_toolkit migration."""

from __future__ import annotations

import asyncio
import inspect
from dataclasses import replace
from pathlib import Path
from typing import Any
from unittest.mock import patch

from rich.console import Console

from anima.shell.cli_commands import talk_runtime
from anima.shell.cli_commands.talk_deps import default_deps
from anima.shell.cli_commands.talk_input import PROMPT_RICH_PREFIX
from anima.shell.cli_commands.talk_session import (
    execute_stop_turn,
    handle_interact_result,
    handle_inline_slash_command,
    latest_approval_reason,
)


def _fake_read_input_factory(messages: list[str | None]):
    queue = list(messages)

    async def _fake_read_input(_session: object) -> str | None:
        if not queue:
            return None
        return queue.pop(0)

    return _fake_read_input


async def _wait_for_compat(*, deps, **wait_kwargs):
    """Call wait helper across deps/non-deps signatures."""

    wait_fn = talk_runtime.wait_for_interact_or_input_or_stream
    wait_params = inspect.signature(wait_fn).parameters

    def _print_detach_compat(*args, **kwargs) -> None:
        if args and not kwargs:
            agent_id, json_output, console = args[:3]
            newline_prefix = False
        else:
            agent_id = kwargs.get("agent_id", args[0] if args else "")
            json_output = kwargs.get("json_output", args[1] if len(args) > 1 else False)
            console = kwargs.get("console", args[2] if len(args) > 2 else None)
            newline_prefix = kwargs.get("newline_prefix", False)
        deps.print_detach(
            agent_id=agent_id,
            json_output=bool(json_output),
            console=console,
            newline_prefix=bool(newline_prefix),
        )

    def _print_help_compat(*args, **kwargs) -> None:
        if args and not kwargs:
            json_output, console = args[:2]
        else:
            json_output = kwargs.get("json_output", args[0] if args else False)
            console = kwargs.get("console", args[1] if len(args) > 1 else None)
        try:
            deps.print_help(json_output=json_output, console=console)
        except TypeError:
            deps.print_help(json_output, console)

    def _handle_interact_result_compat(*args, **kwargs):
        if args:
            mcp_result = args[0]
            agent_id = args[1]
            json_output = args[2]
            console = args[3]
            was_awaiting_approval = args[4]
            print_approval_prompt = args[5]
        else:
            mcp_result = kwargs["mcp_result"]
            agent_id = kwargs["agent_id"]
            json_output = kwargs["json_output"]
            console = kwargs["console"]
            was_awaiting_approval = kwargs["was_awaiting_approval"]
            print_approval_prompt = kwargs["print_approval_prompt"]

        return handle_interact_result(
            mcp_result,
            agent_id,
            json_output,
            console,
            was_awaiting_approval,
            print_approval_prompt,
            deps=deps,
            suppress_display=bool(kwargs.get("suppress_display", False)),
        )

    wait_compat: dict[str, Any] = {
        "deps": deps,
        "execute_stop_turn_func": (
            lambda session, json_output, console: execute_stop_turn(
                session,
                json_output,
                console,
                deps=deps,
            )
        ),
        "cleanup_stdin_func": talk_runtime.cleanup_stdin,
        "print_detach_func": _print_detach_compat,
        "emit_with_prompt_coordination_func": deps.emit_with_prompt_coordination,
        "print_help_func": _print_help_compat,
        "handle_interact_result_func": _handle_interact_result_compat,
    }
    for name, value in wait_compat.items():
        if name in wait_params and name not in wait_kwargs:
            wait_kwargs[name] = value
    return await wait_fn(**wait_kwargs)


def _run_talk_session_compat(*, console: Console, json_output: bool, deps) -> None:
    """Call talk_runtime.run_talk_session across deps/non-deps signatures."""

    params = inspect.signature(talk_runtime.run_talk_session).parameters
    run_session = talk_runtime.run_talk_session
    kwargs: dict[str, Any] = {
        "socket_path": Path("/tmp/fake.sock"),
        "agent_id": "inst_real",
        "json_output": json_output,
        "console": console,
    }

    def _print_detach_compat(*args, **kwargs) -> None:
        if args and not kwargs:
            agent_id, json_output, console = args[:3]
            newline_prefix = False
        else:
            agent_id = kwargs.get("agent_id", args[0] if args else "")
            json_output = kwargs.get("json_output", args[1] if len(args) > 1 else False)
            console = kwargs.get("console", args[2] if len(args) > 2 else None)
            newline_prefix = kwargs.get("newline_prefix", False)
        deps.print_detach(
            agent_id=agent_id,
            json_output=bool(json_output),
            console=console,
            newline_prefix=bool(newline_prefix),
        )

    def _print_help_compat(*args, **kwargs) -> None:
        if args and not kwargs:
            json_output, console = args[:2]
        else:
            json_output = kwargs.get("json_output", args[0] if args else False)
            console = kwargs.get("console", args[1] if len(args) > 1 else None)
        try:
            deps.print_help(json_output=json_output, console=console)
        except TypeError:
            deps.print_help(json_output, console)

    compat_kwargs: dict[str, Any] = {
        "deps": deps,
        "create_prompt_session_func": deps.create_prompt_session,
        "read_input_func": deps.read_input,
        "suppress_invalid_state_error_func": deps.suppress_invalid_state_error,
        "read_state_func": deps.read_state,
        "parse_slash_command_func": deps.parse_slash_command,
        "handle_inline_slash_command_func": (
            lambda *, message, agent_id, json_output, console: (
                handle_inline_slash_command(
                    message=message,
                    agent_id=agent_id,
                    json_output=json_output,
                    console=console,
                    deps=deps,
                )
            )
        ),
        "probe_streaming_support_func": deps.probe_streaming_support,
        "consume_sse_stream_func": deps.consume_sse_stream,
        "emit_with_prompt_coordination_func": deps.emit_with_prompt_coordination,
        "print_help_func": _print_help_compat,
        "print_detach_func": _print_detach_compat,
        "execute_stop_turn_func": (
            lambda session, json_output, console: execute_stop_turn(
                session,
                json_output,
                console,
                deps=deps,
            )
        ),
        "wait_for_interact_or_input_or_stream_func": (
            lambda **wait_kwargs: _wait_for_compat(deps=deps, **wait_kwargs)
        ),
        "cleanup_stdin_func": talk_runtime.cleanup_stdin,
        "latest_approval_reason_func": (
            lambda agent_id: latest_approval_reason(agent_id, deps=deps)
        ),
        "prompt_rich_prefix": PROMPT_RICH_PREFIX,
    }
    for name, value in compat_kwargs.items():
        if name in params:
            kwargs[name] = value

    missing_required = [
        name
        for name, param in params.items()
        if param.default is inspect._empty and name not in kwargs
    ]
    if missing_required:
        raise AssertionError(
            f"Unsupported run_talk_session signature, missing: {missing_required}"
        )

    if "deps" in params:
        asyncio.run(run_session(**kwargs))
        return

    with (
        patch.object(
            talk_runtime,
            "create_prompt_session",
            deps.create_prompt_session,
            create=True,
        ),
        patch.object(talk_runtime, "read_input", deps.read_input, create=True),
    ):
        asyncio.run(run_session(**kwargs))


def test_run_talk_session_prompt_toolkit_quit_detaches(mock_mcp, talk_console) -> None:
    mock_mcp.patch(tool_response='{"response":"unused","turn_number":1}')
    deps = replace(
        default_deps(),
        create_prompt_session=lambda: object(),
        read_input=_fake_read_input_factory(["/quit"]),
    )

    console, stream = talk_console
    _run_talk_session_compat(console=console, json_output=False, deps=deps)

    assert "Detaching from agent" in stream.getvalue()


def test_run_talk_session_prompt_toolkit_json_mode_keeps_payload(
    mock_mcp, talk_console
) -> None:
    mock_mcp.patch(tool_response='{"response":"agent json response","turn_number":7}')
    deps = replace(
        default_deps(),
        create_prompt_session=lambda: object(),
        read_input=_fake_read_input_factory(["hello", "/quit"]),
    )

    console, stream = talk_console
    _run_talk_session_compat(console=console, json_output=True, deps=deps)

    output = stream.getvalue()
    assert "user_input" in output
    assert "agent json response" in output
    assert "turn_number" in output and "7" in output
