# #region Test.Models.FilterState [C:2] [TYPE Module] [SEMANTICS test,models,filter,state,pydantic]
# @BRIEF Tests for models/filter_state.py — FilterState, NativeFilterDataMask,
#        ParsedNativeFilters, DashboardURLFilterExtraction, ExtraFormDataMerge.
# @RELATION BINDS_TO -> [FilterStateModels]
# @TEST_EDGE: missing_field -> optional fields default correctly
# @TEST_EDGE: invalid_type -> Pydantic validation rejects bad types
# @TEST_EDGE: serialization -> round-trip via model_dump/model_validate

from datetime import datetime, timezone
import pytest
from pydantic import ValidationError


class TestFilterState:
    """FilterState — single native filter state."""

    def test_defaults(self):
        from src.models.filter_state import FilterState

        fs = FilterState()
        assert fs.extraFormData == {}
        assert fs.filterState == {}
        assert fs.ownState == {}

    def test_with_values(self):
        from src.models.filter_state import FilterState

        fs = FilterState(
            extraFormData={"filters": [{"col": "region", "op": "IN", "val": ["EMEA"]}]},
            filterState={"value": ["EMEA"]},
            ownState={"id": "FILTER-1"},
        )
        assert fs.extraFormData["filters"][0]["col"] == "region"
        assert fs.filterState["value"] == ["EMEA"]
        assert fs.ownState["id"] == "FILTER-1"

    def test_extra_allows_unknown(self):
        from src.models.filter_state import FilterState

        fs = FilterState(unknown_field="anything")
        assert fs.model_extra == {"unknown_field": "anything"}
        assert fs.unknown_field == "anything"

    def test_serialize_roundtrip(self):
        from src.models.filter_state import FilterState

        fs = FilterState(extraFormData={"filters": []}, filterState={"value": None})
        data = fs.model_dump()
        restored = FilterState.model_validate(data)
        assert "filters" in restored.extraFormData


class TestNativeFilterDataMask:
    """NativeFilterDataMask — container for filter states."""

    def test_defaults(self):
        from src.models.filter_state import NativeFilterDataMask

        mask = NativeFilterDataMask()
        assert mask.filters == {}

    def test_with_filters(self):
        from src.models.filter_state import FilterState, NativeFilterDataMask

        fs = FilterState(extraFormData={"filters": [{"col": "region"}]}, filterState={"value": ["EMEA"]})
        mask = NativeFilterDataMask(filters={"FILTER-1": fs})
        assert "FILTER-1" in mask.filters
        assert mask.get_filter_ids() == ["FILTER-1"]
        assert mask.get_extra_form_data("FILTER-1")["filters"][0]["col"] == "region"
        assert mask.get_extra_form_data("NONEXISTENT") == {}

    def test_serialize_roundtrip(self):
        from src.models.filter_state import FilterState, NativeFilterDataMask

        fs = FilterState()
        mask = NativeFilterDataMask(filters={"F1": fs})
        data = mask.model_dump()
        restored = NativeFilterDataMask.model_validate(data)
        assert "F1" in restored.filters

    def test_extra_allows_unknown(self):
        from src.models.filter_state import NativeFilterDataMask

        mask = NativeFilterDataMask(extra_field="test")
        assert mask.extra_field == "test"


class TestParsedNativeFilters:
    """ParsedNativeFilters — result of parsing native filters."""

    def test_defaults(self):
        from src.models.filter_state import ParsedNativeFilters

        pf = ParsedNativeFilters()
        assert pf.dataMask == {}
        assert pf.filter_type is None
        assert pf.dashboard_id is None
        assert pf.active_tabs == []
        assert pf.chart_states == {}

    def test_with_values(self):
        from src.models.filter_state import ParsedNativeFilters

        pf = ParsedNativeFilters(
            dataMask={"FILTER-1": {}},
            filter_type="permalink",
            dashboard_id="42",
            permalink_key="abc123",
            active_tabs=["tab1"],
            chart_states={"CHART-1": {}},
        )
        assert pf.filter_type == "permalink"
        assert pf.dashboard_id == "42"
        assert pf.has_filters() is True
        assert pf.get_filter_count() == 1

    def test_no_filters(self):
        from src.models.filter_state import ParsedNativeFilters

        pf = ParsedNativeFilters()
        assert pf.has_filters() is False
        assert pf.get_filter_count() == 0

    def test_serialize_roundtrip(self):
        from src.models.filter_state import ParsedNativeFilters

        pf = ParsedNativeFilters(dataMask={"F1": {}}, filter_type="native_filters_key")
        data = pf.model_dump()
        restored = ParsedNativeFilters.model_validate(data)
        assert restored.filter_type == "native_filters_key"


