# #region SupersetProfileLookup [C:5] [TYPE Module] [SEMANTICS superset, profile, account, lookup, adapter]
#
# @BRIEF Provides environment-scoped Superset account lookup adapter with stable normalized output.
# @LAYER Service
# @RELATION DEPENDS_ON -> [APIClient]
# @RELATION DEPENDS_ON -> [SupersetAPIError]
# @RELATION DEPENDS_ON -> [SupersetAccountLookupAdapter]
#
# @INVARIANT  Adapter never leaks raw upstream payload shape to API consumers.
# @SIDE_EFFECT  Makes HTTP requests to Superset
# @DATA_CONTRACT  ProfileQuery -> SupersetProfile
import json
from typing import Any

from .logger import belief_scope, logger
from .utils.network import APIClient, AuthenticationError, SupersetAPIError


# #region SupersetAccountLookupAdapter [C:3] [TYPE Class]
# @BRIEF Lookup Superset users and normalize candidates for profile binding.
# @RELATION DEPENDS_ON -> [APIClient]
# @RELATION DEPENDS_ON -> [SupersetProfileLookup]
class SupersetAccountLookupAdapter:
    # #region __init__ [TYPE Function]
    # @PURPOSE: Initializes lookup adapter with authenticated API client and environment context.
    # @PRE  network_client supports request(method, endpoint, params=...).
    # @POST  Adapter is ready to perform users lookup requests.
    def __init__(self, network_client: APIClient, environment_id: str):
        self.network_client = network_client
        self.environment_id = str(environment_id or "")
    # #endregion __init__
    # #region get_users_page [TYPE Function]
    # @PURPOSE: Fetch one users page from Superset with passthrough search/sort parameters.
    # @PRE  page_index >= 0 and page_size >= 1.
    # @POST  Returns deterministic payload with normalized items and total count.
    # @RETURN  Dict[str, Any]
    def get_users_page(
        self,
        search: str | None = None,
        page_index: int = 0,
        page_size: int = 20,
        sort_column: str = "username",
        sort_order: str = "desc",
    ) -> dict[str, Any]:
        with belief_scope("SupersetAccountLookupAdapter.get_users_page"):
            normalized_page_index = max(int(page_index), 0)
            normalized_page_size = max(int(page_size), 1)
            normalized_sort_column = str(sort_column or "username").strip().lower() or "username"
            normalized_sort_order = str(sort_order or "desc").strip().lower()
            if normalized_sort_order not in {"asc", "desc"}:
                normalized_sort_order = "desc"
            query: dict[str, Any] = {
                "page": normalized_page_index,
                "page_size": normalized_page_size,
                "order_column": normalized_sort_column,
                "order_direction": normalized_sort_order,
            }
            normalized_search = str(search or "").strip()
            if normalized_search:
                query["filters"] = [{"col": "username", "opr": "ct", "value": normalized_search}]
            logger.reason(
                "Lookup Superset users",
                extra={"src": "SupersetAccountLookupAdapter.get_users_page", "payload": {"env": self.environment_id, "page": normalized_page_index, "page_size": normalized_page_size}},
            )
            logger.reflect(
                "Prepared Superset users lookup query",
                extra={"src": "SupersetAccountLookupAdapter.get_users_page", "payload": {"env": self.environment_id, "order_column": normalized_sort_column, "sort_order": normalized_sort_order, "direction": query.get("order_direction")}},
            )
            primary_error: Exception | None = None
            last_error: Exception | None = None
            for attempt_index, endpoint in enumerate(("/security/users/", "/security/users"), start=1):
                try:
                    logger.reason(
                        "Users lookup request attempt",
                        extra={"src": "SupersetAccountLookupAdapter.get_users_page", "payload": {"env": self.environment_id, "attempt": attempt_index, "endpoint": endpoint}},
                    )
                    response = self.network_client.request(
                        method="GET",
                        endpoint=endpoint,
                        params={"q": json.dumps(query)},
                    )
                    logger.reflect(
                        "Users lookup endpoint succeeded",
                        extra={"src": "SupersetAccountLookupAdapter.get_users_page", "payload": {"env": self.environment_id, "attempt": attempt_index, "endpoint": endpoint}},
                    )
                    return self._normalize_lookup_payload(
                        response=response,
                        page_index=normalized_page_index,
                        page_size=normalized_page_size,
                    )
                except Exception as exc:
                    if primary_error is None:
                        primary_error = exc
                    last_error = exc
                    cause = getattr(exc, "__cause__", None)
                    cause_response = getattr(cause, "response", None)
                    status_code = getattr(cause_response, "status_code", None)
                    logger.explore(
                        "Users lookup endpoint failed",
                        extra={"src": "SupersetAccountLookupAdapter.get_users_page", "payload": {"env": self.environment_id, "attempt": attempt_index, "endpoint": endpoint, "error_type": type(exc).__name__, "status_code": status_code, "direction": query.get("order_direction")}, "error": str(exc)},
                    )
            if last_error is not None:
                selected_error: Exception = last_error
                if (
                    primary_error is not None
                    and primary_error is not last_error
                    and isinstance(last_error, AuthenticationError)
                    and not isinstance(primary_error, AuthenticationError)
                ):
                    selected_error = primary_error
                    logger.reflect(
                        "Preserving primary lookup failure over fallback auth error",
                        extra={"src": "SupersetAccountLookupAdapter.get_users_page", "payload": {"env": self.environment_id, "primary_error_type": type(primary_error).__name__, "fallback_error_type": type(last_error).__name__}},
                    )
                logger.explore(
                    "All Superset users lookup endpoints failed",
                    extra={"src": "SupersetAccountLookupAdapter.get_users_page", "payload": {"env": self.environment_id, "direction": query.get("order_direction"), "selected_error_type": type(selected_error).__name__}},
                )
                raise selected_error
            raise SupersetAPIError("Superset users lookup failed without explicit error")
    # #endregion get_users_page
    # #region _normalize_lookup_payload [TYPE Function]
    # @PURPOSE: Convert Superset users response variants into stable candidates payload.
    # @PRE  response can be dict/list in any supported upstream shape.
    # @POST  Output contains canonical keys: status, environment_id, page_index, page_size, total, items.
    # @RETURN  Dict[str, Any]
    def _normalize_lookup_payload(
        self,
        response: Any,
        page_index: int,
        page_size: int,
    ) -> dict[str, Any]:
        with belief_scope("SupersetAccountLookupAdapter._normalize_lookup_payload"):
            payload = response
            if isinstance(payload, dict) and isinstance(payload.get("result"), dict):
                payload = payload.get("result")
            raw_items: list[Any] = []
            total = 0
            if isinstance(payload, dict):
                if isinstance(payload.get("result"), list):
                    raw_items = payload.get("result") or []
                    total = int(payload.get("count", len(raw_items)) or 0)
                elif isinstance(payload.get("users"), list):
                    raw_items = payload.get("users") or []
                    total = int(payload.get("total", len(raw_items)) or 0)
                elif isinstance(payload.get("items"), list):
                    raw_items = payload.get("items") or []
                    total = int(payload.get("total", len(raw_items)) or 0)
            elif isinstance(payload, list):
                raw_items = payload
                total = len(raw_items)
            normalized_items: list[dict[str, Any]] = []
            seen_usernames = set()
            for raw_user in raw_items:
                candidate = self.normalize_user_payload(raw_user)
                username_key = str(candidate.get("username") or "").strip().lower()
                if not username_key:
                    continue
                if username_key in seen_usernames:
                    continue
                seen_usernames.add(username_key)
                normalized_items.append(candidate)
            logger.reflect(
                "Normalized lookup payload",
                extra={"src": "SupersetAccountLookupAdapter._normalize_lookup_payload", "payload": {"env": self.environment_id, "items": len(normalized_items), "total": max(total, len(normalized_items))}},
            )
            return {
                "status": "success",
                "environment_id": self.environment_id,
                "page_index": max(int(page_index), 0),
                "page_size": max(int(page_size), 1),
                "total": max(int(total), len(normalized_items)),
                "items": normalized_items,
            }
    # #endregion _normalize_lookup_payload
    # #region normalize_user_payload [TYPE Function]
    # @PURPOSE: Project raw Superset user object to canonical candidate shape.
    # @PRE  raw_user may have heterogenous key names between Superset versions.
    # @POST  Returns normalized candidate keys (environment_id, username, display_name, email, is_active).
    # @RETURN  Dict[str, Any]
    def normalize_user_payload(self, raw_user: Any) -> dict[str, Any]:
        if not isinstance(raw_user, dict):
            raw_user = {}
        username = str(
            raw_user.get("username")
            or raw_user.get("userName")
            or raw_user.get("name")
            or ""
        ).strip()
        full_name = str(raw_user.get("full_name") or "").strip()
        first_name = str(raw_user.get("first_name") or "").strip()
        last_name = str(raw_user.get("last_name") or "").strip()
        display_name = full_name or " ".join(
            part for part in [first_name, last_name] if part
        ).strip()
        if not display_name:
            display_name = username or None
        email = str(raw_user.get("email") or "").strip() or None
        is_active_raw = raw_user.get("is_active")
        is_active = bool(is_active_raw) if is_active_raw is not None else None
        return {
            "environment_id": self.environment_id,
            "username": username,
            "display_name": display_name,
            "email": email,
            "is_active": is_active,
        }
    # #endregion normalize_user_payload
# #endregion SupersetAccountLookupAdapter
# #endregion SupersetProfileLookup
