# #region SupersetPreviewPipelineTests [TYPE Module] [C:3] [SEMANTICS tests, superset, preview, chart_data, network, 404-mapping]
# @BRIEF Verify explicit chart-data preview compilation and ensure non-dashboard 404 errors remain generic across sync and async clients.
# @LAYER Domain
# @RELATION BINDS_TO -> [AsyncNetworkModule]
import json
import pytest
from unittest.mock import MagicMock

import httpx
import requests

from src.core.config_models import Environment
from src.core.superset_client import SupersetClient
from src.core.utils.async_network import AsyncAPIClient
from src.core.utils.network import APIClient, DashboardNotFoundError, SupersetAPIError


# #region _make_environment [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
def _make_environment() -> Environment:
    return Environment(
        id="env-1",
        name="DEV",
        url="http://superset.local",
        username="demo",
        password="secret",
    )
# #endregion _make_environment
# #region _make_requests_http_error [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
def _make_requests_http_error(
    status_code: int, url: str
) -> requests.exceptions.HTTPError:
    response = requests.Response()
    response.status_code = status_code
    response.url = url
    response._content = b'{"message":"not found"}'
    request = requests.Request("GET", url).prepare()
    response.request = request
    return requests.exceptions.HTTPError(response=response, request=request)
# #endregion _make_requests_http_error
# #region _make_httpx_status_error [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
def _make_httpx_status_error(status_code: int, url: str) -> httpx.HTTPStatusError:
    request = httpx.Request("GET", url)
    response = httpx.Response(
        status_code=status_code, request=request, text='{"message":"not found"}'
    )
    return httpx.HTTPStatusError("upstream error", request=request, response=response)
# #endregion _make_httpx_status_error
# #region test_compile_dataset_preview_prefers_legacy_explore_form_data_strategy [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Superset preview compilation should prefer the legacy form_data transport inferred from browser traffic before falling back to chart-data.
def test_compile_dataset_preview_prefers_legacy_explore_form_data_strategy():
    client = SupersetClient(_make_environment())
    client.get_dataset = MagicMock(
        return_value={
            "result": {
                "id": 42,
                "schema": "public",
                "datasource": {"id": 42, "type": "table"},
                "result_format": "json",
                "result_type": "full",
            }
        }
    )
    client.network = MagicMock()
    client.network.request.return_value = {
        "result": {
            "query": "SELECT count(*) FROM public.sales WHERE country IN ('DE')",
        }
    }
    result = client.compile_dataset_preview(
        dataset_id=42,
        template_params={"country": "DE"},
        effective_filters=[{"filter_name": "country", "effective_value": ["DE"]}],
    )
    assert (
        result["compiled_sql"]
        == "SELECT count(*) FROM public.sales WHERE country IN ('DE')"
    )
    client.network.request.assert_called_once()
    request_call = client.network.request.call_args
    assert request_call.kwargs["method"] == "POST"
    assert request_call.kwargs["endpoint"] == "/explore_json/form_data"
    assert request_call.kwargs["params"] is not None
    assert request_call.kwargs["params"].keys() == {"form_data"}
    legacy_form_data = json.loads(request_call.kwargs["params"]["form_data"])
    assert "datasource" not in legacy_form_data
    assert legacy_form_data["datasource_id"] == 42
    assert legacy_form_data["datasource_type"] == "table"
    assert legacy_form_data["extra_filters"] == [
        {"col": "country", "op": "IN", "val": ["DE"]}
    ]
    assert legacy_form_data["extra_form_data"] == {
        "filters": [{"col": "country", "op": "IN", "val": ["DE"]}]
    }
    assert legacy_form_data["url_params"] == {"country": "DE"}
    assert legacy_form_data["result_type"] == "query"
    assert legacy_form_data["result_format"] == "json"
    assert legacy_form_data["force"] is True
    assert result["endpoint"] == "/explore_json/form_data"
    assert result["endpoint_kind"] == "legacy_explore_form_data"
    assert result["dataset_id"] == 42
    assert result["response_diagnostics"] == [
        {"source": "query", "has_query": False},
        {"source": "sql", "has_query": False},
        {"source": "compiled_sql", "has_query": False},
        {"source": "result.query", "has_query": True},
    ]
    assert result["legacy_form_data"]["extra_filters"] == [
        {"col": "country", "op": "IN", "val": ["DE"]}
    ]
    assert result["query_context"]["datasource"] == {"id": 42, "type": "table"}
    assert result["strategy_attempts"] == [
        {
            "endpoint": "/explore_json/form_data",
            "endpoint_kind": "legacy_explore_form_data",
            "request_transport": "query_param_form_data",
            "contains_root_datasource": False,
            "contains_form_datasource": False,
            "contains_query_object_datasource": False,
            "request_param_keys": ["form_data"],
            "request_payload_keys": [],
            "success": True,
        }
    ]
