# #region SupersetContextExtractorBase [C:4] [TYPE Module] [SEMANTICS superset, context, extract, base, recovery]
# @LAYER Infrastructure
# @BRIEF Base class and helpers for recovering dataset/dashboard context from Superset links with explicit partial-recovery markers.
# @RELATION DEPENDS_ON -> [ImportedFilter]
# @RELATION DEPENDS_ON -> [TemplateVariable]
# @RELATION CALLS -> [SupersetClient]
# @PRE  Superset link or dataset reference must be parseable enough to resolve an environment-scoped target resource.
# @POST  Returns the best available recovered context with explicit provenance and partial-recovery markers when necessary.
# @SIDE_EFFECT  Performs upstream Superset API reads.
# @INVARIANT  Partial recovery is surfaced explicitly and never misrepresented as fully confirmed context.
# #endregion SupersetContextExtractorBase
# #region _base_imports [TYPE Block]
from __future__ import annotations

from dataclasses import dataclass, field
import json
from typing import Any, cast
from urllib.parse import unquote

from ...config_models import Environment
from ...logger import logger
from ...superset_client import SupersetClient

# #endregion _base_imports
logger = cast(Any, logger)
# #region SupersetParsedContext [C:2] [TYPE Class]
# @BRIEF Normalized output of Superset link parsing for session intake and recovery.
@dataclass
class SupersetParsedContext:
    source_url: str
    dataset_ref: str
    dataset_id: int | None = None
    dashboard_id: int | None = None
    chart_id: int | None = None
    resource_type: str = "unknown"
    query_state: dict[str, Any] = field(default_factory=dict)
    imported_filters: list[dict[str, Any]] = field(default_factory=list)
    unresolved_references: list[str] = field(default_factory=list)
    partial_recovery: bool = False
    dataset_payload: dict[str, Any] | None = None
