"""Phase 8 — Donor + Learning report PDF/XLSX exports (FRSME-MEI020/021).

Until Phase 8 the reports rendered HTML only; Phase 8 wires the existing
``apps.mel.reports.services`` xlsx/pdf scaffolding through to the SME-Hub
donor + learning report views via ``?format=xlsx`` / ``?format=pdf``.
"""
from __future__ import annotations

from io import BytesIO
from unittest.mock import patch

import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
from openpyxl import load_workbook

from apps.core.audit.models import AuditLog
from apps.core.permissions.roles import UserRole

User = get_user_model()
pytestmark = pytest.mark.django_db


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------

@pytest.fixture
def mel_officer():
    return User.objects.create_user(
        email="mel-rx8@example.com", password="x", role=UserRole.MEL_OFFICER,
    )


@pytest.fixture
def entrepreneur():
    return User.objects.create_user(
        email="ent-rx8@example.com", password="x", role=UserRole.ENTREPRENEUR,
    )


def _donor_url(fmt: str | None = None) -> str:
    base = reverse("mel_reports:smehub_donor")
    return f"{base}?format={fmt}" if fmt else base


def _learning_url(fmt: str | None = None) -> str:
    base = reverse("mel_reports:smehub_learning")
    return f"{base}?format={fmt}" if fmt else base


# ---------------------------------------------------------------------------
# Renderer unit tests — operate directly on dicts (no view layer)
# ---------------------------------------------------------------------------

def test_render_smehub_donor_xlsx_returns_workbook_with_expected_sheets():
    from apps.mel.reports.smehub_renderers import render_smehub_donor_xlsx

    sample = {
        "period_start": "2026-01-01",
        "period_end": "2026-04-30",
        "by_sector": [{"sector": "Agriculture", "funding_total": 1000.0, "count": 2}],
        "by_cohort": [{"cohort_id": 7, "funding_total": 500.0, "count": 1}],
        "by_aih": [{"aih": "Makerere", "funding_total": 1000.0, "count": 2}],
        "totals": {"funding_total": 1500.0, "applications_funded": 3, "manual_records": 0, "manual_total": 0},
        "rims_links": [],
        "rep_links": [],
        "repository_links": [],
    }
    raw = render_smehub_donor_xlsx(sample)
    wb = load_workbook(BytesIO(raw))
    assert {"Totals", "By sector", "By cohort", "By AIH", "RIMS links", "REP links", "Repository links"} <= set(wb.sheetnames)
    assert wb["By sector"].cell(2, 1).value == "Agriculture"
    assert wb["By sector"].cell(2, 2).value == 1000.0


def test_render_smehub_learning_xlsx_has_sector_performance_sheet():
    from apps.mel.reports.smehub_renderers import render_smehub_learning_xlsx

    sample = {
        "sector_performance": [
            {"sector": "Agriculture", "records": 5, "sp2": 3, "sp3": 2, "sp4": 1, "sp5": 0},
        ],
        "aih_completion": [{"aih": "Makerere", "members": 4, "completed": 3, "completion_pct": 75.0}],
        "partnership_to_deal": {"partnerships": 5, "deals": 3, "conversion_pct": 60.0},
        "dropout_reasons": [{"reason": "personal", "count": 2}],
        "rims_links": [],
        "rep_links": [],
        "repository_links": [],
    }
    raw = render_smehub_learning_xlsx(sample)
    wb = load_workbook(BytesIO(raw))
    assert "Sector performance" in wb.sheetnames
    assert wb["Sector performance"].cell(2, 1).value == "Agriculture"
    assert wb["Sector performance"].cell(2, 2).value == 5
    assert wb["Partnership → deal"].cell(2, 2).value == 5


def test_render_smehub_donor_pdf_invokes_render_pdf_from_html():
    """We can't run WeasyPrint in CI deps reliably — assert the contract:
    our renderer pipes the rendered template through the canonical helper.
    """
    from apps.mel.reports import smehub_renderers

    with patch.object(
        smehub_renderers, "render_to_string", return_value="<html><body>x</body></html>",
    ) as render_html, patch(
        "apps.core.utils.pdf.render_pdf_from_html", return_value=b"%PDF-FAKE",
    ) as render_pdf:
        out = smehub_renderers.render_smehub_donor_pdf({})
    render_html.assert_called_once()
    render_pdf.assert_called_once()
    assert out == b"%PDF-FAKE"


