"""Endpoint tests for GET /api/engagements/<eid>/export."""
from __future__ import annotations

from datetime import date

from flask.testing import FlaskClient

from backend.app.extensions import db
from backend.app.models import Engagement, EngagementStatus, User
from backend.app.models.simulation import Simulation, SimulationStatus
from backend.app.services.export import _export_filename
from backend.tests.conftest import auth_headers as _h

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_engagement(client: FlaskClient, token: str, name: str = "Op Alpha") -> dict:
    resp = client.post(
        "/api/engagements",
        headers=_h(token),
        json={"name": name, "start_date": "2026-06-01"},
    )
    assert resp.status_code == 201, resp.get_json()
    return resp.get_json()


def _make_sim(client: FlaskClient, token: str, eid: int, name: str = "Sim One") -> dict:
    resp = client.post(
        f"/api/engagements/{eid}/simulations",
        headers=_h(token),
        json={"name": name},
    )
    assert resp.status_code == 201, resp.get_json()
    return resp.get_json()


def _export(client: FlaskClient, token: str, eid: int, fmt: str):
    return client.get(
        f"/api/engagements/{eid}/export?format={fmt}",
        headers=_h(token),
    )


# ---------------------------------------------------------------------------
# RBAC
# ---------------------------------------------------------------------------


def test_export_markdown_admin_returns_md_with_engagement_header_and_all_simulations(
    client: FlaskClient, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token)
    _make_sim(client, admin_token, eng["id"], "Lateral Movement")
    _make_sim(client, admin_token, eng["id"], "Persistence Check")

    resp = _export(client, admin_token, eng["id"], "md")
    assert resp.status_code == 200
    assert "text/markdown" in resp.content_type
    body = resp.data.decode()
    assert "Op Alpha" in body
    # Both simulation names appear as cells in the 7-column table
    assert "Lateral Movement" in body
    assert "Persistence Check" in body
    # Table uses French column headers
    assert "Scénario" in body


def test_export_markdown_redteam_ok(
    client: FlaskClient, redteam_token: str
) -> None:
    eng = _make_engagement(client, redteam_token)
    resp = _export(client, redteam_token, eng["id"], "md")
    assert resp.status_code == 200


def test_export_markdown_soc_403(
    client: FlaskClient, soc_token: str, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token)
    resp = _export(client, soc_token, eng["id"], "md")
    assert resp.status_code == 403


def test_export_unauthenticated_401(client: FlaskClient, admin_token: str) -> None:
    eng = _make_engagement(client, admin_token)
    resp = client.get(f"/api/engagements/{eng['id']}/export?format=md")
    assert resp.status_code == 401


# ---------------------------------------------------------------------------
# CSV
# ---------------------------------------------------------------------------


def test_export_csv_returns_csv_with_one_row_per_simulation(
    client: FlaskClient, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token)
    _make_sim(client, admin_token, eng["id"], "S1")
    _make_sim(client, admin_token, eng["id"], "S2")

    resp = _export(client, admin_token, eng["id"], "csv")
    assert resp.status_code == 200
    assert "text/csv" in resp.content_type

    import csv as csv_mod
    import io

    rows = list(csv_mod.reader(io.StringIO(resp.data.decode())))
    # 1 header + 2 simulations
    assert len(rows) == 3


def test_export_csv_columns_match_contract(
    client: FlaskClient, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token)
    resp = _export(client, admin_token, eng["id"], "csv")
    assert resp.status_code == 200

    import csv as csv_mod
    import io

    rows = list(csv_mod.reader(io.StringIO(resp.data.decode())))
    expected_headers = [
        "Scénario",
        "Test",
        "Source de log",
        "Commentaires SOC",
        "Exécution",
        "Logs remontés au SIEM",
        "Cyber incident",
    ]
    assert rows[0] == expected_headers


