"""DTOs for comparison and validation results."""

from __future__ import annotations

from dataclasses import asdict, dataclass, field
from typing import Any

from .types import RowDict


@dataclass
class ValidationDifference:
    """A single difference reported by a validation level."""

    level: str = ""
    type: str = ""
    detail: str = ""
    source: str | None = None


@dataclass
class ComparisonResult:
    """Result of comparing baseline vs. actual data.

    Returned by :class:`ResultComparator`, :class:`InMemoryResultComparator`,
    and the top-level :func:`compare_results` helper.
    """

    match: bool
    match_type: str
    match_level: str = ""
    differences: list[ValidationDifference | str] = field(default_factory=list)
    in_memory_diff: dict[str, Any] | None = None
    baseline_rows: list[RowDict] | None = field(default=None, repr=False)
    actual_rows: list[RowDict] | None = field(default=None, repr=False)


@dataclass
class ValidationCaseResult:
    """Result of validating a single test case against its baseline.

    Returned by :func:`_validate_case` in the validation runner.
    """

    procedure: str
    params_hash: str
    params: dict[str, Any]
    status: str
    match_type: str = ""
    match_level: str = ""
    error: str = ""
    differences: list[ValidationDifference | str] = field(default_factory=list)
    metadata: dict[str, Any] = field(default_factory=dict)
    executed_at: str = ""
    in_memory_diff: dict[str, Any] | None = None
    baseline_rows: list[RowDict] | None = field(default=None, repr=False)
    actual_rows: list[RowDict] | None = field(default=None, repr=False)

    def to_dict(self) -> dict[str, Any]:
        """Serialize to a plain dict suitable for JSON / Snowflake insertion.

        ``ValidationDifference`` items in *differences* are converted to
        dicts; ``baseline_rows`` / ``actual_rows`` are mapped to the
        private keys expected by :func:`write_results_local`.
        """
        d: dict[str, Any] = {
            "procedure": self.procedure,
            "params_hash": self.params_hash,
            "params": self.params,
            "status": self.status,
            "match_type": self.match_type,
            "match_level": self.match_level,
            "error": self.error,
            "differences": [
                asdict(diff) if isinstance(diff, ValidationDifference) else diff
                for diff in self.differences
            ],
            "metadata": self.metadata,
            "executed_at": self.executed_at,
        }
        if self.in_memory_diff is not None:
            d["in_memory_diff"] = self.in_memory_diff
        if self.baseline_rows is not None:
            d["_baseline_rows"] = self.baseline_rows
        if self.actual_rows is not None:
            d["_actual_rows"] = self.actual_rows
        return d
