"""RCA input normalization helpers for MCP tools.

This module keeps MCP adapter functions thin by centralizing parsing and
context resolution for manual RCA advancement.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from ._snapshot_loader import load_snapshot
from .rca_state import build_failure_signature, derive_session_id


@dataclass(frozen=True)
class RCAAdvanceContext:
    """Normalized inputs for one manual RCA advancement request."""

    snapshot: dict[str, Any] | None
    session_id: str
    failure_signature: str
    evidence_refs: list[str]
    verification_result: dict[str, Any] | None
    hypothesis: str | None
    experiment_plan: str | None
    proposed_change_summary: str | None
    override_reason: str | None


class SnapshotLoadError(RuntimeError):
    """Raised when a requested snapshot cannot be loaded for manual RCA flow."""


class RCAInputError(ValueError):
    """Raised when manual RCA inputs are ambiguous or internally inconsistent."""


def _split_csv_values(raw: str) -> list[str]:
    return [item.strip() for item in raw.split(",") if item.strip()]


def _resolve_snapshot(
    *,
    out_dir: str,
    snapshot_path: str,
    session_id: str,
    failure_signature: str,
) -> dict[str, Any] | None:
    if snapshot_path:
        try:
            return load_snapshot(snapshot_path, out_dir)
        except Exception as exc:  # pragma: no cover - exercised through public API
            raise SnapshotLoadError(f"Error loading snapshot: {type(exc).__name__}: {exc}") from exc

    has_session = bool(session_id.strip())
    has_signature = bool(failure_signature.strip())
    if not has_session and not has_signature:
        try:
            return load_snapshot(None, out_dir)
        except Exception:
            return None
    if has_session != has_signature:
        raise RCAInputError(
            "When snapshot_path is omitted, provide both session_id and failure_signature "
            "together, or leave both empty to derive from the latest snapshot."
        )
    return None


def _verification_result(status: str, checks: str) -> dict[str, Any] | None:
    normalized_status = status.strip()
    if not normalized_status:
        return None
    return {
        "status": normalized_status,
        "checks": _split_csv_values(checks),
    }


def _strip_or_none(raw: str) -> str | None:
    normalized = raw.strip()
    return normalized or None


def resolve_rca_advance_context(
    *,
    out_dir: str,
    snapshot_path: str,
    session_id: str,
    failure_signature: str,
    evidence_refs: str,
    hypothesis: str,
    experiment_plan: str,
    proposed_change_summary: str,
    verification_status: str,
    verification_checks: str,
    override_reason: str,
) -> RCAAdvanceContext:
    """Resolve normalized RCA context from MCP tool string inputs."""
    snapshot = _resolve_snapshot(
        out_dir=out_dir,
        snapshot_path=snapshot_path,
        session_id=session_id,
        failure_signature=failure_signature,
    )
    resolved_session = session_id.strip() or derive_session_id(
        snapshot, out_dir=out_dir, fallback="manual"
    )
    signature = failure_signature.strip() or build_failure_signature(snapshot)
    return RCAAdvanceContext(
        snapshot=snapshot,
        session_id=resolved_session,
        failure_signature=signature,
        evidence_refs=_split_csv_values(evidence_refs),
        verification_result=_verification_result(verification_status, verification_checks),
        hypothesis=_strip_or_none(hypothesis),
        experiment_plan=_strip_or_none(experiment_plan),
        proposed_change_summary=_strip_or_none(proposed_change_summary),
        override_reason=_strip_or_none(override_reason),
    )