# #endregion SupersetParsedContext
# #region SupersetContextExtractorBase [C:4] [TYPE Class]
# @BRIEF Bind extractor to one Superset environment and client instance; provide shared URL-parsing helpers.
# @RELATION DEPENDS_ON -> [Environment]
# @PRE  constructor receives a configured environment with a usable Superset base URL.
# @POST  extractor instance is ready to parse links against one Superset environment.
# @SIDE_EFFECT  downstream parse operations may call Superset APIs through SupersetClient.
class SupersetContextExtractorBase:
    # #region SupersetContextExtractorBase.__init__ [TYPE Function] [C:2]
    # @PURPOSE: Bind extractor to one Superset environment and client instance.
    def __init__(
        self, environment: Environment, client: SupersetClient | None = None
    ) -> None:
        self.environment = environment
        self.client = client or SupersetClient(environment)
    # #endregion SupersetContextExtractorBase.__init__
    # #region SupersetContextExtractorBase.build_recovery_summary [TYPE Function] [C:2]
    # @PURPOSE: Summarize recovered, partial, and unresolved context for session state and UX.
    def build_recovery_summary(
        self, parsed_context: SupersetParsedContext
    ) -> dict[str, Any]:
        return {
            "dataset_ref": parsed_context.dataset_ref,
            "dataset_id": parsed_context.dataset_id,
            "dashboard_id": parsed_context.dashboard_id,
            "chart_id": parsed_context.chart_id,
            "partial_recovery": parsed_context.partial_recovery,
            "unresolved_references": list(parsed_context.unresolved_references),
            "imported_filter_count": len(parsed_context.imported_filters),
        }
    # #endregion SupersetContextExtractorBase.build_recovery_summary
    # #region SupersetContextExtractorBase._extract_numeric_identifier [TYPE Function] [C:2]
    # @PURPOSE: Extract a numeric identifier from a REST-like Superset URL path.
    def _extract_numeric_identifier(
        self, path_parts: list[str], resource_name: str
    ) -> int | None:
        if resource_name not in path_parts:
            return None
        try:
            resource_index = path_parts.index(resource_name)
        except ValueError:
            return None
        if resource_index + 1 >= len(path_parts):
            return None
        candidate = str(path_parts[resource_index + 1]).strip()
        if not candidate.isdigit():
            return None
        return int(candidate)
    # #endregion SupersetContextExtractorBase._extract_numeric_identifier
    # #region SupersetContextExtractorBase._extract_dashboard_reference [TYPE Function] [C:2]
    # @PURPOSE: Extract a dashboard id-or-slug reference from a Superset URL path.
    def _extract_dashboard_reference(self, path_parts: list[str]) -> str | None:
        if "dashboard" not in path_parts:
            return None
        try:
            resource_index = path_parts.index("dashboard")
        except ValueError:
            return None
        if resource_index + 1 >= len(path_parts):
            return None
        candidate = str(path_parts[resource_index + 1]).strip()
        if not candidate or candidate == "p":
            return None
        return candidate
    # #endregion SupersetContextExtractorBase._extract_dashboard_reference
    # #region SupersetContextExtractorBase._extract_dashboard_permalink_key [TYPE Function] [C:2]
    # @PURPOSE: Extract a dashboard permalink key from a Superset URL path.
    def _extract_dashboard_permalink_key(self, path_parts: list[str]) -> str | None:
        if "dashboard" not in path_parts:
            return None
        try:
            resource_index = path_parts.index("dashboard")
        except ValueError:
            return None
        if resource_index + 2 >= len(path_parts):
            return None
        permalink_marker = str(path_parts[resource_index + 1]).strip()
        permalink_key = str(path_parts[resource_index + 2]).strip()
        if permalink_marker != "p" or not permalink_key:
            return None
        return permalink_key
    # #endregion SupersetContextExtractorBase._extract_dashboard_permalink_key
    # #region SupersetContextExtractorBase._extract_dashboard_id_from_state [TYPE Function] [C:2]
    # @PURPOSE: Extract a dashboard identifier from returned permalink state when present.
    def _extract_dashboard_id_from_state(self, state: dict[str, Any]) -> int | None:
        return self._search_nested_numeric_key(
            payload=state,
            candidate_keys={"dashboardId", "dashboard_id", "dashboard_id_value"},
        )
    # #endregion SupersetContextExtractorBase._extract_dashboard_id_from_state
    # #region SupersetContextExtractorBase._extract_chart_id_from_state [TYPE Function] [C:2]
    # @PURPOSE: Extract a chart identifier from returned permalink state when dashboard id is absent.
    def _extract_chart_id_from_state(self, state: dict[str, Any]) -> int | None:
        return self._search_nested_numeric_key(
            payload=state,
            candidate_keys={"slice_id", "sliceId", "chartId", "chart_id"},
        )
    # #endregion SupersetContextExtractorBase._extract_chart_id_from_state
    # #region SupersetContextExtractorBase._search_nested_numeric_key [TYPE Function] [C:3]
    # @PURPOSE: Recursively search nested dict/list payloads for the first numeric value under a candidate key set.
    # @RELATION DEPENDS_ON -> [SupersetContextExtractorBase]
    def _search_nested_numeric_key(
        self, payload: Any, candidate_keys: set[str]
    ) -> int | None:
        if isinstance(payload, dict):
            for key, value in payload.items():
                if key in candidate_keys:
                    try:
                        if value is not None:
                            return int(value)
                    except (TypeError, ValueError):
                        logger.debug("Could not parse numeric value from payload key %s", key)
                found = self._search_nested_numeric_key(value, candidate_keys)
                if found is not None:
                    return found
        elif isinstance(payload, list):
            for item in payload:
                found = self._search_nested_numeric_key(item, candidate_keys)
                if found is not None:
                    return found
        return None
    # #endregion SupersetContextExtractorBase._search_nested_numeric_key
    # #region SupersetContextExtractorBase._decode_query_state [TYPE Function] [C:2]
    # @PURPOSE: Decode query-string structures used by Superset URL state transport.
    def _decode_query_state(self, query_params: dict[str, list[str]]) -> dict[str, Any]:
        query_state: dict[str, Any] = {}
        for key, values in query_params.items():
            if not values:
                continue
            raw_value = values[-1]
            decoded_value = unquote(raw_value)
            if key in {"native_filters", "form_data", "q"}:
                try:
                    query_state[key] = json.loads(decoded_value)
                    continue
                except Exception:
                    logger.explore(
                        "Failed to decode structured Superset query state; preserving raw value",
                        extra={"key": key},
                    )
            query_state[key] = decoded_value
        return query_state
    # #endregion SupersetContextExtractorBase._decode_query_state
# #endregion SupersetContextExtractorBase
