# #region Test.Superset.UserProjection [C:3] [TYPE Module] [SEMANTICS test,superset,user,projection]
# @BRIEF Tests for core/superset_client/_user_projection.py — SupersetUserProjectionMixin.
# @RELATION BINDS_TO -> [SupersetUserProjection]
# @TEST_EDGE: owners_payload_none -> []
# @TEST_EDGE: owner_single_dict_wrapped -> normalize as list

from pathlib import Path
import sys

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

from unittest.mock import MagicMock
import pytest


class TestSanitizeUserText:
    """Verify _sanitize_user_text."""

    @pytest.fixture
    def mixin(self):
        from src.core.superset_client._user_projection import SupersetUserProjectionMixin

        return SupersetUserProjectionMixin()

    def test_none_returns_none(self, mixin):
        assert mixin._sanitize_user_text(None) is None

    def test_empty_string_returns_none(self, mixin):
        assert mixin._sanitize_user_text("") is None

    def test_whitespace_string_returns_none(self, mixin):
        assert mixin._sanitize_user_text("   ") is None

    def test_valid_string_stripped(self, mixin):
        assert mixin._sanitize_user_text("  John  ") == "John"

    def test_int_converted_to_string(self, mixin):
        assert mixin._sanitize_user_text(123) == "123"


class TestExtractUserDisplay:
    """Verify _extract_user_display."""

    @pytest.fixture
    def mixin(self):
        from src.core.superset_client._user_projection import SupersetUserProjectionMixin

        return SupersetUserProjectionMixin()

    def test_preferred_value_used(self, mixin):
        result = mixin._extract_user_display("Preferred", {"full_name": "Ignored"})
        assert result == "Preferred"

    def test_preferred_value_whitespace_skipped(self, mixin):
        result = mixin._extract_user_display("  ", {"full_name": "John Doe"})
        assert result == "John Doe"

    def test_full_name_used(self, mixin):
        result = mixin._extract_user_display(None, {"full_name": "John Doe"})
        assert result == "John Doe"

    def test_first_name_last_name_combined(self, mixin):
        result = mixin._extract_user_display(None, {"first_name": "John", "last_name": "Doe"})
        assert result == "John Doe"

    def test_first_name_only(self, mixin):
        result = mixin._extract_user_display(None, {"first_name": "John"})
        assert result == "John"

    def test_last_name_only(self, mixin):
        result = mixin._extract_user_display(None, {"last_name": "Doe"})
        assert result == "Doe"

    def test_username_fallback(self, mixin):
        result = mixin._extract_user_display(None, {"username": "johndoe"})
        assert result == "johndoe"

    def test_email_fallback(self, mixin):
        result = mixin._extract_user_display(None, {"email": "john@example.com"})
        assert result == "john@example.com"

    def test_none_payload_returns_none(self, mixin):
        result = mixin._extract_user_display(None, None)
        assert result is None

    def test_empty_dict_returns_none(self, mixin):
        result = mixin._extract_user_display(None, {})
        assert result is None

    def test_non_dict_payload_returns_none(self, mixin):
        result = mixin._extract_user_display(None, "string")
        assert result is None


class TestExtractOwnerLabels:
    """Verify _extract_owner_labels."""

    @pytest.fixture
    def mixin(self):
        from src.core.superset_client._user_projection import SupersetUserProjectionMixin

        m = SupersetUserProjectionMixin()
        m._extract_user_display = MagicMock(return_value="John Doe")  # noqa: SIM300
        m._sanitize_user_text = MagicMock(side_effect=lambda x: str(x).strip() if x else None)
        return m

    def test_none_owners_returns_empty(self, mixin):
        result = mixin._extract_owner_labels(None)
        assert result == []

    def test_list_of_dicts(self, mixin):
        result = mixin._extract_owner_labels([{"id": 1}, {"id": 2}])
        assert len(result) >= 1  # extract_user_display returns "John Doe" for each

    def test_single_dict_wrapped_in_list(self, mixin):
        # When a single dict is passed, the code wraps it in a list
        mixin._extract_user_display = MagicMock(return_value="Single Owner")
        result = mixin._extract_owner_labels([{"id": 1}])
        assert "Single Owner" in result

    def test_deduplicates_labels(self, mixin):
        mixin._extract_user_display = MagicMock(return_value="Same Person")
        result = mixin._extract_owner_labels([{"id": 1}, {"id": 2}])
        assert result == ["Same Person"]  # deduplicated

    def test_non_list_wrapped_single(self, mixin):
        mixin._extract_user_display = MagicMock(return_value="Owner")
        # When owners_payload is not a list, it's wrapped in a list
        # But the code checks isinstance(owners_payload, list) first
        # To test the else branch, we need non-list, non-None input
        mixin._sanitize_user_text = MagicMock(side_effect=lambda x: x if isinstance(x, str) else None)
        mixin._extract_user_display = MagicMock(return_value=None)
        result = mixin._extract_owner_labels("string_value")
        # The else branch sanitizes the scalar
        assert "string_value" in result

    def test_extract_user_display_none_not_added(self, mixin):
        mixin._extract_user_display = MagicMock(return_value=None)
        mixin._sanitize_user_text = MagicMock(return_value=None)
        result = mixin._extract_owner_labels([{"id": 1}])
        assert result == []


class TestSanitizeUserTextDirect:
    """Direct tests for _sanitize_user_text with the real method."""

    @pytest.fixture
    def mixin(self):
        from src.core.superset_client._user_projection import SupersetUserProjectionMixin

        return SupersetUserProjectionMixin()

    def test_whitespace_only_returns_none(self, mixin):
        assert mixin._sanitize_user_text("   ") is None

    def test_leading_trailing_spaces_removed(self, mixin):
        assert mixin._sanitize_user_text("  hello  ") == "hello"
# #endregion Test.Superset.UserProjection
