#!/usr/bin/env python3
"""
🗃️ Connecteur base de données - Web Sentinel

Simplifie les interactions avec la base d'authentification réelle de Web
Sentinel pour le panneau d'administration. Les principales tâches couvertes :

* Création d'utilisateurs de test
* Mise à jour des tiers et des dates d'expiration
* Récupération de la liste des utilisateurs et de statistiques agrégées
"""

from __future__ import annotations

import json
import sqlite3
from dataclasses import dataclass
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Dict, List, Optional

from web_sentinel.auth.models import AuthProvider, User
from web_sentinel.subscription.manager import SubscriptionManager, SubscriptionTier

SQLITE_ISOLATION_LEVEL = None  # mode autocommit pour éviter les verrous longs


@dataclass
class UserRecord:
    user_id: str
    email: str
    username: Optional[str]
    tier: str
    expires_at: Optional[str]
    created_at: Optional[str]
    updated_at: Optional[str]


class RealDatabaseConnector:
    """Interface simple avec la base SQLite utilisée par l'application réelle."""

    def __init__(self, data_dir: Optional[Path] = None) -> None:
        self.data_dir = data_dir or (Path.home() / ".web-sentinel")
        self.data_dir.mkdir(parents=True, exist_ok=True)
        self.auth_db_path = self.data_dir / "auth.db"
        self._ensure_schema()

    # ------------------------------------------------------------------ #
    # Initialisation
    # ------------------------------------------------------------------ #

    def _ensure_schema(self) -> None:
        """Créer les tables nécessaires si elles n'existent pas."""
        with sqlite3.connect(self.auth_db_path, isolation_level=SQLITE_ISOLATION_LEVEL) as conn:
            conn.executescript(
                """
                CREATE TABLE IF NOT EXISTS users (
                    id TEXT PRIMARY KEY,
                    email TEXT UNIQUE NOT NULL,
                    username TEXT,
                    data TEXT NOT NULL,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                );

                CREATE TABLE IF NOT EXISTS user_subscriptions (
                    user_id TEXT PRIMARY KEY,
                    email TEXT UNIQUE NOT NULL,
                    tier TEXT NOT NULL DEFAULT 'free',
                    expires_at TIMESTAMP,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    FOREIGN KEY (user_id) REFERENCES users(id)
                );
                """
            )

    # ------------------------------------------------------------------ #
    # Opérations utilisateurs
    # ------------------------------------------------------------------ #

    def list_users(self) -> List[UserRecord]:
        """Récupérer la liste des utilisateurs et leurs abonnements."""
        with sqlite3.connect(self.auth_db_path) as conn:
            cursor = conn.execute(
                """
                SELECT u.id, u.email, u.username, s.tier, s.expires_at, u.created_at, u.updated_at
                FROM users u
                LEFT JOIN user_subscriptions s ON u.id = s.user_id
                ORDER BY u.created_at DESC
                """
            )
            rows = cursor.fetchall()

        users: List[UserRecord] = []
        for row in rows:
            tier = row[3] or "free"
            users.append(
                UserRecord(
                    user_id=row[0],
                    email=row[1],
                    username=row[2],
                    tier=tier.upper(),
                    expires_at=row[4],
                    created_at=row[5],
                    updated_at=row[6],
                )
            )
        return users

    def get_user(self, email: str) -> Optional[UserRecord]:
        """Retourner les informations d'un utilisateur donné."""
        email = email.lower()
        users = self.list_users()
        for user in users:
            if user.email.lower() == email:
                return user
        return None

    def create_test_user(
        self,
        email: str,
        password: str = "test123",
        first_name: str = "Admin",
        last_name: str = "Panel",
    ) -> UserRecord:
        """Créer un utilisateur s'il n'existe pas déjà."""
        existing = self.get_user(email)
        if existing:
            return existing

        user = User(
            email=email,
            first_name=first_name,
            last_name=last_name,
            auth_provider=AuthProvider.EMAIL,
            email_verified=True,
        )
        user.set_password(password)

        tier = SubscriptionTier.FREE

        with sqlite3.connect(self.auth_db_path, isolation_level=SQLITE_ISOLATION_LEVEL) as conn:
            conn.execute(
                """
                INSERT INTO users (id, email, username, data)
                VALUES (?, ?, ?, ?)
                """,
                (user.id, user.email, user.username, json.dumps(user.to_dict())),
            )
            conn.execute(
                """
                INSERT INTO user_subscriptions (user_id, email, tier)
                VALUES (?, ?, ?)
                """,
                (user.id, user.email, tier.value),
            )

        return self.get_user(email)  # type: ignore[return-value]

    def set_user_tier(self, email: str, tier: SubscriptionTier, expires_at: Optional[datetime] = None) -> bool:
        """Mettre à jour le tier (et éventuellement l'expiration) d'un utilisateur."""
        record = self.get_user(email)
        if not record:
            return False

        expires_value = expires_at.isoformat() if expires_at else None
        with sqlite3.connect(self.auth_db_path, isolation_level=SQLITE_ISOLATION_LEVEL) as conn:
            conn.execute(
                """
                INSERT OR REPLACE INTO user_subscriptions (user_id, email, tier, expires_at, updated_at)
                VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
                """,
                (record.user_id, record.email, tier.value, expires_value),
            )

        return True

    def set_user_expiry(self, email: str, days_from_now: int) -> bool:
        """Fixer une date d'expiration relative (en jours)."""
        target = self.get_user(email)
        if not target:
            return False
        expiry = datetime.now(timezone.utc) + timedelta(days=days_from_now)
        return self.set_user_tier(email, SubscriptionTier(target.tier.lower()), expiry)

    # ------------------------------------------------------------------ #
    # Statistiques
    # ------------------------------------------------------------------ #

    def compute_subscription_stats(self) -> Dict[str, Dict]:
        """Calculer des statistiques agrégées par tier."""
        users = self.list_users()
        now = datetime.now(timezone.utc)

        summary = {
            "total_users": len(users),
            "tiers": {},
            "expired": 0,
            "expiring_7_days": 0,
        }

        for user in users:
            tier = user.tier.upper()
            bucket = summary["tiers"].setdefault(
                tier,
                {
                    "count": 0,
                    "active": 0,
                    "expired": 0,
                    "expires_soon": 0,
                    "next_expiration": None,
                },
            )
            bucket["count"] += 1

            if user.expires_at:
                try:
                    expiry_dt = datetime.fromisoformat(user.expires_at.replace("Z", "+00:00"))
                except ValueError:
                    expiry_dt = None
                if expiry_dt:
                    if expiry_dt <= now:
                        bucket["expired"] += 1
                        summary["expired"] += 1
                    elif expiry_dt <= now + timedelta(days=7):
                        bucket["expires_soon"] += 1
                        summary["expiring_7_days"] += 1
                        bucket["active"] += 1
                    else:
                        bucket["active"] += 1
                    if expiry_dt and (
                        bucket["next_expiration"] is None or expiry_dt.isoformat() < bucket["next_expiration"]
                    ):
                        bucket["next_expiration"] = expiry_dt.isoformat()
                else:
                    bucket["active"] += 1
            else:
                bucket["active"] += 1

        return summary


__all__ = ["RealDatabaseConnector", "UserRecord"]
