"""DONNÉES FICTIVES de démonstration. Interdites en production (voir cli.py)."""
from __future__ import annotations

import random
from datetime import datetime, timedelta, timezone
from decimal import Decimal

from sqlalchemy import delete, select
from sqlalchemy.orm import Session

from .core import audit, isoweek
from .models import Bilan, BilanValue, BilanVersion, Indicator, ValueDetail
from .services import bilans as svc

TAG = "[DONNÉES FICTIVES]"
# Ordres de grandeur arbitraires, sans lien avec une activité réelle.
BASE = {
    "alcool_delictuelle": 3, "alcool_contraventionnelle": 4, "stupefiants": 3, "vitesse_plus_50": 1,
    "vitesse_40_49": 3, "vitesse_39_moins": 28, "distracteurs": 6, "afd_stupefiants": 2, "afd_assurance": 2,
    "afd_permis": 2, "contrefacons": 0, "esi": 1, "autres_infractions": 9, "enquetes_judiciaires": 2,
}
OBSERVATIONS = [
    "Opération coordonnée fictive sur un axe départemental.",
    "Contrôle de nuit (exemple fictif).",
    "Dont 1 récidive (exemple fictif).",
    "Procédure transmise (exemple fictif).",
    "Contrôles en agglomération (exemple fictif).",
]


def _weeks() -> list[tuple[int, int]]:
    out = []
    y, w = 2025, 27
    while (y, w) <= (2026, 40):
        out.append((y, w))
        y, w = isoweek.shift_week(y, w, 1)
    return out


def load(db: Session) -> str:
    if db.execute(select(Bilan.id).where(Bilan.is_demo.is_(True)).limit(1)).first():
        return "Des données fictives sont déjà présentes (utilisez demo-clear avant de recharger)."
    taken = {(b.iso_year, b.iso_week) for b in db.execute(select(Bilan)).scalars()}
    indicators = list(db.execute(select(Indicator).where(Indicator.is_active.is_(True))
                                 .order_by(Indicator.position)).scalars())
    rng = random.Random(52)
    now = datetime.now(timezone.utc)
    created = 0
    prev_values: dict[int, int | None] = {}
    for y, w in _weeks():
        if (y, w) in taken:
            continue
        start, end = isoweek.week_bounds(y, w)
        is_ref = (y, w) == (2026, 40)
        status = "brouillon" if is_ref else "archive" if y == 2025 and w < 30 else "valide"
        b = Bilan(iso_year=y, iso_week=w, period_start=start, period_end=end, status=status, revision=1,
                  current_version=0, is_demo=True,
                  updated_at=datetime.combine(end + timedelta(days=1), datetime.min.time(), timezone.utc))
        db.add(b)
        db.flush()
        duplicate = (y, w) == (2026, 33)  # exemple de doublon : copie exacte de la semaine 32
        season = 1.25 if start.month in (7, 8) else 1.0
        for ind in indicators:
            base = BASE.get(ind.code, 2)
            if duplicate:
                value = prev_values.get(ind.id)
            else:
                value = max(0, round(rng.gauss(base * season, max(1.0, base * 0.45))))
                if rng.random() < 0.04:
                    value = None  # quelques cellules non renseignées
                if is_ref and ind.code in ("esi", "contrefacons"):
                    value = None
                if ind.code == "contrefacons" and value is not None and rng.random() < 0.6:
                    value = 0  # zéros explicites
            obs = ""
            if value and rng.random() < 0.16:
                obs = f"{rng.choice(OBSERVATIONS)} {TAG}"
            v = BilanValue(bilan_id=b.id, indicator_id=ind.id, value=value, observation=obs, label_snapshot=ind.label,
                           source_kind="demo", warnings=[])
            db.add(v)
            db.flush()
            if ind.detail_kind and value:
                for pos in range(min(value, 3)):
                    amount = (Decimal(str(round(rng.uniform(0.85, 1.6), 2))) if ind.detail_kind == "taux"
                              else Decimal(rng.randint(125, 168)))
                    db.add(ValueDetail(value_id=v.id, kind=ind.detail_kind, amount=amount, unit=ind.detail_unit,
                                       position=pos))
            prev_values[ind.id] = value
        db.flush()
        db.refresh(b)
        if status != "brouillon":
            snap = svc.snapshot_of(db, b)
            at = datetime.combine(end + timedelta(days=2), datetime.min.time(), timezone.utc) + timedelta(hours=8)
            snap.update(version_no=1, validated_by="Démonstration (fictif)", validated_at=at.isoformat())
            db.add(BilanVersion(bilan_id=b.id, version_no=1, snapshot=snap, snapshot_sha256=svc.snapshot_digest(snap),
                                comment=TAG, is_demo=True, created_at=at))
            b.current_version, b.validated_at = 1, at
            if status == "archive":
                b.archived_at = now
        created += 1
    audit.record(db, "demo.loaded", username="cli", details={"bilans": created, "mention": TAG})
    return f"{created} bilans FICTIFS chargés (S27 2025 → S40 2026 ; S40 2026 laissée en brouillon)."


def clear(db: Session) -> str:
    ids = list(db.execute(select(Bilan.id).where(Bilan.is_demo.is_(True))).scalars())
    if not ids:
        return "Aucune donnée fictive à supprimer."
    db.execute(delete(BilanVersion).where(BilanVersion.bilan_id.in_(ids), BilanVersion.is_demo.is_(True)))
    db.execute(delete(Bilan).where(Bilan.id.in_(ids)))
    audit.record(db, "demo.cleared", username="cli", details={"bilans": len(ids)})
    return f"{len(ids)} bilans fictifs supprimés."
