from __future__ import annotations

"""Passerelle runtime pour charger la licence embarquée et appliquer les restrictions."""

import os
import shutil
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable, Optional

from .backends import load_backend, requires_backend
from .license_manager import LicenceError, LicenceInfo
from .license_validator import LicenceStatus, LicenceValidator
from ..subscription.manager import SubscriptionManager
from ..subscription.models import SubscriptionFeatures, SubscriptionTier, get_features_for_tier


DEFAULT_LICENSE_FILENAME = "license.lic"
ENV_LICENSE_PATH = "WEB_SENTINEL_LICENCE_PATH"
ENV_LICENSE_PATH_ALT = "WEB_SENTINEL_LICENSE_PATH"

_ALERT_THRESHOLDS: Iterable[int] = (30, 14, 7, 3, 1)


@dataclass
class LicenceRuntimeState:
    """Contient l'état courant de la licence et les fonctionnalités associées."""

    validator: LicenceValidator
    status: LicenceStatus
    features: SubscriptionFeatures
    alert_threshold: Optional[int]
    backend_name: str
    backend_verified: bool

    def apply_to(self, manager: SubscriptionManager) -> None:
        """Appliquer les fonctionnalités calculées au SubscriptionManager fourni."""
        manager.apply_features(self.features)

    @property
    def is_expired(self) -> bool:
        return self.status.expires_in_days < 0


def resolve_license_path() -> Path:
    """Déterminer le chemin de la licence (variable d'env ou ~/.web-sentinel/license.lic)."""
    env_path = os.getenv(ENV_LICENSE_PATH) or os.getenv(ENV_LICENSE_PATH_ALT)
    if env_path:
        return Path(env_path).expanduser()
    return Path.home() / ".web-sentinel" / DEFAULT_LICENSE_FILENAME


def load_runtime_state(
    licence_path: Optional[Path] = None,
    hardware_fingerprint: str = "",
) -> LicenceRuntimeState:
    """Charger la licence et retourner l'état runtime complet."""
    path = licence_path or resolve_license_path()
    validator = LicenceValidator(path, hardware_fingerprint=hardware_fingerprint)
    status = validator.refresh()
    features = _build_features_from_licence(status.info)
    alert = _determine_alert_threshold(status.expires_in_days)

    backend = load_backend()
    backend_verified = False
    if requires_backend(status.info):
        backend.verify(status.info)
        backend_verified = True

    return LicenceRuntimeState(
        validator=validator,
        status=status,
        features=features,
        alert_threshold=alert,
        backend_name=getattr(backend, "name", "none"),
        backend_verified=backend_verified,
    )


def install_licence_file(source_path: Path, destination_path: Optional[Path] = None) -> Path:
    """Copier un fichier licence fourni vers l’emplacement attendu et retourner le chemin."""
    if not source_path.exists():
        raise LicenceError(f"Licence introuvable: {source_path}")
    destination = destination_path or resolve_license_path()
    destination.parent.mkdir(parents=True, exist_ok=True)
    shutil.copy2(source_path, destination)
    return destination


def _build_features_from_licence(info: LicenceInfo) -> SubscriptionFeatures:
    """Convertir les informations de licence en matrice de fonctionnalités."""
    tier = _coerce_tier(info.tier)
    base_features = get_features_for_tier(tier)
    licence_flags = info.features or {}

    domain_limit = info.max_domains if (info.max_domains and info.max_domains > 0) else base_features.domain_limit
    max_users = info.max_users if (info.max_users and info.max_users > 0) else base_features.max_users

    allow_invasive = base_features.allow_invasive_tests or licence_flags.get("invasive_tests", False)
    allow_html = base_features.allow_html_export or any(
        licence_flags.get(alias, False) for alias in ("html_export", "advanced_reports")
    )
    allow_multi_user = base_features.allow_multi_user or licence_flags.get("multi_user", False)
    allow_api = base_features.allow_api_access or any(
        licence_flags.get(alias, False) for alias in ("api_access", "rest_api")
    )

    return SubscriptionFeatures(
        tier=tier,
        domain_limit=domain_limit,
        max_users=max_users,
        allow_invasive_tests=allow_invasive,
        allow_html_export=allow_html,
        allow_multi_user=allow_multi_user,
        allow_api_access=allow_api,
    )


def _coerce_tier(raw_tier: str) -> SubscriptionTier:
    """Normaliser le tier fourni par la licence."""
    if not raw_tier:
        return SubscriptionTier.FREE
    normalized = raw_tier.strip().lower()
    for tier in SubscriptionTier:
        if tier.value == normalized:
            return tier
    # Tiers premium (ENTERPRISE+, GOVERNMENT, etc.) → aligner sur ENTERPRISE
    return SubscriptionTier.ENTERPRISE


def _determine_alert_threshold(expires_in_days: float) -> Optional[int]:
    """Retourner le seuil d'alerte correspondant à la date d'expiration."""
    if expires_in_days < 0:
        return 0
    # Chercher le plus petit seuil où on est encore dans la période d'alerte
    for threshold in sorted(_ALERT_THRESHOLDS):
        if expires_in_days <= threshold:
            return threshold
    return None