# #endregion test_compile_dataset_preview_prefers_legacy_explore_form_data_strategy
# #region test_compile_dataset_preview_falls_back_to_chart_data_after_legacy_failures [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Superset preview compilation should fall back to chart-data when legacy form_data strategies are rejected.
def test_compile_dataset_preview_falls_back_to_chart_data_after_legacy_failures():
    client = SupersetClient(_make_environment())
    client.get_dataset = MagicMock(
        return_value={
            "result": {
                "id": 42,
                "schema": "public",
                "datasource": {"id": 42, "type": "table"},
            }
        }
    )
    client.network = MagicMock()
    client.network.request.side_effect = [
        SupersetAPIError("legacy explore failed"),
        SupersetAPIError("legacy data failed"),
        {
            "result": [
                {
                    "query": "SELECT count(*) FROM public.sales",
                }
            ]
        },
    ]
    result = client.compile_dataset_preview(dataset_id=42)
    assert client.network.request.call_count == 3
    first_call = client.network.request.call_args_list[0]
    second_call = client.network.request.call_args_list[1]
    third_call = client.network.request.call_args_list[2]
    assert first_call.kwargs["endpoint"] == "/explore_json/form_data"
    assert second_call.kwargs["endpoint"] == "/data"
    assert third_call.kwargs["endpoint"] == "/chart/data"
    assert third_call.kwargs["headers"] == {"Content-Type": "application/json"}
    first_legacy_form_data = json.loads(first_call.kwargs["params"]["form_data"])
    second_legacy_form_data = json.loads(second_call.kwargs["params"]["form_data"])
    assert "datasource" not in first_legacy_form_data
    assert "datasource" not in second_legacy_form_data
    query_context = json.loads(third_call.kwargs["data"])
    assert query_context["datasource"] == {"id": 42, "type": "table"}
    assert result["endpoint"] == "/chart/data"
    assert result["endpoint_kind"] == "v1_chart_data"
    assert len(result["strategy_attempts"]) == 3
    assert result["strategy_attempts"][0]["endpoint"] == "/explore_json/form_data"
    assert result["strategy_attempts"][0]["endpoint_kind"] == "legacy_explore_form_data"
    assert (
        result["strategy_attempts"][0]["request_transport"] == "query_param_form_data"
    )
    assert result["strategy_attempts"][0]["contains_root_datasource"] is False
    assert result["strategy_attempts"][0]["contains_form_datasource"] is False
    assert result["strategy_attempts"][0]["contains_query_object_datasource"] is False
    assert result["strategy_attempts"][0]["request_param_keys"] == ["form_data"]
    assert result["strategy_attempts"][0]["request_payload_keys"] == []
    assert result["strategy_attempts"][0]["success"] is False
    assert "legacy explore failed" in result["strategy_attempts"][0]["error"]
    assert result["strategy_attempts"][1]["endpoint"] == "/data"
    assert result["strategy_attempts"][1]["endpoint_kind"] == "legacy_data_form_data"
    assert (
        result["strategy_attempts"][1]["request_transport"] == "query_param_form_data"
    )
    assert result["strategy_attempts"][1]["contains_root_datasource"] is False
    assert result["strategy_attempts"][1]["contains_form_datasource"] is False
    assert result["strategy_attempts"][1]["contains_query_object_datasource"] is False
    assert result["strategy_attempts"][1]["request_param_keys"] == ["form_data"]
    assert result["strategy_attempts"][1]["request_payload_keys"] == []
    assert result["strategy_attempts"][1]["success"] is False
    assert "legacy data failed" in result["strategy_attempts"][1]["error"]
    assert result["strategy_attempts"][2] == {
        "endpoint": "/chart/data",
        "endpoint_kind": "v1_chart_data",
        "request_transport": "json_body",
        "contains_root_datasource": True,
        "contains_form_datasource": False,
        "contains_query_object_datasource": False,
        "request_param_keys": [],
        "request_payload_keys": [
            "datasource",
            "force",
            "form_data",
            "queries",
            "result_format",
            "result_type",
        ],
        "success": True,
    }
