"""Tests for test_runner.baseline."""

from __future__ import annotations

import json
from datetime import datetime, timezone
from decimal import Decimal
from pathlib import Path

import pytest

from test_runner.common.baseline import (
    get_baseline_path,
    load_baseline,
    serialize_result_sets,
    write_baseline,
)
from test_runner.common.models import BaselineData, TestCaseResult


class TestSerializeResultSets:
    def test_datetime_serialization(self):
        dt = datetime(2026, 1, 15, 10, 30, 0)
        rows = [[{"ts": dt}]]
        result = serialize_result_sets(rows)
        assert result[0][0]["ts"] == "2026-01-15T10:30:00"

    def test_decimal_serialization(self):
        rows = [[{"price": Decimal("10.50")}]]
        result = serialize_result_sets(rows)
        assert result[0][0]["price"] == "10.50"

    def test_bytes_serialization(self):
        rows = [[{"data": b"\xde\xad"}]]
        result = serialize_result_sets(rows)
        assert result[0][0]["data"] == "dead"

    def test_none_preserved(self):
        rows = [[{"val": None}]]
        result = serialize_result_sets(rows)
        assert result[0][0]["val"] is None

    def test_plain_values_pass_through(self):
        rows = [[{"id": 1, "name": "Widget"}]]
        result = serialize_result_sets(rows)
        assert result == [[{"id": 1, "name": "Widget"}]]


class TestGetBaselinePath:
    def test_path_format(self):
        dt = datetime(2026, 2, 13, tzinfo=timezone.utc)
        path = get_baseline_path("/base", "dbo.GetProducts", 0, "abcd1234", dt)
        assert path == Path("/base/20260213/dbo_GetProducts/case_000_abcd1234.json")

    def test_case_index_padding(self):
        dt = datetime(2026, 1, 1, tzinfo=timezone.utc)
        path = get_baseline_path("/base", "P", 42, "hash1234", dt)
        assert path.name == "case_042_hash1234.json"


