"""App-layer terminal-result service contracts.

Responsibility: define adapter-facing output-policy projections for terminal
result shaping across CLI and MCP presentation surfaces.

Non-responsibility: inspect provider payloads, format terminal strings, or
perform filesystem/network/runtime work.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Literal, Protocol

from anima.core.execution import ExecutionOutputPolicyView, RuntimeOutputPolicyView

TerminalResultAdapterSurface = Literal[
    "cli_terminal_presenter",
    "mcp_response_presenter",
]


@dataclass(frozen=True)
class TerminalResultPolicyProjection:
    """Adapter-facing output-governance projection for terminal results.

    Invariants:
    - Terminal presenters consume this projection for result shaping only.
    - Model-injection adapters must not use this projection as a source of
      runtime-context facts.
    - ``output_policy`` remains the single source of truth for provider profile,
      per-call cap, per-turn budget, and truncation/pagination strategy.
    """

    adapter_surface: TerminalResultAdapterSurface
    output_policy: RuntimeOutputPolicyView
    terminal_result_char_cap: int | None
    prefer_pagination: bool


class ResultService(Protocol):
    """App-layer boundary for terminal-result shaping policy.

    Invariants:
    - Consumers depend on ``ExecutionOutputPolicyView`` instead of the full
      ``ResolvedExecutionContract``.
    - CLI and MCP terminal presenters consume this service; model-injection
      adapters do not.
    - The service returns shaping policy only and does not define result-store or
      terminal-state retrieval.
    """

    def project_terminal_result_policy(
        self,
        contract: ExecutionOutputPolicyView,
        *,
        adapter_surface: TerminalResultAdapterSurface,
    ) -> TerminalResultPolicyProjection: ...


@dataclass(frozen=True)
class DefaultResultService:
    """Default implementation of ResultService.

    This service projects terminal-result shaping policy directly from resolved
    contract output policy. It never re-derives output-governance facts from
    adapter-local defaults.
    """

    def project_terminal_result_policy(
        self,
        contract: ExecutionOutputPolicyView,
        *,
        adapter_surface: TerminalResultAdapterSurface,
    ) -> TerminalResultPolicyProjection:
        """Project terminal-result shaping policy from contract truth.

        Args:
            contract: Contract view exposing immutable output policy.
            adapter_surface: Target presenter surface.

        Returns:
            TerminalResultPolicyProjection with derived terminal shaping hints.
        """
        char_cap = _resolve_terminal_result_char_cap(contract.output_policy)
        truncation_strategy = contract.output_policy.truncation_strategy.lower()
        prefer_pagination = "paginate" in truncation_strategy
        return TerminalResultPolicyProjection(
            adapter_surface=adapter_surface,
            output_policy=contract.output_policy,
            terminal_result_char_cap=char_cap,
            prefer_pagination=prefer_pagination,
        )


def _resolve_terminal_result_char_cap(
    output_policy: RuntimeOutputPolicyView,
) -> int | None:
    """Resolve strict terminal-result char cap from output-policy caps."""
    candidates = [
        output_policy.per_call_tool_output_chars,
        output_policy.per_turn_tool_output_chars,
    ]
    valid_caps = [value for value in candidates if isinstance(value, int) and value > 0]
    if not valid_caps:
        return None
    return min(valid_caps)
