"""
Validate runner -- orchestrates the full validation pipeline.

1. Resolve Snowflake executor from the injected registry
2. Discover test YAML files and filter to those with ``target`` steps
3. Load baselines from ``VALIDATION.BASELINE_ROWS`` table (stage-first)
4. For each YAML test case: materialize baseline + actual into temp tables,
   compare via the data-validation framework's Snowflake-native validators
5. Write results to ``VALIDATION.RESULTS``
"""

from __future__ import annotations

import logging
import re
import time
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Callable, Optional

from snowflake.snowflake_data_validation.connector.connector_base import ConnectorBase

from test_runner.common.baseline import compute_params_hash
from test_runner.common.config import TestRunnerConfig
from test_runner.common.database_dialect import DatabaseDialect
from test_runner.common.database_executor import DatabaseExecutor, DatabaseExecutorFactory
from test_runner.common.dependency_classifier import enrich_test_files
from test_runner.common.discovery import discover_test_files, filter_test_files
from test_runner.common.models import (
    MAX_DETAIL_LENGTH,
    MAX_SUMMARY_LENGTH,
    BaselineData,
    TestFile,
    ValidationCaseResult,
    ValidationDifference,
    ValidationStats,
    ValidationSteps,
    truncate,
)
from test_runner.common.template import (
    build_parameters,
    render_output_fetch_statements,
    render_validate_outputs_sql,
    render_validate_resultset_sql,
)

from .baseline_loader import create_baseline_loader
from .comparator import compare_results, errors_match
from .deploy import deploy_snowpark_validator
from .results_writer import write_results_batch, write_results_local
from test_runner.replay.validate_handler import validate_deltas
from test_runner.replay.isolation import SnowflakeCloneIsolation
from test_runner.parallel.partitioner import partition_cases
from test_runner.parallel.executor import ParallelExecutor

LOGGER = logging.getLogger(__name__)


def _rewrite_database(sql: str, original_db: str, clone_db: str) -> str:
    """Case-insensitive replacement of the database prefix in SQL.

    Rewrites ``CALL WideWorldImporters.Schema.Proc(...)`` to
    ``CALL CLONE_DB.Schema.Proc(...)`` so that ``EXECUTE AS OWNER``
    procedures run inside the clone, resolving unqualified table
    references to the clone's schemas.
    """
    return re.sub(
        r"(?<!\.)" + re.escape(original_db) + r"(?=\.)",
        clone_db,
        sql,
        flags=re.IGNORECASE,
    )


# ---------------------------------------------------------------------------
# Console output
# ---------------------------------------------------------------------------

_SEP = "\u2550" * 60


def _print_header(source: str) -> None:
    print(f"\n{_SEP}")
    print("  VALIDATING SNOWFLAKE AGAINST BASELINES")
    print(f"  Baseline source: {source}")
    print(f"{_SEP}\n")


def _print_case(
    procedure: str,
    status: str,
    match_type: str,
    error: str,
    differences: list[ValidationDifference | str] | None = None,
) -> None:
    icon = "\u2705" if status == "PASS" else "\u274c"
    detail = match_type
    if error:
        detail = f"{match_type}: {truncate(error, MAX_SUMMARY_LENGTH)}"
    print(f"{icon} {status} {procedure} -- {detail}")
    if status != "PASS" and differences:
        for i, diff in enumerate(differences[:5]):
            if isinstance(diff, ValidationDifference):
                print(f"    [{i}] level={diff.level}  type={diff.type}  detail={truncate(diff.detail, MAX_DETAIL_LENGTH)}")
            else:
                print(f"    [{i}] {truncate(str(diff), MAX_DETAIL_LENGTH)}")


def _print_summary(stats: ValidationStats) -> None:
    print(f"\n{_SEP}")
    print(f"  SUMMARY: {stats.total} test cases")
    print(f"  Passed: {stats.passed}  Failed: {stats.failed}  Errors: {stats.errors}")
    if stats.no_baseline:
        print(f"  No baseline: {stats.no_baseline}")
    print(f"{_SEP}\n")


