# #region Test.DashboardsFiltersMixin [C:3] [TYPE Module] [SEMANTICS test, dashboard, filter, permalink, mixin]
# @BRIEF Tests for core/superset_client/_dashboards_filters.py — SupersetDashboardsFiltersMixin.
# @RELATION BINDS_TO -> [SupersetDashboardsFiltersMixin]

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))

from unittest.mock import AsyncMock, MagicMock, patch

import pytest


class TestDashboardsFiltersMixin:
    """Test all methods of SupersetDashboardsFiltersMixin."""

    @pytest.fixture
    def mixin(self):
        from src.core.superset_client._dashboards_filters import (
            SupersetDashboardsFiltersMixin,
        )

        m = SupersetDashboardsFiltersMixin()
        m.client = MagicMock()
        m.client.request = AsyncMock()
        return m

    @pytest.mark.asyncio
    async def test_get_dashboard_with_int(self, mixin):
        mixin.client.request.return_value = {"result": {"id": 1, "dashboard_title": "Test"}}
        result = await mixin.get_dashboard(1)
        mixin.client.request.assert_awaited_once_with(method="GET", endpoint="/dashboard/1")
        assert result["result"]["id"] == 1

    @pytest.mark.asyncio
    async def test_get_dashboard_with_str(self, mixin):
        mixin.client.request.return_value = {"result": {"id": 2, "slug": "my-dash"}}
        result = await mixin.get_dashboard("my-dash")
        mixin.client.request.assert_awaited_once_with(method="GET", endpoint="/dashboard/my-dash")
        assert result["result"]["slug"] == "my-dash"

    @pytest.mark.asyncio
    async def test_get_dashboard_permalink_state(self, mixin):
        mixin.client.request.return_value = {"result": {"state": {"dataMask": {}}}}
        result = await mixin.get_dashboard_permalink_state("abc123")
        mixin.client.request.assert_awaited_once_with(
            method="GET", endpoint="/dashboard/permalink/abc123"
        )
        assert "state" in result["result"]

    @pytest.mark.asyncio
    async def test_get_native_filter_state(self, mixin):
        mixin.client.request.return_value = {"result": {"value": '{"id": 1}'}}
        result = await mixin.get_native_filter_state(1, "key123")
        mixin.client.request.assert_awaited_once_with(
            method="GET", endpoint="/dashboard/1/filter_state/key123"
        )
        assert "value" in result["result"]

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_permalink(self, mixin):
        mixin.get_dashboard_permalink_state = AsyncMock(
            return_value={
                "result": {
                    "state": {
                        "dataMask": {
                            "filter_1": {
                                "extraFormData": {"col1": "val1"},
                                "filterState": {"value": 1},
                                "ownState": {},
                            }
                        },
                        "activeTabs": ["tab1"],
                        "anchor": "tab1",
                        "chartStates": {"chart1": {}},
                    }
                }
            }
        )
        result = await mixin.extract_native_filters_from_permalink("perm_key")
        assert result["dataMask"]["filter_1"]["extraFormData"] == {"col1": "val1"}
        assert result["activeTabs"] == ["tab1"]
        assert result["anchor"] == "tab1"
        assert result["chartStates"] == {"chart1": {}}
        assert result["permalink_key"] == "perm_key"

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_permalink_empty_state(self, mixin):
        mixin.get_dashboard_permalink_state = AsyncMock(return_value={})
        result = await mixin.extract_native_filters_from_permalink("empty_key")
        assert result["dataMask"] == {}
        assert result["activeTabs"] == []

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_permalink_skips_non_dict(self, mixin):
        mixin.get_dashboard_permalink_state = AsyncMock(
            return_value={
                "result": {
                    "state": {
                        "dataMask": {
                            "filter_1": {"extraFormData": {}, "filterState": {}, "ownState": {}},
                            "filter_2": "not_a_dict",
                        }
                    }
                }
            }
        )
        result = await mixin.extract_native_filters_from_permalink("key")
        assert "filter_1" in result["dataMask"]
        assert "filter_2" not in result["dataMask"]

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_key_with_str_value(self, mixin):
        mixin.get_native_filter_state = AsyncMock(
            return_value={"result": {"value": '{"id": "f1", "extraFormData": {}, "filterState": {}, "ownState": {}}'}}
        )
        result = await mixin.extract_native_filters_from_key(1, "key1")
        assert "f1" in result["dataMask"]
        assert result["dashboard_id"] == 1
        assert result["filter_state_key"] == "key1"

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_key_with_dict_value(self, mixin):
        mixin.get_native_filter_state = AsyncMock(
            return_value={
                "result": {
                    "value": {
                        "f1": {"extraFormData": {}, "filterState": {}, "ownState": {}},
                        "f2": {"extraFormData": {}, "filterState": {}, "ownState": {}},
                    }
                }
            }
        )
        result = await mixin.extract_native_filters_from_key(1, "key2")
        assert "f1" in result["dataMask"]
        assert "f2" in result["dataMask"]

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_key_with_invalid_json(self, mixin):
        mixin.get_native_filter_state = AsyncMock(
            return_value={"result": {"value": "{invalid json}"}}
        )
        with patch("src.core.superset_client._dashboards_filters.app_logger") as mock_log:
            result = await mixin.extract_native_filters_from_key(1, "bad_key")
            mock_log.warning.assert_called_once()
            assert result["dataMask"] == {}

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_key_with_non_dict_value(self, mixin):
        mixin.get_native_filter_state = AsyncMock(
            return_value={"result": {"value": 42}}
        )
        result = await mixin.extract_native_filters_from_key(1, "int_key")
        assert result["dataMask"] == {}

    @pytest.mark.asyncio
    async def test_extract_native_filters_from_key_skips_non_dict_items(self, mixin):
        mixin.get_native_filter_state = AsyncMock(
            return_value={
                "result": {
                    "value": {
                        "f1": {"extraFormData": {}, "filterState": {}, "ownState": {}},
                        "f2": "not_a_dict",
                    }
                }
            }
        )
        result = await mixin.extract_native_filters_from_key(1, "key3")
        assert "f1" in result["dataMask"]
        assert "f2" not in result["dataMask"]

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_for_filters_permalink(self, mixin):
        mixin.extract_native_filters_from_permalink = AsyncMock(
            return_value={"dataMask": {}, "permalink_key": "abc123"}
        )
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/dashboard/p/abc123/"
        )
        assert result["filter_type"] == "permalink"
        assert result["filters"]["permalink_key"] == "abc123"

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_for_filters_native_filters_key_with_int_id(self, mixin):
        mixin.extract_native_filters_from_key = AsyncMock(
            return_value={"dataMask": {}, "dashboard_id": 5}
        )
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/dashboard/5/?native_filters_key=key123"
        )
        assert result["filter_type"] == "native_filters_key"
        assert result["dashboard_id"] == 5

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_for_filters_native_filters_key_with_slug(self, mixin):
        # Slug resolution calls get_dashboard first
        mixin.get_dashboard = AsyncMock(return_value={"result": {"id": 42}})
        mixin.extract_native_filters_from_key = AsyncMock(
            return_value={"dataMask": {}, "dashboard_id": 42}
        )
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/dashboard/my-slug/?native_filters_key=key456"
        )
        assert result["filter_type"] == "native_filters_key"
        assert result["dashboard_id"] == 42

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_for_filters_slug_resolution_fails(self, mixin):
        mixin.get_dashboard = AsyncMock(side_effect=Exception("Not found"))
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/dashboard/unknown/?native_filters_key=key789"
        )
        # Should have no filter_type set since resolution failed
        assert result["filter_type"] is None

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_for_filters_native_filters_json(self, mixin):
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/dashboard/1/?native_filters=%7B%22key%22%3A%22val%22%7D"
        )
        assert result["filter_type"] == "native_filters"
        assert "dataMask" in result["filters"]

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_for_filters_bad_native_filters_json(self, mixin):
        with patch("src.core.superset_client._dashboards_filters.app_logger") as mock_log:
            result = await mixin.parse_dashboard_url_for_filters(
                "https://superset.example.com/dashboard/1/?native_filters={invalid}"
            )
            assert result["filter_type"] is None
            mock_log.warning.assert_called_once()

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_for_filters_no_filters(self, mixin):
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/dashboard/1/"
        )
        assert result["filter_type"] is None
        assert result["filters"] == {}

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_no_dashboard_in_path_for_native_key(self, mixin):
        # native_filters_key present but no dashboard in path
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/other/?native_filters_key=keyX"
        )
        assert result["filter_type"] is None

    @pytest.mark.asyncio
    async def test_parse_dashboard_url_p_is_reserved_in_path(self, mixin):
        # URL has /p/ but not in dashboard context
        # The mixin will try extract_native_filters_from_permalink -> get_dashboard_permalink_state
        # which calls self.client.request. Set it to return an empty dict.
        mixin.client.request.return_value = {"result": {"state": {}}}
        result = await mixin.parse_dashboard_url_for_filters(
            "https://superset.example.com/superset/other/p/something/"
        )
        assert result["filter_type"] == "permalink"
# #endregion Test.DashboardsFiltersMixin
