# #region profile_service [C:5] [TYPE Module] [SEMANTICS sqlalchemy, profile, preference, superset, lookup]
#
# @BRIEF Composite facade orchestrating profile preference persistence, Superset account lookup,
#        security badges, and deterministic actor matching by delegating to focused sub-services.
# @LAYER     Domain
# @RELATION DEPENDS_ON -> [UserDashboardPreference]
# @RELATION DEPENDS_ON -> [ProfilePreferenceResponse]
# @RELATION DEPENDS_ON -> [SupersetClient]
# @RELATION DEPENDS_ON -> [AuthRepositoryModule]
# @RELATION DEPENDS_ON -> [User]
# @RELATION DEPENDS_ON -> [EXT:Library:sqlalchemy.orm.Session]
# @RELATION DEPENDS_ON -> [ProfilePreferenceService]
# @RELATION DEPENDS_ON -> [SupersetLookupService]
# @RELATION DEPENDS_ON -> [SecurityBadgeService]
# @RELATION DEPENDS_ON -> [ProfileUtils]
#
# @INVARIANT  Profile ID needs to be unique per-user session
#
# @TEST_CONTRACT  ProfilePreferenceUpdateRequest -> ProfilePreferenceResponse
# @TEST_FIXTURE  valid_profile_update -> {"user_id":"u-1","superset_username":"John_Doe","show_only_my_dashboards":true}
# @TEST_EDGE  enable_without_username -> toggle=true with empty username returns validation error
# @TEST_EDGE  cross_user_mutation -> attempt to update another user preference returns forbidden
# @TEST_EDGE  lookup_env_not_found -> unknown environment_id returns not found
# @TEST_INVARIANT  normalization_consistency -> VERIFIED_BY: [valid_profile_update, enable_without_username]
# @DATA_CONTRACT  Profile_id -> ProfileInfo; session_id -> valid UUID
# @PRE  Session is active and valid
# @POST  Profile with updated fields populated and
# @SIDE_EFFECT  Database read/write operations
# @RATIONALE Decomposed from monolithic 770-line ProfileService into three focused services
#            (ProfilePreferenceService, SupersetLookupService, SecurityBadgeService) plus pure
#            utility functions (ProfileUtils) to satisfy INV_7. This module is a thin facade
#            that preserves the public API contract for all existing callers.
# @REJECTED Keeping all three domains in a single ProfileService class was rejected — it violated
#            INV_7 (770 lines vs 150 max), mixed I/O patterns (DB writes + HTTP calls), and created
#            unnecessary coupling between preference persistence and Superset network operations.

from collections.abc import Iterable
from typing import Any

from sqlalchemy.orm import Session

from ..models.auth import User
from ..schemas.profile import (
    ProfilePreferenceResponse,
    ProfilePreferenceUpdateRequest,
    SupersetAccountLookupRequest,
    SupersetAccountLookupResponse,
)
from .profile_preference_service import ProfilePreferenceService
from .profile_utils import (
    EnvironmentNotFoundError,
    ProfileAuthorizationError,
    ProfileValidationError,
    normalize_owner_tokens,
    normalize_username,
)
from .security_badge_service import SecurityBadgeService
from .superset_lookup_service import SupersetLookupService

# Re-export exception classes for backward-compatible imports
__all__ = [
    "EnvironmentNotFoundError",
    "ProfileAuthorizationError",
    "ProfilePreferenceService",
    "ProfileService",
    "ProfileValidationError",
    "SecurityBadgeService",
    "SupersetLookupService",
]


# #region ProfileService [C:4] [TYPE Class] [SEMANTICS profile,facade,delegate,coordinator]
# @BRIEF Facade that composes ProfilePreferenceService, SupersetLookupService, and
#        SecurityBadgeService into a single interface for backward compatibility.
# @RELATION DEPENDS_ON -> [ProfilePreferenceService]
# @RELATION DEPENDS_ON -> [SupersetLookupService]
# @RELATION DEPENDS_ON -> [SecurityBadgeService]
# @RELATION DEPENDS_ON -> [ProfileUtils]
# @PRE  Caller provides authenticated User context for external service methods.
# @POST  Delegates to sub-services and returns normalized profile/lookup responses.
# @SIDE_EFFECT  Writes preference records and encrypted tokens; performs external account lookups when requested.
# @DATA_CONTRACT  Input[User,ProfilePreferenceUpdateRequest|SupersetAccountLookupRequest] -> Output[ProfilePreferenceResponse|SupersetAccountLookupResponse|bool]
# @INVARIANT  Profile data integrity maintained, cache consistency with database state
# @RATIONALE Thin coordinator — all sub-10-line delegating methods. Actual business logic lives
#            in the injected sub-services.
class ProfileService:
    """Facade composing preference, lookup, and security services."""

    # region init [TYPE Function]
    # @BRIEF Initialize facade and create sub-services.
    # @PRE  db session is active and config_manager supports get_environments().
    # @POST  All sub-services are initialized and ready.
    def __init__(self, db: Session, config_manager: Any, plugin_loader: Any = None):
        self.preference_service = ProfilePreferenceService(db, config_manager, plugin_loader)
        self.lookup_service = SupersetLookupService(config_manager)
        self.security_badge_service = SecurityBadgeService(plugin_loader)
        self.db = db
        self.config_manager = config_manager
        self.plugin_loader = plugin_loader

    # endregion init

    # region get_my_preference [TYPE Function]
    # @BRIEF Delegate to ProfilePreferenceService.
    def get_my_preference(self, current_user: User) -> ProfilePreferenceResponse:
        return self.preference_service.get_my_preference(current_user)

    # endregion get_my_preference

    # region get_dashboard_filter_binding [TYPE Function]
    # @BRIEF Delegate to ProfilePreferenceService.
    def get_dashboard_filter_binding(self, current_user: User) -> dict:
        return self.preference_service.get_dashboard_filter_binding(current_user)

    # endregion get_dashboard_filter_binding

    # region update_my_preference [TYPE Function]
    # @BRIEF Delegate to ProfilePreferenceService.
    def update_my_preference(
        self,
        current_user: User,
        payload: ProfilePreferenceUpdateRequest,
        target_user_id: str | None = None,
    ) -> ProfilePreferenceResponse:
        return self.preference_service.update_my_preference(
            current_user, payload, target_user_id
        )

    # endregion update_my_preference

    # region lookup_superset_accounts [TYPE Function]
    # @BRIEF Delegate to SupersetLookupService.
    def lookup_superset_accounts(
        self,
        current_user: User,
        request: SupersetAccountLookupRequest,
    ) -> SupersetAccountLookupResponse:
        return self.lookup_service.lookup_superset_accounts(current_user, request)

    # endregion lookup_superset_accounts

    # region matches_dashboard_actor [TYPE Function]
    # @BRIEF Apply trim+case-insensitive actor match across owners OR modified_by.
    # @PRE  bound_username can be empty; owners may contain mixed payload.
    # @POST  Returns True when normalized username matches owners or modified_by.
    def matches_dashboard_actor(
        self,
        bound_username: str | None,
        owners: Iterable[Any] | None,
        modified_by: str | None,
    ) -> bool:
        normalized_actor = normalize_username(bound_username)
        if not normalized_actor:
            return False

        owner_tokens = normalize_owner_tokens(owners)
        modified_token = normalize_username(modified_by)

        if normalized_actor in owner_tokens:
            return True
        if modified_token and normalized_actor == modified_token:
            return True
        return False

    # endregion matches_dashboard_actor


# #endregion ProfileService

# #endregion profile_service
