from __future__ import annotations

"""Backends de validation externe des licences."""

import os
import sqlite3
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional, Protocol

from .license_manager import LicenceError, LicenceInfo

OFFLINE_TIERS = {"GOVERNMENT", "MEDICAL", "FINANCE", "DEFENSE", "AIRGAP"}
DEFAULT_SQLITE_PATH = Path.home() / ".web-sentinel" / "auth.db"


class LicenceBackend(Protocol):
    """Interface minimale pour les backends de validation."""

    name: str

    def verify(self, info: LicenceInfo) -> None:
        """Valider une licence. Lève `LicenceError` en cas d'échec."""


@dataclass
class NullBackend:
    """Backend qui n'applique aucune validation supplémentaire."""

    name: str = "none"

    def verify(self, info: LicenceInfo) -> None:  # noqa: D401 - interface simple
        return


@dataclass
class SQLiteBackend:
    """Validation sur un fichier SQLite (auth.db)."""

    db_path: Path
    name: str = "sqlite"

    def verify(self, info: LicenceInfo) -> None:
        if not self.db_path.exists():
            raise LicenceError(f"Base SQLite introuvable: {self.db_path}")
        if not info.super_admin_email:
            raise LicenceError("La licence ne contient pas d'email super admin pour vérification.")

        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute(
                "SELECT tier, expires_at FROM user_subscriptions WHERE email = ?",
                (info.super_admin_email.lower(),),
            )
            row = cursor.fetchone()

        if row is None:
            raise LicenceError(f"Aucune souscription externe pour {info.super_admin_email}.")

        db_tier, db_expires_at = row
        tier_mismatch = db_tier and db_tier.lower() != info.tier.lower()
        if tier_mismatch:
            raise LicenceError(
                f"Incohérence de tier: licence={info.tier} / base={db_tier}."
            )

        if db_expires_at:
            try:
                db_expiry = datetime.fromisoformat(str(db_expires_at).replace("Z", "+00:00"))
            except ValueError:
                db_expiry = None
            if db_expiry and db_expiry < datetime.now(timezone.utc):
                raise LicenceError("La souscription externe est expirée.")


def requires_backend(info: LicenceInfo) -> bool:
    """Déterminer si une licence doit être validée par un backend externe."""
    tier = info.tier.upper()
    if tier in OFFLINE_TIERS:
        return False
    if info.features.get("air_gap"):
        return False
    return tier in {"FREE", "PRO", "ENTERPRISE"}


def load_backend() -> LicenceBackend:
    """Charger le backend depuis la configuration d'environnement."""
    mode = os.getenv("WEB_SENTINEL_SUBSCRIPTION_BACKEND", "auto").lower()

    if mode == "none":
        return NullBackend()

    if mode in {"sqlite", "auto"}:
        db_path = Path(os.getenv("WEB_SENTINEL_SUBSCRIPTION_DB", str(DEFAULT_SQLITE_PATH))).expanduser()
        if mode == "auto" and not db_path.exists():
            return NullBackend()
        return SQLiteBackend(db_path=db_path)

    return NullBackend()
