"""Saisie, sauvegarde, vide ≠ zéro, conflits, validation, réouverture, versions (intégration)."""
import pytest
from sqlalchemy import text

from edcf.core import db as dbmod
from edcf.models import AuditLog
from tests.conftest import Api, full_rows


def save(api: Api, rows, revision=None, y=2026, w=40):
    if revision is None:
        revision = api.get(f"/api/bilans/{y}/{w}").json()["revision"]
    return api.put(f"/api/bilans/{y}/{w}/draft", {"revision": revision, "rows": rows})


def test_semaine_40_par_defaut_et_14_indicateurs_dans_l_ordre(admin: Api):
    b = admin.get("/api/bilans/2026/40").json()
    assert b["period_start"] == "2026-09-28" and b["period_end"] == "2026-10-04"
    assert b["status"] == "nouveau" and b["editable"]
    labels = [r["label"] for r in b["rows"]]
    assert labels[0] == "Alcoolémie délictuelle" and labels[3] == "Vitesse (+ 50 km/h)"
    assert labels[-1] == "Enquêtes judiciaires" and len(labels) == 14
    assert b["rows"][0]["detail_kind"] == "taux" and b["rows"][3]["detail_kind"] == "vitesse"


def test_vide_reste_vide_et_zero_reste_zero(admin: Api):
    rows = [{"indicator_id": 1, "value": None, "observation": "à compléter"},
            {"indicator_id": 2, "value": 0, "observation": "RAS"},
            {"indicator_id": 3, "value": "", "observation": ""}]
    b = save(admin, rows).json()
    by_id = {r["indicator_id"]: r for r in b["rows"]}
    assert by_id[1]["value"] is None and by_id[1]["observation"] == "à compléter"
    assert by_id[2]["value"] == 0
    assert by_id[3]["value"] is None
    chk = admin.get("/api/bilans/2026/40/check").json()
    assert "Conduite sous stupéfiants" in chk["empty"] and "Alcoolémie contraventionnelle" in chk["zeros"]


def test_erreurs_de_saisie_bloquantes(admin: Api):
    r = save(admin, [{"indicator_id": 1, "value": "-2"}, {"indicator_id": 2, "value": "3,5"}])
    assert r.status_code == 422
    issues = r.json()["issues"]
    assert {i["indicator_id"] for i in issues} == {1, 2}


def test_conflit_de_revision(admin: Api):
    save(admin, [{"indicator_id": 1, "value": 1}])
    r = admin.put("/api/bilans/2026/40/draft", {"revision": 0, "rows": [{"indicator_id": 1, "value": 5}]})
    assert r.status_code == 409 and r.json()["code"] == "conflict"


def test_details_taux_et_vitesses(admin: Api):
    r = save(admin, [{"indicator_id": 1, "value": 2, "details": ["0,92", "1.10"]},
                     {"indicator_id": 4, "value": 1, "details": [{"value": "132"}]}])
    rows = {x["indicator_id"]: x for x in r.json()["rows"]}
    assert [d["value"] for d in rows[1]["details"]] == ["0.92", "1.1"]
    assert rows[4]["details"][0]["unit"] == "km/h"
    assert save(admin, [{"indicator_id": 1, "value": 2, "details": ["abc"]}]).status_code == 422


def test_cycle_validation_reouverture_versions(admin: Api):
    rows = full_rows(admin)
    b = save(admin, rows).json()
    assert admin.post("/api/bilans/2026/40/validate", {"revision": b["revision"], "confirm": False}).status_code == 422
    v = admin.post("/api/bilans/2026/40/validate", {"revision": b["revision"], "confirm": True})
    assert v.status_code == 200, v.text
    v = v.json()
    assert v["status"] == "valide" and v["current_version"] == 1 and v["validated_by"]
    # Verrouillé : aucune modification accidentelle.
    assert save(admin, [{"indicator_id": 1, "value": 99}]).status_code == 423
    assert admin.post("/api/bilans/2026/40/reopen", {"reason": ""}).status_code == 422
    r = admin.post("/api/bilans/2026/40/reopen", {"reason": "Erreur de saisie signalée par le commandant"}).json()
    assert r["status"] == "rouvert"
    r = save(admin, [{"indicator_id": 1, "value": 42}]).json()
    v2 = admin.post("/api/bilans/2026/40/validate", {"revision": r["revision"], "confirm": True}).json()
    assert v2["current_version"] == 2
    versions = admin.get("/api/bilans/2026/40/versions").json()["versions"]
    assert [x["version_no"] for x in versions] == [2, 1] and all(x["integrity_ok"] for x in versions)
    assert versions[1]["snapshot"]["rows"][0]["value"] == rows[0]["value"]
    hist = admin.get("/api/bilans/2026/40/history").json()["changes"]
    assert any(h["old"] == str(rows[0]["value"]) and h["new"] == "42" for h in hist)
    with dbmod.session_scope() as db:
        acts = [a.action for a in db.query(AuditLog)]
    assert {"bilan.validated", "bilan.reopened"} <= set(acts)


