"""Self-contained baseline JSON ready for serialization."""

from __future__ import annotations

import hashlib
import json
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Optional

from .capture_result import TestCaseResult


@dataclass
class BaselineData:
    """A self-contained baseline JSON ready for serialization.

    Contains everything needed to later validate the same procedure on
    Snowflake without needing access to the original YAML or Code Units.
    """

    procedure: str
    """Source procedure name (e.g. ``master.dbo.GetProducts``)."""

    parameters: dict[str, Any]
    """Parameter name -> value mapping used for execution."""

    captured_at: str
    """ISO-8601 timestamp of when the baseline was captured."""

    result_sets: list[list[dict[str, Any]]]
    """Captured result sets from the source execution."""

    row_counts: list[int]
    """Row counts per result set."""

    success: bool
    """Whether the source execution succeeded."""

    error: Optional[str] = None
    """Error message from the source execution, if any."""

    column_types: list[dict[str, str]] = field(default_factory=list)
    """Normalized column types per result set.

    Each element maps ``COLUMN_NAME`` -> ``NormalizedType`` value string
    (e.g. ``{"ID": "INTEGER", "NAME": "STRING"}``).  Populated during
    capture so that the validate step can perform schema-level comparison.
    """

    output_params: Optional[dict[str, Any]] = None
    """Output parameter values captured from the source execution.
    Stored as a dict (name -> value) for comparison during validation."""

    output_param_types: Optional[dict[str, str]] = None
    """Column types for output parameters (name -> Snowflake type).
    Used at materialization time to cast baseline values correctly."""

    table_deltas: Optional[dict[str, Any]] = None
    """Per-table delta dicts for DML procedures (``modifies_data=True``).
    Keys are fully-qualified table names, values are serialized
    :class:`replay.models.Delta` dicts."""

    column_metadata: Optional[dict[str, dict[str, list[str]]]] = None
    """Per-table platform-managed column metadata from source DB introspection.

    Populated during capture by querying the source RDBMS catalog
    (e.g. ``sys.columns`` on SQL Server).  Keys are ``SCHEMA.TABLE``,
    values contain ``platform_managed_columns`` and ``identity_columns``.
    Used during validation to auto-ignore columns that differ by design.
    """

    stored_params_hash: Optional[str] = field(default=None, repr=False)
    """Pre-computed params hash from an external source (e.g. the
    ``VALIDATION.BASELINE_ROWS`` table).  When set, takes precedence
    over the hash computed from :attr:`parameters`."""

    @property
    def params_hash(self) -> str:
        """8-character MD5 hash of the parameters (for filenames)."""
        if self.stored_params_hash is not None:
            return self.stored_params_hash
        param_str = json.dumps(self.parameters, sort_keys=True, default=str)
        return hashlib.md5(param_str.encode()).hexdigest()[:8]

    def to_dict(self) -> dict[str, Any]:
        """Serialize to a plain dict suitable for ``json.dump``."""
        d: dict[str, Any] = {
            "procedure": self.procedure,
            "params_hash": self.params_hash,
            "parameters": self.parameters,
            "captured_at": self.captured_at,
            "result_sets": self.result_sets,
            "row_counts": self.row_counts,
            "success": self.success,
            "error": self.error,
        }
        if self.column_types:
            d["column_types"] = self.column_types
        if self.output_params:
            d["output_params"] = self.output_params
        if self.output_param_types:
            d["output_param_types"] = self.output_param_types
        if self.table_deltas:
            d["table_deltas"] = self.table_deltas
        if self.column_metadata:
            d["column_metadata"] = self.column_metadata
        return d

    @staticmethod
    def from_capture(
        procedure: str,
        parameters: dict[str, Any],
        result: TestCaseResult,
    ) -> BaselineData:
        """Build a baseline from a procedure name, parameters and execution result."""
        return BaselineData(
            procedure=procedure,
            parameters=parameters,
            captured_at=datetime.now(timezone.utc).isoformat(),
            result_sets=result.result_sets,
            row_counts=result.row_counts,
            success=result.success,
            error=result.error,
            column_types=result.column_types,
            output_params=result.output_params,
            output_param_types=result.output_param_types,
        )
