"""Document final : une page A4, dépassement signalé, rien de tronqué, exports et nombre de pages."""
import io
import zipfile

import pdfplumber
from openpyxl import load_workbook

from edcf.documents.exports import neutralize
from tests.conftest import Api, full_rows

LONG = ("Contrôle coordonné sur la RN 67 avec plusieurs interpellations, procédures en cours de rédaction "
        "et rapprochements à effectuer avec les unités voisines. ") * 4


def prepare(api: Api, observations: dict[int, str] | None = None, validate=True):
    rows = full_rows(api)
    for r in rows:
        r["observation"] = (observations or {}).get(r["indicator_id"], "")
    b = api.put("/api/bilans/2026/40/draft", {"revision": 0, "rows": rows}).json()
    if validate:
        api.post("/api/bilans/2026/40/validate", {"revision": b["revision"], "confirm": True})


def pdf_text(content: bytes) -> tuple[int, str]:
    with pdfplumber.open(io.BytesIO(content)) as pdf:
        return len(pdf.pages), "\n".join(p.extract_text() or "" for p in pdf.pages)


def test_bilan_standard_sur_une_page_a4(admin: Api):
    prepare(admin)
    lay = admin.get("/api/bilans/2026/40/document/layout").json()
    assert lay["pages"] == 1 and lay["fits"] and lay["level"] == "standard"
    r = admin.get("/api/bilans/2026/40/document/export.pdf")
    assert r.status_code == 200 and r.headers["x-edcf-pages"] == "1"
    pages, txt = pdf_text(r.content)
    assert pages == 1
    assert "SEMAINE 40" in txt and "28/09/2026" in txt and "04/10/2026" in txt
    assert "INFRACTIONS RELEVÉES" in txt and "Enquêtes judiciaires" in txt


def test_observations_courtes_tiennent_sur_une_page(admin: Api):
    prepare(admin, {i: f"Observation courte n°{i}." for i in range(1, 15)})
    r = admin.get("/api/bilans/2026/40/document/export.pdf")
    assert pdf_text(r.content)[0] == 1


def test_depassement_detecte_sans_troncature(admin: Api):
    obs = {i: LONG + f" Fin-{i}." for i in (1, 2, 4, 7, 13, 14)}
    prepare(admin, obs)
    lay = admin.get("/api/bilans/2026/40/document/layout").json()
    assert lay["pages"] >= 2 and not lay["fits"]
    assert lay["culprits"] and lay["suggestions"]
    assert lay["font_pt"] >= 9.5, "le texte ne descend jamais sous le plancher lisible"
    r = admin.get("/api/bilans/2026/40/document/export.pdf")
    pages, txt = pdf_text(r.content)
    assert pages == lay["pages"]
    for i in (1, 2, 4, 7, 13, 14):
        assert f"Fin-{i}." in txt.replace("\n", " "), "aucune observation n'est tronquée"


def test_ajustement_progressif_puis_paysage(admin: Api):
    prepare(admin, {i: LONG[:260] for i in (1, 2, 3, 4)})
    lay = admin.get("/api/bilans/2026/40/document/layout").json()
    assert lay["pages"] == 1 and lay["level"] != "standard" and lay["steps"]
    portrait = admin.get("/api/bilans/2026/40/document/layout?paper=A4&orientation=paysage&fit=true").json()
    assert portrait["page"]["width"] == 297


def test_apercu_html_fidele_et_sans_script(admin: Api):
    prepare(admin, {1: "<script>alert(1)</script>"})
    r = admin.get("/api/bilans/2026/40/document")
    assert r.status_code == 200
    assert "<script>" not in r.text and "&lt;script&gt;" in r.text
    assert "script-src" not in r.headers["content-security-policy"]
    assert 'name="edcf-pages" content="1"' in r.text
    png = admin.get("/api/bilans/2026/40/document/page/1.png")
    assert png.status_code == 200 and png.content[:4] == b"\x89PNG"


def test_brouillon_marque_comme_non_valide(admin: Api):
    prepare(admin, validate=False)
    r = admin.get("/api/bilans/2026/40/document/export.pdf")
    assert "NON VALIDÉ" in pdf_text(r.content)[1]
    assert "BROUILLON" in r.headers["content-disposition"]


def test_exports_xlsx_ods_csv_et_injection_de_formules(admin: Api):
    prepare(admin, {2: "=HYPERLINK(\"http://x\")", 3: "+33 6 00", 5: "@SUM(A1)"})
    x = admin.get("/api/bilans/2026/40/document/export.xlsx")
    wb = load_workbook(io.BytesIO(x.content))
    ws = wb.worksheets[0]
    values = {ws.cell(row=r, column=1).value: (ws.cell(row=r, column=2).value, ws.cell(row=r, column=3).value)
              for r in range(7, 21)}
    assert values["Alcoolémie délictuelle"][0] == 0 and isinstance(values["Vitesse (+ 50 km/h)"][0], int)
    assert values["Alcoolémie contraventionnelle"][1].startswith("'=")
    assert "Métadonnées" in wb.sheetnames
    o = admin.get("/api/bilans/2026/40/document/export.ods")
    z = zipfile.ZipFile(io.BytesIO(o.content))
    assert z.namelist()[0] == "mimetype" and z.read("mimetype") == b"application/vnd.oasis.opendocument.spreadsheet"
    content = z.read("content.xml").decode()
    assert 'office:value-type="float"' in content and "&apos;=HYPERLINK" not in content
    assert "'=HYPERLINK" in content
    c = admin.get("/api/bilans/2026/40/document/export.csv").content.decode("utf-8-sig")
    assert "'=HYPERLINK" in c and "'@SUM" in c
    assert neutralize("-2 contrôles") == "'-2 contrôles" and neutralize("RAS") == "RAS"


def test_export_vide_reste_vide(admin: Api):
    admin.put("/api/bilans/2026/40/draft", {"revision": 0, "rows": [{"indicator_id": 1, "value": None,
                                                                       "observation": "en attente"}]})
    x = admin.get("/api/bilans/2026/40/document/export.xlsx")
    ws = load_workbook(io.BytesIO(x.content)).worksheets[0]
    assert ws.cell(row=7, column=2).value is None


def test_export_lecteur_selon_permission(app):
    from tests.conftest import make_user
    a = Api(app)
    a.user = make_user("gest")
    a.login(a.user)
    prepare(a)
    r = Api(app)
    r.login(make_user("lect.export", "reader", can_export=True))
    assert r.get("/api/bilans/2026/40/document/export.pdf").status_code == 200
