# #region Test.Core.SupersetCompilationAdapter.Edge [C:4] [TYPE Module] [SEMANTICS test, compilation, adapter, preview, edge]
# @BRIEF Tests for SupersetCompilationAdapter edge cases.
# @RELATION BINDS_TO -> [SupersetCompilationAdapter]

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

from dataclasses import dataclass
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from src.core.config_models import Environment
from src.core.utils.superset_compilation_adapter import (
    SupersetCompilationAdapter,
    PreviewCompilationPayload,
    SqlLabLaunchPayload,
)
from src.models.dataset_review import CompiledPreview, PreviewStatus


@pytest.fixture
def env():
    return MagicMock(spec=Environment)


@pytest.fixture
def adapter(env):
    return SupersetCompilationAdapter(env)


@pytest.fixture
def preview_payload():
    return PreviewCompilationPayload(
        session_id="s1",
        dataset_id=5,
        preview_fingerprint="fp1",
        template_params={"key": "val"},
        effective_filters=[{"col": "status", "op": "==", "val": "active"}],
    )


class TestSupportsClientMethod:
    def test_method_in_instance_dict(self, adapter):
        adapter.client.__dict__["test_method"] = lambda: None
        assert adapter._supports_client_method("test_method") is True

    def test_method_on_class(self, adapter):
        assert adapter._supports_client_method("get_dataset") is True  # exists on SupersetClient

    def test_nonexistent_method(self, adapter):
        assert adapter._supports_client_method("nonexistent") is False


class TestCompilePreview:
    @pytest.mark.asyncio
    async def test_invalid_dataset_id(self, adapter, preview_payload):
        preview_payload = PreviewCompilationPayload(
            session_id="s1", dataset_id=0, preview_fingerprint="fp1",
            template_params={}, effective_filters=[],
        )
        with pytest.raises(ValueError, match="positive integer"):
            await adapter.compile_preview(preview_payload)

    @pytest.mark.asyncio
    async def test_empty_compiled_sql(self, adapter, preview_payload):
        with patch.object(adapter, '_request_superset_preview', return_value={"compiled_sql": ""}):
            result = await adapter.compile_preview(preview_payload)
            assert result.preview_status == PreviewStatus.FAILED
            assert result.error_code == "superset_preview_empty"

    @pytest.mark.asyncio
    async def test_request_exception_returns_failed(self, adapter, preview_payload):
        with patch.object(adapter, '_request_superset_preview', side_effect=RuntimeError("API error")):
            result = await adapter.compile_preview(preview_payload)
            assert result.preview_status == PreviewStatus.FAILED
            assert result.error_code == "superset_preview_failed"

    @pytest.mark.asyncio
    async def test_successful_preview(self, adapter, preview_payload):
        with patch.object(adapter, '_request_superset_preview', return_value={"compiled_sql": "SELECT 1"}):
            result = await adapter.compile_preview(preview_payload)
            assert result.preview_status == PreviewStatus.READY
            assert result.compiled_sql == "SELECT 1"


class TestMarkPreviewStale:
    def test_marks_stale(self, adapter):
        preview = CompiledPreview(
            session_id="s1", preview_status=PreviewStatus.READY,
            compiled_sql="SELECT 1", preview_fingerprint="fp1",
        )
        result = adapter.mark_preview_stale(preview)
        assert result.preview_status == PreviewStatus.STABLE


class TestCreateSqlLabSession:
    @pytest.mark.asyncio
    async def test_empty_compiled_sql(self, adapter):
        payload = SqlLabLaunchPayload(
            session_id="s1", dataset_id=5, preview_id="p1",
            compiled_sql="", template_params={},
        )
        with pytest.raises(ValueError, match="compiled_sql must be non-empty"):
            await adapter.create_sql_lab_session(payload)

    @pytest.mark.asyncio
    async def test_no_session_ref(self, adapter):
        payload = SqlLabLaunchPayload(
            session_id="s1", dataset_id=5, preview_id="p1",
            compiled_sql="SELECT 1", template_params={},
        )
        with patch.object(adapter, '_request_sql_lab_session', return_value={}):
            with pytest.raises(RuntimeError, match="session reference"):
                await adapter.create_sql_lab_session(payload)

    @pytest.mark.asyncio
    async def test_session_ref_from_query_id(self, adapter):
        payload = SqlLabLaunchPayload(
            session_id="s1", dataset_id=5, preview_id="p1",
            compiled_sql="SELECT 1", template_params={},
        )
        with patch.object(adapter, '_request_sql_lab_session', return_value={"query_id": "q-42"}):
            result = await adapter.create_sql_lab_session(payload)
            assert result == "q-42"

    @pytest.mark.asyncio
    async def test_session_ref_from_result_id(self, adapter):
        payload = SqlLabLaunchPayload(
            session_id="s1", dataset_id=5, preview_id="p1",
            compiled_sql="SELECT 1", template_params={},
        )
        with patch.object(adapter, '_request_sql_lab_session', return_value={"result": {"id": "tab-1"}}):
            result = await adapter.create_sql_lab_session(payload)
            assert result == "tab-1"


class TestNormalizePreviewResponse:
    def test_not_dict_returns_none(self, adapter):
        assert adapter._normalize_preview_response("string") is None
        assert adapter._normalize_preview_response(42) is None
        assert adapter._normalize_preview_response(None) is None

    def test_empty_dict(self, adapter):
        assert adapter._normalize_preview_response({}) is None

    def test_compiled_sql_field(self, adapter):
        result = adapter._normalize_preview_response({"compiled_sql": "SELECT 1"})
        assert result is not None
        assert result["compiled_sql"] == "SELECT 1"

    def test_sql_field(self, adapter):
        result = adapter._normalize_preview_response({"sql": "SELECT 2"})
        assert result is not None
        assert result["compiled_sql"] == "SELECT 2"

    def test_result_payload_nested(self, adapter):
        result = adapter._normalize_preview_response({"result": {"sql": "SELECT 3"}})
        assert result is not None
        assert result["compiled_sql"] == "SELECT 3"

    def test_empty_string_candidates(self, adapter):
        result = adapter._normalize_preview_response({
            "compiled_sql": "",
            "sql": "",
            "result": {"compiled_sql": ""},
        })
        assert result is None


class TestDumpJson:
    def test_sorts_keys(self, adapter):
        result = adapter._dump_json({"z": 1, "a": 2})
        import json
        parsed = json.loads(result)
        assert list(parsed.keys()) == ["a", "z"]

    def test_with_non_serializable(self, adapter):
        from datetime import datetime
        result = adapter._dump_json({"dt": datetime(2026, 6, 15)})
        import json
        parsed = json.loads(result)
        assert "2026" in str(parsed["dt"])
# #endregion Test.Core.SupersetCompilationAdapter.Edge