def update_result_statistics(result: ValidationCaseResult, stats: ValidationStats) -> None:
    """Update *stats* counters from a single validation result."""
    stats.total += 1
    if result.status == "PASS":
        stats.passed += 1
    elif result.status == "NO_BASELINE":
        stats.no_baseline += 1
    elif result.status == "ERROR":
        stats.errors += 1
    else:
        stats.failed += 1


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

def run_validate(
    config: TestRunnerConfig,
    factory_registry: dict[DatabaseDialect, DatabaseExecutorFactory],
    project_root: str | Path,
    snowflake_connection: Optional[str] = None,
    baseline_dir: Optional[str | Path] = None,
    baseline_stage: Optional[str] = None,
    where: str | None = None,
    include_dependencies: bool = False,
    create_schema: bool = False,
    output_dir: Optional[str | Path] = None,
    dump_baseline_and_actual: bool = False,
    workers: int = 1,
) -> ValidationStats:
    """Execute the full validation pipeline.

    Baselines are loaded from the ``BASELINE_ROWS`` table first; if empty, falls
    back to the implicit stage ``@{database}.VALIDATION.BASELINES``.
    *dump_baseline_and_actual* requires *output_dir* and writes per-case row dumps.
    """
    project_root = Path(project_root)

    sf_factory = factory_registry.get(DatabaseDialect.SNOWFLAKE)
    if sf_factory is None:
        raise ValueError("No executor factory registered for Snowflake.")

    target_raw = dict(config.target_connection_raw)
    if snowflake_connection:
        target_raw["name"] = snowflake_connection
    if not target_raw.get("name"):
        raise ValueError(
            "Validate requires a Snowflake connection. Configure a default in "
            "~/.snowflake/config.toml or pass --connection."
        )
    target_raw.setdefault("mode", "name")
    sf_config = sf_factory.build_config(target_raw)
    executor = sf_factory.create_executor(sf_config)
    sf_factory.executor = executor

    connector = executor.connector
    if connector is None:
        raise ValueError(
            "Validate requires a Snowflake connector. "
            "The executor must provide a connector property."
        )

    try:
        if create_schema:
            deploy_snowpark_validator(connector, config.validation_database)

        test_files = discover_test_files(project_root, config.selectors)
        test_files = [tf for tf in test_files if tf.target is not None]
        test_files = filter_test_files(test_files, project_root, where, include_dependencies)

        enrich_test_files(test_files, project_root)

        if not test_files:
            print("No test YAML files with target steps found.")
            return ValidationStats()

        # Keep two-part procedure calls (schema.procedure) resolving to the
        # procedure database when validation objects live in a separate database.
        execution_database = next(
            (tf.target_database for tf in test_files if tf.target_database),
            config.validation_database,
        )
        cursor = connector.connection.cursor()
        try:
            cursor.execute(f"USE DATABASE {execution_database}")
        finally:
            cursor.close()

        loader = create_baseline_loader(
            connector=connector,
            database=config.validation_database,
            baseline_dir=Path(baseline_dir) if baseline_dir else None,
            baseline_stage=baseline_stage,
            project_root=project_root,
        )
        t0 = time.monotonic()
        baselines = loader.load()
        LOGGER.info("[timing] baseline load: %.3fs  (%d entries, source=%s)",
                    time.monotonic() - t0, len(baselines), loader.source_label)
        baseline_index: dict[tuple[str, str], BaselineData] = {
            (bl.procedure, bl.params_hash): bl for bl in baselines
        }

        _print_header(loader.source_label)

        stats = ValidationStats()
        all_results: list[ValidationCaseResult] = []
        format_literal = sf_factory.create_literal_formatter().format_literal

        procedure_db = next(
            (tf.target_database for tf in test_files if tf.target_database),
            None,
        )
        clone_source_db = (procedure_db or config.validation_database or "").upper()

        if workers > 1:
            all_results = _run_parallel_validate(
                test_files=test_files,
                baseline_index=baseline_index,
                connector=connector,
                sf_factory=sf_factory,
                sf_config=sf_config,
                format_literal=format_literal,
                config=config,
                dump_baseline_and_actual=dump_baseline_and_actual,
                workers=workers,
            )
            for result in all_results:
                update_result_statistics(result, stats)
                _print_case(
                    result.procedure, result.status,
                    result.match_type, result.error,
                    differences=result.differences or None,
                )
        else:
            run_isolation: SnowflakeCloneIsolation | None = None
            if clone_source_db:
                run_isolation = SnowflakeCloneIsolation(clone_source_db)
                t_clone = time.monotonic()
                run_isolation.setup(connector.connection)
                LOGGER.info(
                    "[timing] run-level clone setup: %.3fs -> %s",
                    time.monotonic() - t_clone, run_isolation.clone_database,
                )

            try:
                for test_file in test_files:
                    for case_idx, test_case in enumerate(test_file.test_cases):
                        t_case = time.monotonic()
                        result = _validate_case(
                            test_file=test_file,
                            test_case=test_case,
                            baseline_index=baseline_index,
                            connector=connector,
                            executor=executor,
                            format_literal=format_literal,
                            config=config,
                            dump_baseline_and_actual=dump_baseline_and_actual,
                            run_isolation=run_isolation,
                            executor_factory=sf_factory,
                        )
                        LOGGER.info("[timing] case %s (%s): %.3fs  status=%s",
                                    result.procedure, result.params_hash,
                                    time.monotonic() - t_case, result.status)
                        all_results.append(result)
                        update_result_statistics(result, stats)
                        _print_case(
                            result.procedure, result.status,
                            result.match_type, result.error,
                            differences=result.differences or None,
                        )
            finally:
                if run_isolation is not None:
                    run_isolation.teardown(connector.connection)

        if all_results:
            result_dicts = [r.to_dict() for r in all_results]
            # Write local output first -- it pops _baseline_rows/_actual_rows from
            # each result dict and saves them as separate files.  write_results_batch
            # must run afterwards so the Snowflake insert never sees those keys.
            if output_dir:
                try:
                    write_results_local(result_dicts, Path(output_dir))
                except Exception as exc:
                    print(f"Warning: failed to write local results to {output_dir}: {exc}")

            t_write = time.monotonic()
            try:
                write_results_batch(connector, result_dicts, config.validation_database)
                LOGGER.info("[timing] write_results_batch: %.3fs", time.monotonic() - t_write)
            except Exception as exc:
                print(f"Warning: failed to write results to VALIDATION.RESULTS: {exc}")

        _print_summary(stats)
        return stats

    finally:
        executor.close()


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------