class TestWriteBaseline:
    def test_creates_file(self, tmp_path: Path):
        baseline = BaselineData(
            procedure="dbo.GetProducts",
            parameters={"id": 1},
            captured_at="2026-02-13T10:00:00+00:00",
            result_sets=[[{"Id": 1, "Name": "Widget"}]],
            row_counts=[1],
            success=True,
        )
        dt = datetime(2026, 2, 13, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        assert path.exists()
        data = json.loads(path.read_text())
        assert data["procedure"] == "dbo.GetProducts"
        assert data["parameters"] == {"id": 1}
        assert data["success"] is True
        assert data["result_sets"] == [[{"Id": 1, "Name": "Widget"}]]

    def test_serializes_complex_types(self, tmp_path: Path):
        baseline = BaselineData(
            procedure="dbo.P",
            parameters={"dt": datetime(2026, 1, 1)},
            captured_at="2026-02-13T10:00:00+00:00",
            result_sets=[[{"val": Decimal("3.14")}]],
            row_counts=[1],
            success=True,
        )
        dt = datetime(2026, 2, 13, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        data = json.loads(path.read_text())
        assert data["parameters"]["dt"] == "2026-01-01T00:00:00"
        assert data["result_sets"][0][0]["val"] == "3.14"

    def test_failed_baseline(self, tmp_path: Path):
        baseline = BaselineData(
            procedure="dbo.P",
            parameters={},
            captured_at="2026-02-13T10:00:00+00:00",
            result_sets=[],
            row_counts=[],
            success=False,
            error="Procedure not found",
        )
        dt = datetime(2026, 2, 13, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        data = json.loads(path.read_text())
        assert data["success"] is False
        assert data["error"] == "Procedure not found"


class TestTableDeltasRoundTrip:
    """Verify table_deltas survive the write_baseline -> load_baseline cycle."""

    _SAMPLE_DELTAS = {
        "WAREHOUSE.COLORS": {
            "table": "WAREHOUSE.COLORS",
            "strategy_used": "full_snapshot",
            "inserts": [{"COLORID": 99, "COLORNAME": "Crimson", "LASTEDITEDBY": 1}],
            "deletes": [],
            "updates": [],
        }
    }

    def _make_baseline(self, *, table_deltas=None):
        return BaselineData(
            procedure="dbo.InsertColor",
            parameters={"color": "Crimson"},
            captured_at="2026-03-11T10:00:00+00:00",
            result_sets=[],
            row_counts=[],
            success=True,
            table_deltas=table_deltas,
        )

    def test_write_includes_table_deltas(self, tmp_path: Path):
        baseline = self._make_baseline(table_deltas=self._SAMPLE_DELTAS)
        dt = datetime(2026, 3, 11, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        data = json.loads(path.read_text())
        assert "table_deltas" in data
        assert "WAREHOUSE.COLORS" in data["table_deltas"]
        delta = data["table_deltas"]["WAREHOUSE.COLORS"]
        assert delta["strategy_used"] == "full_snapshot"
        assert len(delta["inserts"]) == 1
        assert delta["inserts"][0]["COLORNAME"] == "Crimson"

    def test_write_omits_table_deltas_when_none(self, tmp_path: Path):
        baseline = self._make_baseline(table_deltas=None)
        dt = datetime(2026, 3, 11, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        data = json.loads(path.read_text())
        assert "table_deltas" not in data

    def test_load_restores_table_deltas(self, tmp_path: Path):
        baseline = self._make_baseline(table_deltas=self._SAMPLE_DELTAS)
        dt = datetime(2026, 3, 11, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        loaded = load_baseline(path)
        assert loaded is not None
        assert loaded.table_deltas is not None
        assert "WAREHOUSE.COLORS" in loaded.table_deltas
        assert loaded.table_deltas["WAREHOUSE.COLORS"]["inserts"][0]["COLORNAME"] == "Crimson"

    def test_load_returns_none_table_deltas_when_absent(self, tmp_path: Path):
        baseline = self._make_baseline(table_deltas=None)
        dt = datetime(2026, 3, 11, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        loaded = load_baseline(path)
        assert loaded is not None
        assert loaded.table_deltas is None

    def test_datetime_values_in_deltas_serialized(self, tmp_path: Path):
        deltas = {
            "DBO.ORDERS": {
                "table": "DBO.ORDERS",
                "strategy_used": "full_snapshot",
                "inserts": [{"ID": 1, "CREATED": datetime(2026, 3, 11, 12, 0, 0)}],
                "deletes": [],
                "updates": [],
            }
        }
        baseline = self._make_baseline(table_deltas=deltas)
        dt = datetime(2026, 3, 11, tzinfo=timezone.utc)
        path = write_baseline(baseline, tmp_path, case_index=0, capture_date=dt)

        data = json.loads(path.read_text())
        assert data["table_deltas"]["DBO.ORDERS"]["inserts"][0]["CREATED"] == "2026-03-11T12:00:00"


class TestBaselineDataParamsHash:
    def test_deterministic(self):
        b1 = BaselineData(
            procedure="P", parameters={"a": 1, "b": 2},
            captured_at="", result_sets=[], row_counts=[], success=True,
        )
        b2 = BaselineData(
            procedure="P", parameters={"a": 1, "b": 2},
            captured_at="", result_sets=[], row_counts=[], success=True,
        )
        assert b1.params_hash == b2.params_hash

    def test_different_params_different_hash(self):
        b1 = BaselineData(
            procedure="P", parameters={"a": 1},
            captured_at="", result_sets=[], row_counts=[], success=True,
        )
        b2 = BaselineData(
            procedure="P", parameters={"a": 2},
            captured_at="", result_sets=[], row_counts=[], success=True,
        )
        assert b1.params_hash != b2.params_hash

    def test_hash_length(self):
        b = BaselineData(
            procedure="P", parameters={"x": "hello"},
            captured_at="", result_sets=[], row_counts=[], success=True,
        )
        assert len(b.params_hash) == 8