def test_render_smehub_learning_pdf_invokes_render_pdf_from_html():
    from apps.mel.reports import smehub_renderers

    with patch.object(
        smehub_renderers, "render_to_string", return_value="<html><body>y</body></html>",
    ), patch(
        "apps.core.utils.pdf.render_pdf_from_html", return_value=b"%PDF-LEARN",
    ) as render_pdf:
        out = smehub_renderers.render_smehub_learning_pdf({})
    render_pdf.assert_called_once()
    assert out == b"%PDF-LEARN"


# ---------------------------------------------------------------------------
# View tests — full flow
# ---------------------------------------------------------------------------

def test_donor_xlsx_export_for_mel_officer(client, mel_officer):
    client.force_login(mel_officer)
    resp = client.get(_donor_url("xlsx"))
    assert resp.status_code == 200
    assert resp["Content-Type"].startswith("application/vnd.openxmlformats")
    assert "smehub-donor-report.xlsx" in resp["Content-Disposition"]
    wb = load_workbook(BytesIO(resp.content))
    assert "By sector" in wb.sheetnames


def test_donor_pdf_export_for_mel_officer(client, mel_officer):
    client.force_login(mel_officer)
    with patch(
        "apps.core.utils.pdf.render_pdf_from_html", return_value=b"%PDF-DONOR",
    ):
        resp = client.get(_donor_url("pdf"))
    assert resp.status_code == 200
    assert resp["Content-Type"] == "application/pdf"
    assert "smehub-donor-report.pdf" in resp["Content-Disposition"]


def test_learning_xlsx_export_for_mel_officer(client, mel_officer):
    client.force_login(mel_officer)
    resp = client.get(_learning_url("xlsx"))
    assert resp.status_code == 200
    wb = load_workbook(BytesIO(resp.content))
    assert "Sector performance" in wb.sheetnames


def test_learning_pdf_export_for_mel_officer(client, mel_officer):
    client.force_login(mel_officer)
    with patch(
        "apps.core.utils.pdf.render_pdf_from_html", return_value=b"%PDF-LEARN",
    ):
        resp = client.get(_learning_url("pdf"))
    assert resp.status_code == 200
    assert resp["Content-Type"] == "application/pdf"


def test_export_audit_log_records_format(client, mel_officer):
    client.force_login(mel_officer)
    client.get(_donor_url("xlsx"))
    rows = AuditLog.objects.filter(target_model="SMEHubDonorReport")
    assert rows.exists()
    assert rows.filter(changes__format="xlsx").exists()


def test_html_default_audit_log_records_html_format(client, mel_officer):
    client.force_login(mel_officer)
    resp = client.get(_donor_url())
    assert resp.status_code == 200
    rows = AuditLog.objects.filter(target_model="SMEHubDonorReport", changes__format="html")
    assert rows.exists()


def test_export_blocks_anonymous(client):
    resp = client.get(_donor_url("xlsx"))
    assert resp.status_code in (302, 403)


def test_export_blocks_entrepreneur(client, entrepreneur):
    client.force_login(entrepreneur)
    resp = client.get(_donor_url("xlsx"))
    assert resp.status_code == 403
    resp = client.get(_learning_url("xlsx"))
    assert resp.status_code == 403


def test_donor_xlsx_propagates_period_query(client, mel_officer):
    """Ensures the period filter doesn't get dropped in the export path."""
    client.force_login(mel_officer)
    resp = client.get(reverse("mel_reports:smehub_donor") + "?format=xlsx&period_start=2026-01-01&period_end=2026-04-30")
    assert resp.status_code == 200
    rows = AuditLog.objects.filter(
        target_model="SMEHubDonorReport", changes__format="xlsx",
    )
    assert rows.filter(changes__period_start="2026-01-01").exists()
