# #region Test.ProfileService [C:3] [TYPE Module] [SEMANTICS test,profile,facade,delegate]
# @BRIEF Tests for services/profile_service.py — ProfileService facade that delegates to
#        ProfilePreferenceService, SupersetLookupService, and SecurityBadgeService.
# @RELATION BINDS_TO -> [ProfileService]
# @TEST_EDGE: missing_field -> empty/none username returns False from matches_dashboard_actor
# @TEST_EDGE: invalid_type -> mixed owners payload handled correctly
# @TEST_EDGE: delegation -> facade methods call sub-services and return their results

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

from src.schemas.profile import ProfilePreferenceUpdateRequest, SupersetAccountLookupRequest


class _MockUser:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)


class TestProfileServiceInit:
    """ProfileService __init__ — creates sub-services."""

    @patch("src.services.profile_service.ProfilePreferenceService")
    @patch("src.services.profile_service.SupersetLookupService")
    @patch("src.services.profile_service.SecurityBadgeService")
    def test_init_creates_sub_services(self, MockBadge, MockLookup, MockPref):
        from src.services.profile_service import ProfileService

        db = MagicMock()
        config = MagicMock()
        loader = MagicMock()
        svc = ProfileService(db, config, loader)

        MockPref.assert_called_once_with(db, config, loader)
        MockLookup.assert_called_once_with(config)
        MockBadge.assert_called_once_with(loader)
        assert svc.db is db
        assert svc.config_manager is config
        assert svc.plugin_loader is loader

    @patch("src.services.profile_service.ProfilePreferenceService")
    @patch("src.services.profile_service.SupersetLookupService")
    @patch("src.services.profile_service.SecurityBadgeService")
    def test_init_without_plugin_loader(self, MockBadge, MockLookup, MockPref):
        from src.services.profile_service import ProfileService

        db = MagicMock()
        config = MagicMock()
        svc = ProfileService(db, config)

        MockBadge.assert_called_once_with(None)


class TestProfileServiceDelegation:
    """ProfileService delegates to sub-services."""

    @pytest.fixture
    def service(self):
        from src.services.profile_service import ProfileService
        svc = ProfileService.__new__(ProfileService)
        svc.preference_service = MagicMock()
        svc.lookup_service = AsyncMock()
        svc.security_badge_service = MagicMock()
        svc.db = MagicMock()
        svc.config_manager = MagicMock()
        svc.plugin_loader = MagicMock()
        return svc

    def test_get_my_preference_delegates(self, service):
        user = _MockUser(id="u-1")
        expected = MagicMock()
        service.preference_service.get_my_preference.return_value = expected

        result = service.get_my_preference(user)
        assert result is expected
        service.preference_service.get_my_preference.assert_called_once_with(user)

    def test_get_dashboard_filter_binding_delegates(self, service):
        user = _MockUser(id="u-1")
        expected = {"show_only_my_dashboards": True}
        service.preference_service.get_dashboard_filter_binding.return_value = expected

        result = service.get_dashboard_filter_binding(user)
        assert result == expected
        service.preference_service.get_dashboard_filter_binding.assert_called_once_with(user)

    def test_update_my_preference_delegates(self, service):
        user = _MockUser(id="u-1")
        payload = ProfilePreferenceUpdateRequest(superset_username="jdoe")
        expected = MagicMock()
        service.preference_service.update_my_preference.return_value = expected

        result = service.update_my_preference(user, payload)
        assert result is expected
        service.preference_service.update_my_preference.assert_called_once_with(user, payload, None)

    def test_update_my_preference_with_target_user(self, service):
        user = _MockUser(id="u-1")
        payload = ProfilePreferenceUpdateRequest(superset_username="jdoe")
        service.update_my_preference(user, payload, target_user_id="u-2")
        service.preference_service.update_my_preference.assert_called_once_with(user, payload, "u-2")

    @pytest.mark.asyncio
    async def test_lookup_superset_accounts_delegates(self, service):
        user = _MockUser(id="u-1")
        request = SupersetAccountLookupRequest(environment_id="env-1")
        expected = MagicMock()
        service.lookup_service.lookup_superset_accounts.return_value = expected

        result = await service.lookup_superset_accounts(user, request)
        assert result is expected
        service.lookup_service.lookup_superset_accounts.assert_called_once_with(user, request)


class TestMatchesDashboardActor:
    """ProfileService.matches_dashboard_actor — normalize + match logic."""

    @pytest.fixture
    def service(self):
        from src.services.profile_service import ProfileService
        return ProfileService.__new__(ProfileService)

    def test_none_username_returns_false(self, service):
        assert service.matches_dashboard_actor(None, [], None) is False

    def test_empty_username_returns_false(self, service):
        assert service.matches_dashboard_actor("", [], None) is False

    def test_matches_owner_username(self, service):
        owners = [{"username": "AdminUser"}]
        assert service.matches_dashboard_actor("adminuser", owners, None) is True

    def test_matches_modified_by(self, service):
        assert service.matches_dashboard_actor("admin", None, "Admin") is True

    def test_no_match_returns_false(self, service):
        owners = [{"username": "OtherUser"}]
        assert service.matches_dashboard_actor("admin", owners, "Other") is False

    def test_matches_with_case_insensitive(self, service):
        owners = [{"username": "John.Doe"}]
        assert service.matches_dashboard_actor("john.doe", owners, None) is True

    def test_owner_tokens_with_full_name(self, service):
        owners = [{"first_name": "John", "last_name": "Doe"}]
        assert service.matches_dashboard_actor("john_doe", owners, None) is True

    def test_empty_owners_list(self, service):
        assert service.matches_dashboard_actor("admin", [], None) is False
# #endregion Test.ProfileService
