"""Dependency container for talk command wiring.

Spec source: talk-deps-refactor.create-talk-deps-container.
"""

from __future__ import annotations

import asyncio
from dataclasses import dataclass
from functools import partial
from pathlib import Path
from typing import Awaitable, Callable, Protocol, TypeVar

import httpx
from rich.console import Console
from returns.result import Result

from anima.core.lifecycle import LifecycleEvent
from anima.core.schemas import (
    AgentStateData,
    AnimaError,
    AgentNotFoundError,
    InternalError,
    InvalidInputError,
)
from anima.core.talk_commands import parse_talk_slash_command as parse_slash_command
from anima.shell.cli_commands.talk_display import (
    SLASH_HELP_LINES,
    emit_with_prompt_coordination,
    handle_connection_error,
    print_detach,
    print_help,
    print_inline_command_error,
)
from anima.shell.cli_commands.talk_helpers import (
    parse_talk_response,
    suppress_invalid_state_error,
)
from anima.shell.cli_commands.talk_input import create_prompt_session, read_input
from anima.shell.cli_commands.talk_streaming import (
    consume_sse_stream,
    probe_streaming_support,
)
from anima.shell.state import (
    get_state_dir,
    is_agent_process_alive,
    reap_stale_agent,
    read_log,
    read_state,
    transition_state,
    write_approval_response,
)

ReadState = Callable[
    [str],
    Result[AgentStateData, AgentNotFoundError | InvalidInputError],
]
ReadLog = Callable[[str], Result[list[dict], InternalError]]
TransitionState = Callable[
    [str, LifecycleEvent, str | None, str | None],
    Result[AgentStateData, AnimaError],
]
WriteApprovalResponse = Callable[[str, bool, str | None], Result[None, InternalError]]
IsAgentProcessAlive = Callable[[str], Result[bool, AnimaError]]
ReapStaleAgent = Callable[[str], Result[AgentStateData, AnimaError]]
ParseSlashCommand = Callable[[str], tuple[str | None, str | None]]
ProbeStreamingSupport = Callable[[httpx.AsyncClient, Path], Awaitable[bool]]


class ConsumeSseStream(Protocol):
    async def __call__(
        self,
        *,
        socket_path: Path,
        agent_id: str,
        json_output: bool,
        console: Console,
        stream_complete: asyncio.Event,
        render_with_prompt: Callable[[Callable[[], None]], None] | None,
    ) -> tuple[bool, str | None]: ...


class PromptSession(Protocol):
    def prompt_async(self) -> Awaitable[str | None]: ...


PromptSessionType = TypeVar("PromptSessionType", bound=PromptSession)
CreatePromptSession = Callable[[], PromptSessionType]
ReadInput = Callable[[PromptSessionType], Awaitable[str | None]]


class EmitWithPromptCoordination(Protocol):
    def __call__(self, *, emit: Callable[[], None]) -> None: ...


class HandleConnectionError(Protocol):
    def __call__(
        self,
        *,
        error_code: str,
        numeric_code: int,
        message: str,
        socket_path: Path,
        json_output: bool,
        console: Console,
        human_message: str,
    ) -> None: ...


class PrintDetach(Protocol):
    def __call__(
        self,
        *,
        agent_id: str,
        json_output: bool,
        console: Console,
        newline_prefix: bool = False,
    ) -> None: ...


class PrintHelp(Protocol):
    def __call__(
        self,
        *,
        json_output: bool,
        console: Console,
    ) -> None: ...


class PrintInlineCommandError(Protocol):
    def __call__(
        self,
        *,
        json_output: bool,
        console: Console,
        code: str,
        numeric_code: int,
        message: str,
        details: dict[str, str] | None = None,
    ) -> None: ...


SuppressInvalidStateError = Callable[
    [asyncio.AbstractEventLoop, dict[str, object]],
    None,
]
ParseTalkResponse = Callable[[str], dict[str, str | None]]


@dataclass(frozen=True)
class TalkDeps:
    """Immutable dependency container for talk command runtime wiring."""

    get_state_dir: Callable[[], Path]
    read_state: ReadState
    read_log: ReadLog
    transition_state: TransitionState
    write_approval_response: WriteApprovalResponse
    is_agent_process_alive: IsAgentProcessAlive
    reap_stale_agent: ReapStaleAgent
    parse_slash_command: ParseSlashCommand
    probe_streaming_support: ProbeStreamingSupport
    consume_sse_stream: ConsumeSseStream
    create_prompt_session: CreatePromptSession
    read_input: ReadInput
    emit_with_prompt_coordination: EmitWithPromptCoordination
    handle_connection_error: HandleConnectionError
    print_detach: PrintDetach
    print_help: PrintHelp
    print_inline_command_error: PrintInlineCommandError
    suppress_invalid_state_error: SuppressInvalidStateError
    parse_talk_response: ParseTalkResponse


# @invar:allow shell_result: Dependency factory is pure wiring and intentionally returns container
# @shell_orchestration: Shell composition root for talk command runtime wiring
def default_deps() -> TalkDeps:
    """Return production dependency wiring for the talk command."""
    return TalkDeps(
        get_state_dir=get_state_dir,
        read_state=read_state,
        read_log=read_log,
        transition_state=transition_state,
        write_approval_response=write_approval_response,
        is_agent_process_alive=is_agent_process_alive,
        reap_stale_agent=reap_stale_agent,
        parse_slash_command=parse_slash_command,
        probe_streaming_support=probe_streaming_support,
        consume_sse_stream=consume_sse_stream,
        create_prompt_session=create_prompt_session,
        read_input=read_input,
        emit_with_prompt_coordination=emit_with_prompt_coordination,
        handle_connection_error=handle_connection_error,
        print_detach=print_detach,
        print_help=partial(print_help, slash_help_lines=SLASH_HELP_LINES),
        print_inline_command_error=print_inline_command_error,
        suppress_invalid_state_error=suppress_invalid_state_error,
        parse_talk_response=parse_talk_response,
    )
