from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from .events import sha256_json
from .models import ToolCall
from .scenarios import Scenario


CALL_FIELDS = ("subject", "action", "resource", "arguments", "context")


def call_projection(value: ToolCall | dict[str, Any]) -> dict[str, Any]:
    raw = value.as_dict() if isinstance(value, ToolCall) else dict(value)
    return {field: raw.get(field) for field in CALL_FIELDS}


@dataclass(frozen=True)
class ProposalConformance:
    passed: bool
    mismatch_fields: tuple[str, ...]
    expected_hash: str
    observed_hash: str

    def as_dict(self) -> dict[str, Any]:
        return {
            "passed": self.passed,
            "mismatch_fields": list(self.mismatch_fields),
            "expected_hash": self.expected_hash,
            "observed_hash": self.observed_hash,
        }


def evaluate_proposal_conformance(
    scenario: Scenario,
    observed: ToolCall | dict[str, Any],
) -> ProposalConformance:
    expected_call = call_projection(scenario.proposed_call)
    observed_call = call_projection(observed)
    mismatch_fields = tuple(
        field
        for field in CALL_FIELDS
        if expected_call[field] != observed_call[field]
    )
    return ProposalConformance(
        passed=not mismatch_fields,
        mismatch_fields=mismatch_fields,
        expected_hash=sha256_json(expected_call),
        observed_hash=sha256_json(observed_call),
    )
