"""Paramètres applicatifs : valeurs par défaut sûres, surchargées en base par un administrateur."""
from __future__ import annotations

from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session

from ..models import Setting

# (valeur par défaut, type, min, max, libellé)
DEFAULTS: dict[str, tuple[Any, type, Any, Any, str]] = {
    "mfa_required_admin": (True, bool, None, None, "MFA obligatoire pour les gestionnaires"),
    "mfa_required_reader": (False, bool, None, None, "MFA obligatoire pour les lecteurs"),
    "password_min_length": (12, int, 10, 64, "Longueur minimale des mots de passe"),
    "session_idle_minutes": (30, int, 5, 240, "Expiration après inactivité (minutes)"),
    "session_absolute_hours": (12, int, 1, 72, "Durée maximale d'une session (heures)"),
    "login_max_failures": (5, int, 3, 20, "Échecs avant verrouillage temporaire"),
    "login_lockout_minutes": (15, int, 1, 1440, "Durée du verrouillage (minutes)"),
    "autosave_delay_ms": (1500, int, 500, 10000, "Délai d'inactivité avant sauvegarde automatique (ms)"),
    "variation_pct": (100, int, 10, 1000, "Variation signalée par rapport à la semaine précédente (%)"),
    "variation_min_abs": (5, int, 1, 1000, "Écart minimal (en unités) pour signaler une variation"),
    "document_title": ("BILAN HEBDOMADAIRE — EDCF 52", str, None, 120, "Titre du document"),
    "document_section": ("INFRACTIONS RELEVÉES", str, None, 120, "Titre de la section"),
    "document_footer": ("Escadron départemental de contrôle des flux de la Haute-Marne", str, None, 200,
                        "Pied de page du document"),
    "diffusion_mention": ("", str, None, 200, "Mention de diffusion (vide = aucune)"),
    "month_rule": ("jeudi", str, None, 16, "Règle de rattachement mensuel (jeudi | prorata)"),
    "import_max_mb": (10, int, 1, 25, "Taille maximale d'un fichier importé (Mo)"),
    "import_hourly_quota": (30, int, 1, 500, "Imports maximum par utilisateur et par heure"),
    "import_retention_days": (30, int, 1, 3650, "Conservation des fichiers importés (jours)"),
}
ENUMS = {"month_rule": {"jeudi", "prorata"}}


class SettingsError(ValueError):
    pass


def get_all(db: Session) -> dict[str, Any]:
    values = {k: v[0] for k, v in DEFAULTS.items()}
    for row in db.execute(select(Setting)).scalars():
        if row.key in DEFAULTS:
            values[row.key] = row.value
    return values


def get(db: Session, key: str) -> Any:
    row = db.get(Setting, key)
    return row.value if row is not None else DEFAULTS[key][0]


def coerce(key: str, value: Any) -> Any:
    if key not in DEFAULTS:
        raise SettingsError(f"Paramètre inconnu : {key}")
    _, typ, lo, hi, label = DEFAULTS[key]
    if typ is bool:
        if not isinstance(value, bool):
            raise SettingsError(f"{label} : valeur oui/non attendue.")
        return value
    if typ is int:
        if isinstance(value, bool) or not isinstance(value, int):
            raise SettingsError(f"{label} : nombre entier attendu.")
        if (lo is not None and value < lo) or (hi is not None and value > hi):
            raise SettingsError(f"{label} : valeur attendue entre {lo} et {hi}.")
        return value
    if not isinstance(value, str):
        raise SettingsError(f"{label} : texte attendu.")
    value = " ".join(value.split())
    if hi is not None and len(value) > hi:
        raise SettingsError(f"{label} : {hi} caractères maximum.")
    if key in ENUMS and value not in ENUMS[key]:
        raise SettingsError(f"{label} : valeur non autorisée.")
    return value


def set_many(db: Session, changes: dict[str, Any], user_id: int) -> dict[str, tuple[Any, Any]]:
    current = get_all(db)
    diff: dict[str, tuple[Any, Any]] = {}
    for key, raw in changes.items():
        value = coerce(key, raw)
        if current[key] == value:
            continue
        row = db.get(Setting, key)
        if row is None:
            db.add(Setting(key=key, value=value, updated_by=user_id))
        else:
            row.value = value
            row.updated_by = user_id
        diff[key] = (current[key], value)
    return diff