# #endregion test_compile_dataset_preview_falls_back_to_chart_data_after_legacy_failures
# #region test_build_dataset_preview_query_context_places_recovered_filters_in_chart_style_form_data [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Preview query context should mirror chart-style filter transport so recovered native filters reach Superset compilation.
def test_build_dataset_preview_query_context_places_recovered_filters_in_chart_style_form_data():
    client = SupersetClient(_make_environment())
    query_context = client.build_dataset_preview_query_context(
        dataset_id=7,
        dataset_record={
            "id": 7,
            "schema": "public",
            "datasource": {"id": 7, "type": "table"},
            "default_time_range": "Last year",
        },
        template_params={"country": "DE"},
        effective_filters=[
            {
                "filter_name": "country",
                "display_name": "Country",
                "effective_value": ["DE"],
                "normalized_filter_payload": {
                    "filter_clauses": [
                        {"col": "country_code", "op": "IN", "val": ["DE"]}
                    ],
                    "extra_form_data": {
                        "filters": [{"col": "country_code", "op": "IN", "val": ["DE"]}]
                    },
                    "value_origin": "extra_form_data.filters",
                },
            },
            {"filter_name": "status", "effective_value": "active"},
        ],
    )
    assert query_context["force"] is True
    assert query_context["result_type"] == "query"
    assert query_context["datasource"] == {"id": 7, "type": "table"}
    assert "datasource" not in query_context["queries"][0]
    assert query_context["queries"][0]["result_type"] == "query"
    assert query_context["queries"][0]["filters"] == [
        {"col": "country_code", "op": "IN", "val": ["DE"]},
        {"col": "status", "op": "==", "val": "active"},
    ]
    assert query_context["form_data"]["datasource"] == "7__table"
    assert query_context["form_data"]["datasource_id"] == 7
    assert query_context["form_data"]["datasource_type"] == "table"
    assert query_context["form_data"]["extra_filters"] == [
        {"col": "country_code", "op": "IN", "val": ["DE"]},
        {"col": "status", "op": "==", "val": "active"},
    ]
    assert query_context["form_data"]["extra_form_data"] == {
        "filters": [
            {"col": "country_code", "op": "IN", "val": ["DE"]},
            {"col": "status", "op": "==", "val": "active"},
        ],
        "time_range": "Last year",
    }
    assert query_context["form_data"]["url_params"] == {"country": "DE"}
# #endregion test_build_dataset_preview_query_context_places_recovered_filters_in_chart_style_form_data
# #region test_build_dataset_preview_query_context_merges_dataset_template_params_and_preserves_user_values [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Preview query context should merge dataset template params for parity with real dataset definitions while preserving explicit session overrides.
def test_build_dataset_preview_query_context_merges_dataset_template_params_and_preserves_user_values():
    client = SupersetClient(_make_environment())
    query_context = client.build_dataset_preview_query_context(
        dataset_id=8,
        dataset_record={
            "id": 8,
            "schema": "public",
            "datasource": {"id": 8, "type": "table"},
            "template_params": json.dumps({"region": "EMEA", "country": "FR"}),
        },
        template_params={"country": "DE"},
        effective_filters=[],
    )
    assert query_context["queries"][0]["url_params"] == {
        "region": "EMEA",
        "country": "DE",
    }
    assert query_context["form_data"]["url_params"] == {
        "region": "EMEA",
        "country": "DE",
    }
# #endregion test_build_dataset_preview_query_context_merges_dataset_template_params_and_preserves_user_values
# #region test_build_dataset_preview_query_context_preserves_time_range_from_native_filter_payload [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Preview query context should preserve time-range native filter extras even when dataset defaults differ.
def test_build_dataset_preview_query_context_preserves_time_range_from_native_filter_payload():
    client = SupersetClient(_make_environment())
    query_context = client.build_dataset_preview_query_context(
        dataset_id=9,
        dataset_record={
            "id": 9,
            "schema": "public",
            "datasource": {"id": 9, "type": "table"},
            "default_time_range": "Last year",
        },
        template_params={},
        effective_filters=[
            {
                "filter_name": "Order Date",
                "display_name": "Order Date",
                "effective_value": "2020-01-01 : 2020-12-31",
                "normalized_filter_payload": {
                    "filter_clauses": [],
                    "extra_form_data": {"time_range": "2020-01-01 : 2020-12-31"},
                    "value_origin": "extra_form_data.time_range",
                },
            }
        ],
    )
    assert query_context["queries"][0]["time_range"] == "2020-01-01 : 2020-12-31"
    assert query_context["form_data"]["extra_form_data"] == {
        "time_range": "2020-01-01 : 2020-12-31"
    }
    assert query_context["queries"][0]["filters"] == []
