# #region NetworkModule [C:3] [TYPE Module] [SEMANTICS network, http, retry, tenacity, session]
# @defgroup Core Module group.
#
# @BRIEF Инкапсулирует низкоуровневую HTTP-логику для взаимодействия с Superset API, включая аутентификацию, управление сессией, retry-логику и обработку ошибок.
# @LAYER Infrastructure
# @RELATION DEPENDS_ON -> [LoggerModule]
# @PUBLIC_API  APIClient
import io
import json
from pathlib import Path
import threading
import time
from typing import Any, cast

import requests
from requests.adapters import HTTPAdapter
import urllib3
from urllib3.util.retry import Retry

from ..logger import belief_scope, logger as app_logger


# #region SupersetAPIError [C:1] [TYPE Class]
# @BRIEF Base exception for all Superset API related errors.
class SupersetAPIError(Exception):
    # #region __init__ [TYPE Function] [C:1]
    # @PURPOSE: Initializes the exception with a message and context.
    # @PRE  message is a string, context is a dict.
    # @POST  Exception is initialized with context.
    def __init__(self, message: str = "Superset API error", **context: Any):
        with belief_scope("SupersetAPIError.__init__"):
            self.context = context
            super().__init__(f"[API_FAILURE] {message} | Context: {self.context}")
    # #endregion __init__
# #endregion SupersetAPIError
# #region AuthenticationError [C:1] [TYPE Class]
# @BRIEF Exception raised when authentication fails.
class AuthenticationError(SupersetAPIError):
    # #region __init__ [TYPE Function] [C:1]
    # @PURPOSE: Initializes the authentication error.
    # @PRE  message is a string, context is a dict.
    # @POST  AuthenticationError is initialized.
    def __init__(self, message: str = "Authentication failed", **context: Any):
        with belief_scope("AuthenticationError.__init__"):
            super().__init__(message, type="authentication", **context)
    # #endregion __init__
# #endregion AuthenticationError
# #region PermissionDeniedError [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Exception raised when access is denied.
class PermissionDeniedError(AuthenticationError):
    # #region __init__ [TYPE Function]
    # @PURPOSE: Initializes the permission denied error.
    # @PRE  message is a string, context is a dict.
    # @POST  PermissionDeniedError is initialized.
    def __init__(self, message: str = "Permission denied", **context: Any):
        with belief_scope("PermissionDeniedError.__init__"):
            super().__init__(message, **context)
    # #endregion __init__
# #endregion PermissionDeniedError
# #region DashboardNotFoundError [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Exception raised when a dashboard cannot be found.
class DashboardNotFoundError(SupersetAPIError):
    # #region __init__ [TYPE Function]
    # @PURPOSE: Initializes the not found error with resource ID.
    # @PRE  resource_id is provided.
    # @POST  DashboardNotFoundError is initialized.
    def __init__(self, resource_id: int | str, message: str = "Dashboard not found", **context: Any):
        with belief_scope("DashboardNotFoundError.__init__"):
            super().__init__(f"Dashboard '{resource_id}' {message}", subtype="not_found", resource_id=resource_id, **context)
    # #endregion __init__
# #endregion DashboardNotFoundError
# #region NetworkError [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Exception raised when a network level error occurs.
class NetworkError(Exception):
    # #region NetworkError.__init__ [TYPE Function]
# @ingroup Core
    # @PURPOSE: Initializes the network error.
    # @PRE  message is a string.
    # @POST  NetworkError is initialized.
    def __init__(self, message: str = "Network connection failed", **context: Any):
        with belief_scope("NetworkError.__init__"):
            self.context = context
            super().__init__(f"[NETWORK_FAILURE] {message} | Context: {self.context}")
    # #endregion NetworkError.__init__
# #endregion NetworkError
# #region SupersetAuthCache [TYPE Class]
# @defgroup Core Module group.
# @BRIEF Process-local cache for Superset access/csrf tokens keyed by environment credentials.
# @PRE  base_url and username are stable strings.
# @POST  Cached entries expire automatically by TTL and can be reused across requests.
class SupersetAuthCache:
    TTL_SECONDS = 300
    _lock = threading.Lock()
    _entries: dict[tuple[str, str, bool], dict[str, Any]] = {}
    @classmethod
    def build_key(cls, base_url: str, auth: dict[str, Any] | None, verify_ssl: bool) -> tuple[str, str, bool]:
        username = ""
        if isinstance(auth, dict):
            username = str(auth.get("username") or "").strip()
        return (str(base_url or "").strip(), username, bool(verify_ssl))
    @classmethod
    # #region SupersetAuthCache.get [TYPE Function]
# @ingroup Core
    def get(cls, key: tuple[str, str, bool]) -> dict[str, str] | None:
        now = time.time()
        with cls._lock:
            payload = cls._entries.get(key)
            if not payload:
                return None
            expires_at = float(payload.get("expires_at") or 0)
            if expires_at <= now:
                cls._entries.pop(key, None)
                return None
            tokens = payload.get("tokens")
            if not isinstance(tokens, dict):
                cls._entries.pop(key, None)
                return None
            return {
                "access_token": str(tokens.get("access_token") or ""),
                "csrf_token": str(tokens.get("csrf_token") or ""),
            }
    # #endregion SupersetAuthCache.get
    @classmethod
    # #region SupersetAuthCache.set [TYPE Function]
# @ingroup Core
    def set(cls, key: tuple[str, str, bool], tokens: dict[str, str], ttl_seconds: int | None = None) -> None:
        normalized_ttl = max(int(ttl_seconds or cls.TTL_SECONDS), 1)
        with cls._lock:
            cls._entries[key] = {
                "tokens": {
                    "access_token": str(tokens.get("access_token") or ""),
                    "csrf_token": str(tokens.get("csrf_token") or ""),
                },
                "expires_at": time.time() + normalized_ttl,
            }
    # #endregion SupersetAuthCache.set
    @classmethod
    def invalidate(cls, key: tuple[str, str, bool]) -> None:
        with cls._lock:
            cls._entries.pop(key, None)
# #endregion SupersetAuthCache
# #endregion NetworkModule