def _run_parallel_validate(
    test_files: list[TestFile],
    baseline_index: dict[tuple[str, str], BaselineData],
    connector: ConnectorBase,
    sf_factory: DatabaseExecutorFactory,
    sf_config: Any,
    format_literal: Callable[[Any], str],
    config: TestRunnerConfig,
    dump_baseline_and_actual: bool,
    workers: int,
) -> list[ValidationCaseResult]:
    """Partition test cases and run read-only cases in parallel.

    DML cases fall back to sequential execution with a run-level
    zero-copy clone for isolation.
    """
    read_only_cases, dml_cases = partition_cases(test_files)
    LOGGER.info(
        "[parallel] %d read-only cases, %d DML cases, %d workers",
        len(read_only_cases), len(dml_cases), workers,
    )

    all_results: list[ValidationCaseResult] = []

    # -- Read-only cases in parallel ------------------------------------------
    if read_only_cases:
        if sf_config is None:
            raise RuntimeError(
                "Parallel mode requires a Snowflake connection config "
                "(not a connection_override)."
            )

        def _worker_validate(
            thread_executor: DatabaseExecutor,
            test_file: TestFile,
            case_idx: int,
            test_case: list[Any],
        ) -> ValidationCaseResult:
            t_case = time.monotonic()
            try:
                result = _validate_case(
                    test_file=test_file,
                    test_case=test_case,
                    baseline_index=baseline_index,
                    connector=thread_executor.connector,
                    executor=thread_executor,
                    format_literal=format_literal,
                    config=config,
                    dump_baseline_and_actual=dump_baseline_and_actual,
                )
            except Exception as exc:
                LOGGER.error(
                    "Worker exception for %s: %s", test_file.code_unit_name, exc,
                )
                parameters = build_parameters(test_file.parameter_names, test_case)
                result = ValidationCaseResult(
                    procedure=test_file.code_unit_name,
                    params_hash=compute_params_hash(parameters),
                    params=parameters,
                    status="ERROR",
                    error=str(exc),
                    metadata={"worker_exception": True},
                    executed_at=datetime.now(timezone.utc).isoformat(),
                )
            LOGGER.info(
                "[timing] case %s (%s): %.3fs  status=%s  [parallel-ro]",
                result.procedure, result.params_hash,
                time.monotonic() - t_case, result.status,
            )
            return result

        par = ParallelExecutor(max_workers=workers)
        ro_results = par.execute_with_factory(
            read_only_cases, sf_factory, sf_config, _worker_validate,
        )
        all_results.extend(ro_results)

    # -- DML cases sequentially with run-level clone --------------------------
    if dml_cases:
        procedure_db = next(
            (tf.target_database for tf in test_files if tf.target_database),
            None,
        )
        clone_source_db = (procedure_db or config.validation_database or "").upper()
        run_isolation: SnowflakeCloneIsolation | None = None
        if clone_source_db:
            run_isolation = SnowflakeCloneIsolation(clone_source_db)
            t_clone = time.monotonic()
            run_isolation.setup(connector.connection)
            LOGGER.info(
                "[timing] DML run-level clone setup: %.3fs -> %s",
                time.monotonic() - t_clone, run_isolation.clone_database,
            )

        try:
            for test_file, case_idx, test_case in dml_cases:
                t_case = time.monotonic()
                result = _validate_case(
                    test_file=test_file,
                    test_case=test_case,
                    baseline_index=baseline_index,
                    connector=connector,
                    executor=sf_factory.executor,
                    format_literal=format_literal,
                    config=config,
                    dump_baseline_and_actual=dump_baseline_and_actual,
                    run_isolation=run_isolation,
                    executor_factory=sf_factory,
                )
                LOGGER.info(
                    "[timing] case %s (%s): %.3fs  status=%s  [sequential-dml]",
                    result.procedure, result.params_hash,
                    time.monotonic() - t_case, result.status,
                )
                all_results.append(result)
        finally:
            if run_isolation is not None:
                run_isolation.teardown(connector.connection)

    return all_results


