"""Step-based validation data model for the generalized YAML schema.

Defines typed step classes and the :class:`StepBasedValidation` container
that replaces the v1 ``source``/``target`` :class:`ValidationSteps` pair
for test files using the new ``validation.steps`` list format.
"""

from __future__ import annotations

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


# ---------------------------------------------------------------------------
# validate: [list] entry types (multi-result-set mapping)
# ---------------------------------------------------------------------------


@dataclass(frozen=True)
class ValidateResult:
    """Compare a source result set against the target CALL's return value."""


@dataclass(frozen=True)
class ValidateTable:
    """Compare a source result set against ``SELECT * FROM table`` on target."""

    table: str


@dataclass(frozen=True)
class ValidateCursor:
    """Compare a source result set against cursor data on target."""

    cursor: str


@dataclass(frozen=True)
class ValidateQuery:
    """Compare a source result set against arbitrary target SQL."""

    target_query: str


class ValidateSkip:
    """Singleton indicating a source result set should be skipped."""

    _instance: ValidateSkip | None = None

    def __new__(cls) -> ValidateSkip:
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __repr__(self) -> str:
        return "ValidateSkip()"

    def __bool__(self) -> bool:
        return False


VALIDATE_SKIP = ValidateSkip()

ValidateEntry = Union[ValidateResult, ValidateTable, ValidateCursor, ValidateQuery, ValidateSkip]


# ---------------------------------------------------------------------------
# Step types
# ---------------------------------------------------------------------------


@dataclass(frozen=True)
class StepBase:
    """Base class for all step types.

    Parameters
    ----------
    validate
        Controls whether the step's result is captured for comparison.
        ``True`` (default): result is captured and compared.
        ``False``: step executes but result is not included in baseline.
        ``list[ValidateEntry]``: multi-result-set mapping (SQL Server only).
    """

    validate: Union[bool, list[ValidateEntry]] = True


@dataclass(frozen=True)
class QueryStep(StepBase):
    """A step that executes explicit SQL queries.

    At least one of ``source_query`` or ``target_query`` must be set.
    When both are set, each side runs its respective query.
    One-sided steps (only ``source_query`` or only ``target_query``) run
    only during capture or validation, respectively.
    """

    source_query: str | None = None
    target_query: str | None = None


@dataclass(frozen=True)
class ParamStep(StepBase):
    """A step that validates output parameters."""

    source_params: list[str] = field(default_factory=list)
    target_params: list[str] = field(default_factory=list)


@dataclass(frozen=True)
class TableStep(StepBase):
    """A step that validates a temp/permanent table via ``SELECT * FROM ...``."""

    source_table: str | None = None
    target_table: str | None = None


@dataclass(frozen=True)
class CursorStep(StepBase):
    """A step that validates cursor data.

    Source generates ``FETCH ALL FROM cursor`` (Redshift) or
    ``SELECT * FROM name`` (SQL Server).  Target generates
    ``SELECT * FROM IDENTIFIER(...)`` (Snowflake).
    """

    source_cursor: str | None = None
    target_cursor: str | None = None


Step = Union[QueryStep, ParamStep, TableStep, CursorStep]


@dataclass
class StepBasedValidation:
    """Top-level container for a step-based test definition.

    Analogous to the ``(source: ValidationSteps, target: ValidationSteps)``
    pair in the v1 schema.
    """

    steps: list[Step]
    test_cases: list[list[Any]]
    modifies_data: bool | None = None
    affected_tables: list[str] | None = None