class TestDashboardURLFilterExtraction:
    """DashboardURLFilterExtraction — complete URL parsing result."""

    def test_minimal(self):
        from src.models.filter_state import DashboardURLFilterExtraction

        dfe = DashboardURLFilterExtraction(url="http://superset/dash/42")
        assert dfe.url == "http://superset/dash/42"
        assert dfe.dashboard_id is None
        assert dfe.success is True
        assert dfe.error is None
        assert dfe.filters.dataMask == {}

    def test_with_error(self):
        from src.models.filter_state import DashboardURLFilterExtraction

        dfe = DashboardURLFilterExtraction(url="http://bad", success=False, error="Invalid URL")
        assert dfe.success is False
        assert dfe.error == "Invalid URL"

    def test_with_filters(self):
        from src.models.filter_state import DashboardURLFilterExtraction, ParsedNativeFilters

        pf = ParsedNativeFilters(dataMask={"F1": {}}, filter_type="permalink")
        dfe = DashboardURLFilterExtraction(url="http://superset/dash/42", dashboard_id="42", filter_type="permalink", filters=pf)
        assert dfe.dashboard_id == "42"
        assert dfe.filters.filter_type == "permalink"

    def test_missing_url_raises(self):
        from src.models.filter_state import DashboardURLFilterExtraction

        with pytest.raises(ValidationError):
            DashboardURLFilterExtraction()

    def test_serialize_roundtrip(self):
        from src.models.filter_state import DashboardURLFilterExtraction

        dfe = DashboardURLFilterExtraction(url="http://superset/dash/1", dashboard_id="1")
        data = dfe.model_dump()
        restored = DashboardURLFilterExtraction.model_validate(data)
        assert restored.url == "http://superset/dash/1"
        assert restored.dashboard_id == "1"


class TestExtraFormDataMerge:
    """ExtraFormDataMerge — merge configuration and merge method."""

    def test_defaults(self):
        from src.models.filter_state import ExtraFormDataMerge

        merge = ExtraFormDataMerge()
        assert "filters" in merge.append_keys
        assert "time_range" in merge.override_keys

    def test_merge_overrides(self):
        from src.models.filter_state import ExtraFormDataMerge

        merge = ExtraFormDataMerge()
        original = {"time_range": "LAST_7_DAYS", "filters": [{"col": "region", "val": "EMEA"}]}
        new = {"time_range": "LAST_30_DAYS", "filters": [{"col": "product", "val": "Widget"}]}
        result = merge.merge(original, new)
        assert result["time_range"] == "LAST_30_DAYS"  # overridden
        # filters is in append_keys, so it should merge lists
        assert len(result["filters"]) == 2  # appended

    def test_merge_appends(self):
        from src.models.filter_state import ExtraFormDataMerge

        merge = ExtraFormDataMerge()
        original = {"filters": [{"col": "region"}]}
        new = {"filters": [{"col": "product"}]}
        result = merge.merge(original, new)
        assert len(result["filters"]) == 2

    def test_merge_non_list_append_fallback(self):
        from src.models.filter_state import ExtraFormDataMerge

        merge = ExtraFormDataMerge()
        original = {"extras": "single_value"}
        new = {"extras": "override"}
        result = merge.merge(original, new)
        # extras is in append_keys but not a list, so it falls back to new_value
        assert result["extras"] == "override"

    def test_custom_merge(self):
        from src.models.filter_state import ExtraFormDataMerge

        merge = ExtraFormDataMerge(append_keys=["custom_list"], override_keys=["custom_val"])
        original = {"custom_list": [1], "custom_val": "old"}
        new = {"custom_list": [2], "custom_val": "new"}
        result = merge.merge(original, new)
        assert result["custom_val"] == "new"
        assert result["custom_list"] == [1, 2]

    def test_new_keys_added(self):
        from src.models.filter_state import ExtraFormDataMerge

        merge = ExtraFormDataMerge()
        original = {"key1": "val1"}
        new = {"key2": "val2"}
        result = merge.merge(original, new)
        assert result["key1"] == "val1"
        assert result["key2"] == "val2"

    def test_serialize_roundtrip(self):
        from src.models.filter_state import ExtraFormDataMerge

        merge = ExtraFormDataMerge()
        data = merge.model_dump()
        restored = ExtraFormDataMerge.model_validate(data)
        assert "filters" in restored.append_keys
# #endregion Test.Models.FilterState