@dataclass
class _ImplicitReturnCheck:
    """Result of checking whether a Snowflake CALL returned only the
    implicit procedure return value."""
    is_implicit: bool
    row_count: int = 0
    col_names: list[str] | None = None
    error: str | None = None


def _is_implicit_return(col_names: list[str], row_count: int, procedure: str) -> bool:
    """True when the result set looks like Snowflake's implicit CALL return.

    Snowflake SQL Script procedures always return a single-row,
    single-column result set where the column is named after the
    procedure's short name (uppercased).  When the CALL is wrapped
    in a ``BEGIN … END`` scripting block (e.g. because ``pre_execute``
    contains LET declarations), the column is ``ANONYMOUS BLOCK``
    instead.
    """
    if row_count != 1 or len(col_names) != 1:
        return False
    col = col_names[0].upper()
    short_name = procedure.rsplit(".", 1)[-1].upper()
    return col == short_name or col == "ANONYMOUS BLOCK"


def _execute_and_check_implicit_return(
    connector: ConnectorBase,
    rendered_sql: str,
    procedure: str,
) -> _ImplicitReturnCheck:
    """Execute the CALL and determine if the result is just the implicit
    Snowflake procedure return value.

    The caller is responsible for isolation (typically by rewriting
    *rendered_sql* to target a zero-copy clone database so that DML
    side effects from ``EXECUTE AS OWNER`` procedures never touch the
    original database).
    """
    cursor = connector.connection.cursor()
    try:
        cursor.execute(rendered_sql)
        col_names = [d[0] for d in cursor.description] if cursor.description else []
        rows = cursor.fetchall() or []
        row_count = len(rows)

        implicit = _is_implicit_return(col_names, row_count, procedure)
        return _ImplicitReturnCheck(
            is_implicit=implicit,
            row_count=row_count,
            col_names=col_names,
        )
    except Exception as exc:
        return _ImplicitReturnCheck(
            is_implicit=False,
            error=str(exc),
        )
    finally:
        cursor.close()