# #endregion test_build_dataset_preview_query_context_preserves_time_range_from_native_filter_payload
# #region test_build_dataset_preview_legacy_form_data_preserves_native_filter_clauses [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Legacy preview form_data should preserve recovered native filter clauses in browser-style fields without duplicating datasource for QueryObjectFactory.
def test_build_dataset_preview_legacy_form_data_preserves_native_filter_clauses():
    client = SupersetClient(_make_environment())
    legacy_form_data = client.build_dataset_preview_legacy_form_data(
        dataset_id=11,
        dataset_record={
            "id": 11,
            "schema": "public",
            "datasource": {"id": 11, "type": "table"},
            "default_time_range": "No filter",
        },
        template_params={"country": "DE"},
        effective_filters=[
            {
                "filter_name": "Country",
                "display_name": "Country",
                "effective_value": ["DE", "FR"],
                "normalized_filter_payload": {
                    "filter_clauses": [
                        {"col": "country_code", "op": "IN", "val": ["DE", "FR"]}
                    ],
                    "extra_form_data": {
                        "filters": [
                            {"col": "country_code", "op": "IN", "val": ["DE", "FR"]}
                        ],
                        "time_range": "Last quarter",
                    },
                    "value_origin": "extra_form_data.filters",
                },
            }
        ],
    )
    assert "datasource" not in legacy_form_data
    assert legacy_form_data["datasource_id"] == 11
    assert legacy_form_data["datasource_type"] == "table"
    assert legacy_form_data["extra_filters"] == [
        {"col": "country_code", "op": "IN", "val": ["DE", "FR"]}
    ]
    assert legacy_form_data["extra_form_data"] == {
        "filters": [{"col": "country_code", "op": "IN", "val": ["DE", "FR"]}],
        "time_range": "Last quarter",
    }
    assert legacy_form_data["time_range"] == "Last quarter"
    assert legacy_form_data["url_params"] == {"country": "DE"}
    assert legacy_form_data["result_type"] == "query"
# #endregion test_build_dataset_preview_legacy_form_data_preserves_native_filter_clauses
# #region test_sync_network_404_mapping_keeps_non_dashboard_endpoints_generic [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Sync network client should reserve dashboard-not-found translation for dashboard endpoints only.
def test_sync_network_404_mapping_keeps_non_dashboard_endpoints_generic():
    client = APIClient(
        config={
            "base_url": "http://superset.local",
            "auth": {"username": "demo", "password": "secret"},
        }
    )
    with pytest.raises(SupersetAPIError) as exc_info:
        client._handle_http_error(
            _make_requests_http_error(404, "http://superset.local/api/v1/chart/data"),
            "/chart/data",
        )
    assert not isinstance(exc_info.value, DashboardNotFoundError)
    assert "API resource not found at endpoint '/chart/data'" in str(exc_info.value)
# #endregion test_sync_network_404_mapping_keeps_non_dashboard_endpoints_generic
# #region test_sync_network_404_mapping_translates_dashboard_endpoints [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Sync network client should still translate dashboard endpoint 404 responses into dashboard-not-found errors.
def test_sync_network_404_mapping_translates_dashboard_endpoints():
    client = APIClient(
        config={
            "base_url": "http://superset.local",
            "auth": {"username": "demo", "password": "secret"},
        }
    )
    with pytest.raises(DashboardNotFoundError) as exc_info:
        client._handle_http_error(
            _make_requests_http_error(404, "http://superset.local/api/v1/dashboard/10"),
            "/dashboard/10",
        )
    assert "Dashboard '/dashboard/10' Dashboard not found" in str(exc_info.value)
# #endregion test_sync_network_404_mapping_translates_dashboard_endpoints
# #region test_async_network_404_mapping_keeps_non_dashboard_endpoints_generic [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Async network client should reserve dashboard-not-found translation for dashboard endpoints only.
@pytest.mark.asyncio
async def test_async_network_404_mapping_keeps_non_dashboard_endpoints_generic():
    client = AsyncAPIClient(
        config={
            "base_url": "http://superset.local",
            "auth": {"username": "demo", "password": "secret"},
        }
    )
    try:
        with pytest.raises(SupersetAPIError) as exc_info:
            client._handle_http_error(
                _make_httpx_status_error(
                    404, "http://superset.local/api/v1/chart/data"
                ),
                "/chart/data",
            )
        assert not isinstance(exc_info.value, DashboardNotFoundError)
        assert "API resource not found at endpoint '/chart/data'" in str(exc_info.value)
    finally:
        await client.aclose()
# #endregion test_async_network_404_mapping_keeps_non_dashboard_endpoints_generic
# #region test_async_network_404_mapping_translates_dashboard_endpoints [TYPE Function]
# @RELATION BINDS_TO -> SupersetPreviewPipelineTests
# @BRIEF Async network client should still translate dashboard endpoint 404 responses into dashboard-not-found errors.
@pytest.mark.asyncio
async def test_async_network_404_mapping_translates_dashboard_endpoints():
    client = AsyncAPIClient(
        config={
            "base_url": "http://superset.local",
            "auth": {"username": "demo", "password": "secret"},
        }
    )
    try:
        with pytest.raises(DashboardNotFoundError) as exc_info:
            client._handle_http_error(
                _make_httpx_status_error(
                    404, "http://superset.local/api/v1/dashboard/10"
                ),
                "/dashboard/10",
            )
        assert "Dashboard '/dashboard/10' Dashboard not found" in str(exc_info.value)
    finally:
        await client.aclose()
# #endregion test_async_network_404_mapping_translates_dashboard_endpoints
# #endregion SupersetPreviewPipelineTests