def test_versions_immuables_en_base(admin: Api):
    b = save(admin, full_rows(admin)).json()
    admin.post("/api/bilans/2026/40/validate", {"revision": b["revision"], "confirm": True})
    with pytest.raises(Exception):
        with dbmod.engine().begin() as conn:
            conn.execute(text("UPDATE bilan_versions SET snapshot = '{}'::jsonb"))
    with pytest.raises(Exception):
        with dbmod.engine().begin() as conn:
            conn.execute(text("DELETE FROM audit_log"))


def test_restauration_et_abandon_de_reouverture(admin: Api):
    rows = full_rows(admin)
    b = save(admin, rows).json()
    admin.post("/api/bilans/2026/40/validate", {"revision": b["revision"], "confirm": True})
    admin.post("/api/bilans/2026/40/reopen", {"reason": "Correction demandée"})
    save(admin, [{"indicator_id": 1, "value": 77}])
    restored = admin.delete("/api/bilans/2026/40/draft").json()
    assert restored["status"] == "valide" and restored["rows"][0]["value"] == rows[0]["value"]


def test_effacer_un_brouillon_et_reprendre_la_semaine_precedente(admin: Api):
    prev = full_rows(admin, 2026, 39)
    prev[0]["observation"] = "Contrôle de nuit"
    b = save(admin, prev, y=2026, w=39).json()
    admin.post("/api/bilans/2026/39/validate", {"revision": b["revision"], "confirm": True})
    r = admin.post("/api/bilans/2026/40/copy-previous", {"revision": 0, "with_observations": False}).json()
    assert r["rows"][0]["value"] == prev[0]["value"] and r["rows"][0]["observation"] == ""
    assert r["rows"][0]["source"]["kind"] == "reprise"
    chk = admin.get("/api/bilans/2026/40/check").json()
    assert chk["duplicates"], "une copie intégrale doit être signalée comme doublon potentiel"
    cleared = admin.delete("/api/bilans/2026/40/draft").json()
    assert cleared["status"] == "nouveau" and all(x["value"] is None for x in cleared["rows"])


def test_avertissements_valeurs_inhabituelles(admin: Api):
    admin.patch("/api/admin/indicators/7", {"warn_max": 10})
    b39 = save(admin, [{"indicator_id": 2, "value": 2}], y=2026, w=39).json()
    admin.post("/api/bilans/2026/39/validate", {"revision": b39["revision"], "confirm": True})
    save(admin, [{"indicator_id": 7, "value": 15}, {"indicator_id": 2, "value": 30}])
    chk = admin.get("/api/bilans/2026/40/check").json()
    kinds = {(w["indicator_id"], w["kind"]) for w in chk["warnings"]}
    assert (7, "seuil") in kinds and (2, "variation") in kinds
    assert chk["can_validate"] is True  # avertissements non bloquants


def test_indicateur_desactive_conserve_historique(admin: Api):
    b = save(admin, full_rows(admin)).json()
    admin.post("/api/bilans/2026/40/validate", {"revision": b["revision"], "confirm": True})
    admin.patch("/api/admin/indicators/11", {"is_active": False, "label": "Contrefaçons (ancien)"})
    old = admin.get("/api/bilans/2026/40").json()
    assert any(r["indicator_id"] == 11 and r["label"] == "Contrefaçons" for r in old["rows"])
    new = admin.get("/api/bilans/2026/41").json()
    assert all(r["indicator_id"] != 11 for r in new["rows"]) and len(new["rows"]) == 13


def test_ajout_et_reordonnancement_indicateur(admin: Api):
    r = admin.post("/api/admin/indicators", {"label": "Indicateur de test"}).json()
    ids = [i["id"] for i in admin.get("/api/indicators").json()["indicators"]]
    assert ids[-1] == r["id"]
    new_order = [r["id"]] + ids[:-1]
    assert admin.post("/api/admin/indicators/reorder", {"ids": new_order}).status_code == 200
    rows = admin.get("/api/bilans/2026/41").json()["rows"]
    assert rows[0]["label"] == "Indicateur de test"
