# #region SupersetDatasetsMixin [C:3] [TYPE Module] [SEMANTICS dataset, superset, search, superset-datasets-mixin]
# @LAYER Infrastructure
# @BRIEF Dataset domain mixin for SupersetClient — list, get, detail, update.
# @RELATION DEPENDS_ON -> [SupersetClientBase]
import json
from typing import Any, cast

from ..logger import belief_scope, logger as app_logger

app_logger = cast(Any, app_logger)
# #region SupersetDatasetsMixin [C:3] [TYPE Class]
# @BRIEF Mixin providing basic dataset CRUD operations (list, get, detail, update).
# @RELATION DEPENDS_ON -> [SupersetClientBase]
class SupersetDatasetsMixin:
    # #region SupersetClientGetDatasets [TYPE Function] [C:3]
    # @PURPOSE: Получает полный список датасетов, автоматически обрабатывая пагинацию.
    # @RELATION CALLS -> [SupersetClientFetchAllPages]
    def get_datasets(self, query: dict | None = None) -> tuple[int, list[dict]]:
        with belief_scope("get_datasets"):
            app_logger.info("[get_datasets][Enter] Fetching datasets.")
            validated_query = self._validate_query_params(query)
            paginated_data = self._fetch_all_pages(
                endpoint="/dataset/",
                pagination_options={
                    "base_query": validated_query,
                    "results_field": "result",
                },
            )
            total_count = len(paginated_data)
            app_logger.reflect(f"Found {total_count} datasets.", extra={"src": "get_datasets"})
            return total_count, paginated_data
    # #endregion SupersetClientGetDatasets
    # #region SupersetClientGetDatasetsSummary [TYPE Function] [C:3]
    # @PURPOSE: Fetches dataset metadata optimized for the Dataset Hub grid.
    # @RELATION CALLS -> [SupersetClientGetDatasets]
    def get_datasets_summary(self) -> list[dict]:
        with belief_scope("SupersetClient.get_datasets_summary"):
            query = {"columns": ["id", "table_name", "schema", "database"]}
            _, datasets = self.get_datasets(query=query)
            result = []
            for ds in datasets:
                result.append(
                    {
                        "id": ds.get("id"),
                        "table_name": ds.get("table_name"),
                        "schema": ds.get("schema"),
                        "database": ds.get("database", {}).get(
                            "database_name", "Unknown"
                        ),
                    }
                )
            return result
    # #endregion SupersetClientGetDatasetsSummary
    # #region SupersetClientGetDatasetLinkedDashboardCount [TYPE Function] [C:2]
    # @PURPOSE: Fetch the number of dashboards linked to a dataset via related_objects endpoint.
    # @RELATION CALLS -> [APIClient]
    # @RATIONALE Added to fix linked_count=0 in StatsBar. Reuses the same /dataset/{id}/related_objects call
    # that get_dataset_detail() was already making, but as a lightweight count-only call.
    # @REJECTED Enriching get_datasets_summary() with more columns rejected — Superset list endpoint
    # doesn't include linked_dashboard_count (it's a computed field from related_objects).
    def get_dataset_linked_dashboard_count(self, dataset_id: int) -> int:
        with belief_scope("get_dataset_linked_dashboard_count", f"id={dataset_id}"):
            try:
                related_objects = self.network.request(
                    method="GET", endpoint=f"/dataset/{dataset_id}/related_objects"
                )
                if isinstance(related_objects, dict):
                    if "dashboards" in related_objects:
                        dashboards_data = related_objects["dashboards"]
                    elif "result" in related_objects and isinstance(
                        related_objects["result"], dict
                    ):
                        dashboards_data = related_objects["result"].get("dashboards", [])
                    else:
                        dashboards_data = []
                    return len(dashboards_data)
                return 0
            except Exception as e:
                app_logger.warning(
                    f"[get_dataset_linked_dashboard_count][Warning] Failed to fetch related dashboards for dataset {dataset_id}: {e}"
                )
                return 0
    # #endregion SupersetClientGetDatasetLinkedDashboardCount
    # #region SupersetClientGetDatasetDetail [TYPE Function] [C:3]
    # @PURPOSE: Fetches detailed dataset information including columns and linked dashboards.
    # @RELATION CALLS -> [SupersetClientGetDataset]
    # @RELATION CALLS -> [APIClient]
    def get_dataset_detail(self, dataset_id: int) -> dict:
        with belief_scope("SupersetClient.get_dataset_detail", f"id={dataset_id}"):
            def as_bool(value, default=False):
                if value is None:
                    return default
                if isinstance(value, bool):
                    return value
                if isinstance(value, str):
                    return value.strip().lower() in ("1", "true", "yes", "y", "on")
                return bool(value)
            response = self.get_dataset(dataset_id)
            if isinstance(response, dict) and "result" in response:
                dataset = response["result"]
            else:
                dataset = response
            columns = dataset.get("columns", [])
            column_info = []
            for col in columns:
                col_id = col.get("id")
                if col_id is None:
                    continue
                column_info.append(
                    {
                        "id": int(col_id),
                        "name": col.get("column_name"),
                        "type": col.get("type"),
                        "is_dttm": as_bool(col.get("is_dttm"), default=False),
                        "is_active": as_bool(col.get("is_active"), default=True),
                        "description": col.get("description", ""),
                    }
                )
            metrics = dataset.get("metrics", [])
            metric_info = []
            for metric in metrics:
                metric_id = metric.get("id")
                if metric_id is None:
                    continue
                metric_info.append(
                    {
                        "id": int(metric_id),
                        "metric_name": metric.get("metric_name"),
                        "expression": metric.get("expression"),
                        "verbose_name": metric.get("verbose_name"),
                        "description": metric.get("description", ""),
                        "metric_type": metric.get("metric_type"),
                    }
                )
            linked_dashboards = []
            try:
                related_objects = self.network.request(
                    method="GET", endpoint=f"/dataset/{dataset_id}/related_objects"
                )
                if isinstance(related_objects, dict):
                    if "dashboards" in related_objects:
                        dashboards_data = related_objects["dashboards"]
                    elif "result" in related_objects and isinstance(
                        related_objects["result"], dict
                    ):
                        dashboards_data = related_objects["result"].get("dashboards", [])
                    else:
                        dashboards_data = []
                    # related_objects returns {"count": N, "result": [...]} — extract the array
                    dashboards_list = (
                        dashboards_data.get("result", [])
                        if isinstance(dashboards_data, dict)
                        else dashboards_data
                    )
                    for dash in dashboards_list:
                        if isinstance(dash, dict):
                            dash_id = dash.get("id")
                            if dash_id is None:
                                continue
                            linked_dashboards.append(
                                {
                                    "id": int(dash_id),
                                    "title": dash.get("dashboard_title")
                                    or dash.get("title", f"Dashboard {dash_id}"),
                                    "slug": dash.get("slug"),
                                }
                            )
                        else:
                            try:
                                dash_id = int(dash)
                            except (TypeError, ValueError):
                                continue
                            linked_dashboards.append(
                                {"id": dash_id, "title": f"Dashboard {dash_id}", "slug": None}
                            )
            except Exception as e:
                app_logger.warning(
                    f"[get_dataset_detail][Warning] Failed to fetch related dashboards: {e}"
                )
                linked_dashboards = []
            database_obj = dataset.get("database")
            database_dict = database_obj if isinstance(database_obj, dict) else {}
            sql = dataset.get("sql", "")
            result = {
                "id": dataset.get("id"),
                "table_name": dataset.get("table_name"),
                "schema": dataset.get("schema"),
                "database": database_dict,
                "database_id": dataset.get("database_id", database_dict.get("id")),
                "database_backend": database_dict.get("backend") or database_dict.get("engine") or "",
                "description": dataset.get("description", ""),
                "columns": column_info,
                "column_count": len(column_info),
                "metrics": metric_info,
                "metric_count": len(metric_info),
                "sql": sql,
                "linked_dashboards": linked_dashboards,
                "linked_dashboard_count": len(linked_dashboards),
                "is_sqllab_view": as_bool(dataset.get("is_sqllab_view"), default=False),
                "created_on": dataset.get("created_on"),
                "changed_on": dataset.get("changed_on"),
            }
            app_logger.reflect(
                f"Got dataset {dataset_id} with {len(column_info)} columns, {len(metric_info)} metrics, and {len(linked_dashboards)} linked dashboards",
                extra={"src": "get_dataset_detail"},
            )
            return result
    # #endregion SupersetClientGetDatasetDetail
    # #region SupersetClientGetDataset [TYPE Function] [C:3]
    # @PURPOSE: Получает информацию о конкретном датасете по его ID.
    # @RELATION CALLS -> [APIClient]
    def get_dataset(self, dataset_id: int) -> dict:
        with belief_scope("SupersetClient.get_dataset", f"id={dataset_id}"):
            app_logger.info("[get_dataset][Enter] Fetching dataset %s.", dataset_id)
            response = self.network.request(
                method="GET", endpoint=f"/dataset/{dataset_id}"
            )
            response = cast(dict, response)
            app_logger.reflect(f"Got dataset {dataset_id}.", extra={"src": "get_dataset"})
            return response
    # #endregion SupersetClientGetDataset
    # #region SupersetClientUpdateDataset [TYPE Function] [C:3]
    # @PURPOSE: Обновляет данные датасета по его ID.
    # @SIDE_EFFECT  Modifies resource in upstream Superset environment.
    # @RELATION CALLS -> [APIClient]
    def update_dataset(self, dataset_id: int, data: dict, override_columns: bool = False) -> dict:
        with belief_scope("SupersetClient.update_dataset", f"id={dataset_id}"):
            app_logger.info("[update_dataset][Enter] Updating dataset %s.", dataset_id)
            endpoint = f"/dataset/{dataset_id}?override_columns={'true' if override_columns else 'false'}"
            response = self.network.request(
                method="PUT",
                endpoint=endpoint,
                data=json.dumps(data),
                headers={"Content-Type": "application/json"},
            )
            response = cast(dict, response)
            app_logger.reflect(f"Updated dataset {dataset_id}.", extra={"src": "update_dataset"})
            return response
    # #endregion SupersetClientUpdateDataset
# #endregion SupersetDatasetsMixin
# #endregion SupersetDatasetsMixin