# ---------------------------------------------------------------------------
# Internal: validate a single test case
# ---------------------------------------------------------------------------

def _validate_case(
    test_file: TestFile,
    test_case: list[Any],
    baseline_index: dict[tuple[str, str], BaselineData],
    connector: ConnectorBase,
    executor: DatabaseExecutor,
    format_literal: Callable[[Any], str],
    config: TestRunnerConfig,
    dump_baseline_and_actual: bool = False,
    run_isolation: SnowflakeCloneIsolation | None = None,
    executor_factory: DatabaseExecutorFactory | None = None,
) -> ValidationCaseResult:
    """Validate one test case from a YAML file against its baseline.

    When *run_isolation* is provided (the normal production path), all
    procedure executions target the run-level clone database and affected
    tables are reverted via fast table-level clones before each execution.
    When ``None`` (unit-test fallback), SQL is executed as-is.
    """
    parameters = build_parameters(test_file.parameter_names, test_case)
    params_hash = compute_params_hash(parameters)
    procedure = test_file.code_unit_name

    result = ValidationCaseResult(
        procedure=procedure,
        params_hash=params_hash,
        params=parameters,
        status="",
        metadata={
            "modifies_data": test_file.modifies_data,
            "affected_tables": test_file.affected_tables,
            "source_checksum": test_file.source_checksum,
            "converted_checksum": test_file.converted_checksum,
        },
        executed_at=datetime.now(timezone.utc).isoformat(),
    )

    baseline = baseline_index.get((procedure, params_hash))
    if baseline is None:
        result.status = "NO_BASELINE"
        result.error = f"No baseline found for {procedure} (params_hash={params_hash})"
        return result

    assert test_file.target is not None
    target_sql = render_validate_resultset_sql(
        test_file.target, test_case, format_literal,
    )

    fetch_stmts = render_output_fetch_statements(
        test_file.target, test_case, "snowflake",
    )

    rendered_steps = ValidationSteps(
        run=test_file.target.run,
        pre_execute=test_file.target.pre_execute,
        output_parameters=test_file.target.output_parameters,
        output_tables=test_file.target.output_tables,
    )

    outputs_sql: str | None = None
    if test_file.target.output_parameters:
        outputs_sql = render_validate_outputs_sql(
            test_file.target, test_case, format_literal,
        )

    procedure_db = (test_file.target_database or config.validation_database or "").upper()
    has_deltas = bool(baseline.table_deltas)
    replay_cfg = config.replay_configuration

    has_nonempty_results = (
        baseline.result_sets
        and any(rs for rs in baseline.result_sets)
    )
    has_nonzero_counts = (
        baseline.row_counts
        and any(c > 0 for c in baseline.row_counts)
    )
    no_baseline_resultset = (
        not has_nonempty_results
        and not has_nonzero_counts
        and not outputs_sql
    )

    # --- Clone and table-revert helpers --------------------------------
    clone_db = run_isolation.clone_database if run_isolation else None

    def _schema_table(fqn: str) -> str:
        """Normalise to SCHEMA.TABLE, stripping any leading DATABASE prefix."""
        parts = fqn.upper().split(".")
        return ".".join(parts[-2:]) if len(parts) >= 2 else fqn.upper()

    revert_fqns: set[str] = set()
    if baseline.table_deltas:
        revert_fqns.update(_schema_table(k) for k in baseline.table_deltas)
    if test_file.affected_tables:
        revert_fqns.update(_schema_table(t) for t in test_file.affected_tables)
    _revert_list = sorted(revert_fqns)

    def _revert() -> None:
        """Reset affected tables in the clone and switch the session to the
        clone database so ``EXECUTE AS CALLER`` procedures resolve
        unqualified table references against the clone."""
        if run_isolation and _revert_list:
            run_isolation.revert_tables(connector.connection, _revert_list)
        if clone_db:
            cur = connector.connection.cursor()
            try:
                cur.execute(f"USE DATABASE {clone_db}")
            finally:
                cur.close()

    def _to_clone(sql: str) -> str:
        """Rewrite SQL to target the clone database."""
        if clone_db and procedure_db:
            return _rewrite_database(sql, procedure_db, clone_db)
        return sql

    # ------------------------------------------------------------------
    # Error baselines
    # ------------------------------------------------------------------
    if not baseline.success:
        _revert()
        clone_sql = _to_clone(target_sql)
        capture_result = executor.execute(clone_sql, steps=rendered_steps)

        if not capture_result.success:
            if errors_match(baseline.error or "", capture_result.error or ""):
                result.status = "PASS"
                result.match_type = "error_match"
            else:
                result.status = "FAIL"
                result.match_type = "error_mismatch"
                result.error = (
                    f"Baseline: {truncate(baseline.error or '', MAX_SUMMARY_LENGTH)}, "
                    f"Actual: {truncate(capture_result.error or '', MAX_SUMMARY_LENGTH)}"
                )
        else:
            result.status = "FAIL"
            result.error = f"Baseline errored: {truncate(baseline.error or '', MAX_SUMMARY_LENGTH)}"
        return result

    # ------------------------------------------------------------------
    # No baseline result sets — implicit-return check, then optional
    # delta validation.
    # ------------------------------------------------------------------
    if no_baseline_resultset:
        _revert()
        clone_sql = _to_clone(target_sql)
        implicit = _execute_and_check_implicit_return(
            connector, clone_sql, procedure,
        )


        if implicit.error:
            result.status = "FAIL"
            result.match_type = "error"
            result.match_level = "in_memory"
            result.error = truncate(implicit.error, MAX_SUMMARY_LENGTH)
            result.differences = [
                ValidationDifference(type="ERROR", detail=truncate(implicit.error, MAX_DETAIL_LENGTH)),
            ]
            return result
        elif not implicit.is_implicit:
            result.status = "FAIL"
            result.match_type = "unexpected_resultset"
            result.match_level = "in_memory"
            detail = (
                f"Baseline has no result sets but Snowflake returned "
                f"{implicit.row_count} row(s) with columns {implicit.col_names}"
            )
            result.error = detail
            result.differences = [detail]
            return result

        check_deltas = has_deltas or (
            test_file.modifies_data is True
            and bool(test_file.affected_tables)
        )
        LOGGER.info(
            "Snowflake returned only the implicit procedure return "
            "for %s (no baseline result sets); %s.",
            procedure,
            "proceeding to delta validation" if check_deltas else "skipping comparison",
        )
        result.status = "PASS"
        result.match_type = "no_resultset"
        result.match_level = "skipped"

        if check_deltas:
            try:
                _revert()

                def _exec_on_sf(database_override: str | None = None) -> None:
                    sql = _to_clone(target_sql)
                    if database_override and procedure_db:
                        sql = _rewrite_database(target_sql, procedure_db, database_override)
                    capture = executor.execute(sql, steps=rendered_steps)
                    if not capture.success:
                        raise RuntimeError(
                            f"Procedure execution failed during delta validation: "
                            f"{capture.error}"
                        )

                delta_result = validate_deltas(
                    connection=connector.connection,
                    baseline=baseline,
                    replay_cfg=replay_cfg,
                    execute_fn=_exec_on_sf,
                    database=procedure_db,
                    procedure=procedure,
                    params_hash=params_hash,
                    affected_tables=test_file.affected_tables,
                    clone_db=clone_db,
                    executor_factory=executor_factory,
                )

                if not delta_result.match:
                    result.status = "FAIL"
                    result.match_type = delta_result.match_type
                    detail_summary = "; ".join(
                        f"{t}: {len(d.get('differences', []))} diff(s)"
                        for t, d in delta_result.details.items()
                        if not d.get("match", True)
                    )
                    result.error = f"Delta mismatch: {detail_summary}"
                elif result.status == "PASS":
                    result.match_type = "delta_match"

                result.metadata["delta_comparison"] = delta_result.to_dict()
            except Exception as exc:
                LOGGER.error(
                    "Delta validation failed for %s: %s", procedure, exc,
                )
                result.status = "FAIL"
                result.match_type = "delta_error"
                result.error = f"Delta validation failed: {exc}"
                result.metadata["delta_validation_error"] = str(exc)

        return result

    # ------------------------------------------------------------------
    # Procedure WITH result sets — RS comparison + optional delta
    # ------------------------------------------------------------------
    outputs_only = bool(not baseline.row_counts and outputs_sql)

    _revert()
    clone_sql = _to_clone(target_sql)
    clone_outputs_sql = _to_clone(outputs_sql) if outputs_sql else None

    comparison = compare_results(
        config=config,
        connector=connector,
        rendered_sql=clone_sql,
        params_hash=params_hash,
        code_unit_name=procedure,
        baseline=baseline,
        dump_baseline_and_actual=dump_baseline_and_actual,
        fetch_stmts=fetch_stmts or None,
        outputs_sql=clone_outputs_sql,
        outputs_only=outputs_only,
    )

    result.status = "PASS" if comparison.match else "FAIL"
    result.match_type = comparison.match_type
    result.match_level = comparison.match_level
    result.differences = list(comparison.differences[:5])

    if comparison.in_memory_diff is not None:
        result.in_memory_diff = comparison.in_memory_diff

    if comparison.baseline_rows is not None:
        result.baseline_rows = comparison.baseline_rows
    if comparison.actual_rows is not None:
        result.actual_rows = comparison.actual_rows

    if not comparison.match and comparison.differences:
        first = comparison.differences[0]
        if isinstance(first, ValidationDifference):
            result.error = first.detail
        else:
            result.error = str(first)

    # Delta validation reuses a fresh clone (RS clone was dropped).
    # When modifies_data is True we always validate deltas — even if the
    # source baseline has none — to catch erroneous Snowflake mutations.
    check_for_unexpected_mutations = (
        test_file.modifies_data is True
        and bool(test_file.affected_tables)
        and not has_deltas
    )
    if has_deltas or check_for_unexpected_mutations:
        try:
            _revert()

            def _exec_on_sf(database_override: str | None = None) -> None:
                sql = _to_clone(target_sql)
                if database_override and procedure_db:
                    sql = _rewrite_database(target_sql, procedure_db, database_override)
                capture = executor.execute(sql, steps=rendered_steps)
                if not capture.success:
                    raise RuntimeError(
                        f"Procedure execution failed during delta validation: "
                        f"{capture.error}"
                    )

            delta_result = validate_deltas(
                connection=connector.connection,
                baseline=baseline,
                replay_cfg=replay_cfg,
                execute_fn=_exec_on_sf,
                database=procedure_db,
                procedure=procedure,
                params_hash=params_hash,
                affected_tables=test_file.affected_tables,
                clone_db=clone_db,
                executor_factory=executor_factory,
            )

            if not delta_result.match:
                result.status = "FAIL"
                result.match_type = delta_result.match_type
                detail_summary = "; ".join(
                    f"{t}: {len(d.get('differences', []))} diff(s)"
                    for t, d in delta_result.details.items()
                    if not d.get("match", True)
                )
                result.error = f"Delta mismatch: {detail_summary}"
            elif result.status == "PASS":
                result.match_type = "result_and_delta_match"

            result.metadata["delta_comparison"] = delta_result.to_dict()
        except Exception as exc:
            LOGGER.error(
                "Delta validation failed for %s: %s", procedure, exc,
            )
            result.status = "FAIL"
            result.match_type = "delta_error"
            result.error = f"Delta validation failed: {exc}"
            result.metadata["delta_validation_error"] = str(exc)

    return result