def test_export_csv_escapes_special_characters(
    client: FlaskClient, admin_token: str, app
) -> None:
    eng = _make_engagement(client, admin_token)

    with app.app_context():
        admin = User.query.filter_by(username="admin1").first()
        sim = Simulation(
            engagement_id=eng["id"],
            name='Sim "quoted"',
            commands='cmd1, cmd2\nnewline "here"',
            status=SimulationStatus.PENDING,
            created_by_id=admin.id,
        )
        db.session.add(sim)
        db.session.commit()

    resp = _export(client, admin_token, eng["id"], "csv")
    assert resp.status_code == 200
    body = resp.data.decode()
    # csv.writer must have quoted the fields — no raw unquoted double-quotes breaking rows
    import csv as csv_mod
    import io

    rows = list(csv_mod.reader(io.StringIO(body)))
    assert len(rows) == 2  # header + 1 sim
    name_col = rows[1][0]  # col 0 = Scénario
    assert "quoted" in name_col


# ---------------------------------------------------------------------------
# PDF
# ---------------------------------------------------------------------------


def test_export_pdf_returns_pdf_magic_bytes_and_non_empty(
    client: FlaskClient, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token)
    _make_sim(client, admin_token, eng["id"], "S1")

    resp = _export(client, admin_token, eng["id"], "pdf")
    assert resp.status_code == 200
    assert resp.content_type == "application/pdf"
    assert resp.data[:4] == b"%PDF"
    assert len(resp.data) > 1024


# ---------------------------------------------------------------------------
# 400 / 404
# ---------------------------------------------------------------------------


def test_export_unknown_format_400(
    client: FlaskClient, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token)
    resp = client.get(
        f"/api/engagements/{eng['id']}/export?format=xml",
        headers=_h(admin_token),
    )
    assert resp.status_code == 400
    assert "format must be one of" in resp.get_json()["error"]


def test_export_missing_format_400(
    client: FlaskClient, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token)
    resp = client.get(
        f"/api/engagements/{eng['id']}/export",
        headers=_h(admin_token),
    )
    assert resp.status_code == 400
    assert "format must be one of" in resp.get_json()["error"]


def test_export_unknown_engagement_404(
    client: FlaskClient, admin_token: str
) -> None:
    resp = client.get(
        "/api/engagements/99999/export?format=md",
        headers=_h(admin_token),
    )
    assert resp.status_code == 404


# ---------------------------------------------------------------------------
# Edge cases
# ---------------------------------------------------------------------------


def test_export_engagement_with_zero_simulations_renders_header_only(
    client: FlaskClient, admin_token: str
) -> None:
    eng = _make_engagement(client, admin_token, "Empty Engagement")

    resp_md = _export(client, admin_token, eng["id"], "md")
    assert resp_md.status_code == 200
    assert "Empty Engagement" in resp_md.data.decode()

    resp_csv = _export(client, admin_token, eng["id"], "csv")
    assert resp_csv.status_code == 200
    import csv as csv_mod
    import io

    rows = list(csv_mod.reader(io.StringIO(resp_csv.data.decode())))
    assert len(rows) == 1  # header only

    resp_pdf = _export(client, admin_token, eng["id"], "pdf")
    assert resp_pdf.status_code == 200
    assert resp_pdf.data[:4] == b"%PDF"


def test_export_filename_slugifies_name_and_carries_date(app, admin_user: User) -> None:
    with app.app_context():
        eng = Engagement(
            name="Opération Spéciale!",
            start_date=date(2026, 6, 1),
            status=EngagementStatus.PLANNED,
            created_by_id=admin_user.id,
        )
        db.session.add(eng)
        db.session.commit()

        fname = _export_filename(eng, "md")
        from datetime import date as _date

        today = _date.today().strftime("%Y%m%d")
        assert fname.startswith(f"engagement-{eng.id}-")
        assert "operation-speciale" in fname
        assert fname.endswith(f"-{today}.md")
