"""PRD §5.3 — scholarship UI tests (first test file in this app).

Covers:
- StipendListView role gating (admin/grants_manager/finance_officer allowed,
  scholar denied).
- ProgressReportSubmitView ownership: only the scholar themselves can submit.
- HTMX scholar search filters correctly.
- Service functions emit audit log entries.
- ProgressReport unique-period enforcement.
"""

from __future__ import annotations

import uuid
from datetime import date, timedelta
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse

from apps.core.audit.models import AuditLog
from apps.core.permissions.roles import UserRole
from apps.rims.scholarships.models import (
    ProgressReport,
    Scholar,
    Scholarship,
    Stipend,
)
from apps.rims.scholarships.services import process_stipend, submit_progress_report

User = get_user_model()


@pytest.fixture
def scholar(db, applicant_user):
    programme = Scholarship.objects.create(name="MSc Climate Resilience")
    return Scholar.objects.create(user=applicant_user, scholarship=programme)


@pytest.fixture
def grants_manager(db):
    u = User.objects.create_user(email=f"gm-{uuid.uuid4().hex[:6]}@x.com", password="x")
    u.role = UserRole.GRANTS_MANAGER
    u.save(update_fields=["role"])
    return u


@pytest.fixture
def finance_officer(db):
    u = User.objects.create_user(email=f"fo-{uuid.uuid4().hex[:6]}@x.com", password="x")
    u.role = UserRole.FINANCE_OFFICER
    u.save(update_fields=["role"])
    return u


@pytest.fixture
def other_scholar(db):
    """Different user with a SCHOLAR role used to test cross-user blocks."""
    u = User.objects.create_user(
        email=f"other-{uuid.uuid4().hex[:6]}@x.com", password="x"
    )
    u.role = UserRole.SCHOLAR
    u.save(update_fields=["role"])
    return u


# --- Service-level checks ---------------------------------------------------

@pytest.mark.django_db(transaction=True)
def test_submit_progress_report_creates_audit_entry(scholar, applicant_user):
    file = SimpleUploadedFile("report.pdf", b"%PDF-1.4 fake", content_type="application/pdf")
    report = submit_progress_report(
        scholar,
        period_label="2026-Q1",
        file=file,
        notes="Mid-year update",
        actor=applicant_user,
    )
    assert report.pk is not None
    assert AuditLog.objects.filter(
        target_app="rims_scholarships",
        target_model="ProgressReport",
        object_id=report.pk,
    ).exists()


@pytest.mark.django_db
def test_submit_progress_report_rejects_duplicate_period(scholar, applicant_user):
    submit_progress_report(
        scholar,
        period_label="2026-Q1",
        file=SimpleUploadedFile("a.pdf", b"x"),
        actor=applicant_user,
    )
    with pytest.raises(ValidationError):
        submit_progress_report(
            scholar,
            period_label="2026-Q1",
            file=SimpleUploadedFile("b.pdf", b"x"),
            actor=applicant_user,
        )


@pytest.mark.django_db(transaction=True)
def test_process_stipend_records_audit(scholar, finance_officer):
    stipend = process_stipend(
        scholar,
        amount=Decimal("500.00"),
        currency="UGX",
        paid_on=date.today(),
        reference="TX-001",
        actor=finance_officer,
    )
    assert stipend.pk is not None
    assert AuditLog.objects.filter(
        target_app="rims_scholarships",
        target_model="Stipend",
        object_id=stipend.pk,
    ).exists()


@pytest.mark.django_db
def test_process_stipend_rejects_zero_amount(scholar, finance_officer):
    with pytest.raises(ValidationError):
        process_stipend(
            scholar,
            amount=Decimal("0"),
            currency="UGX",
            paid_on=date.today(),
            actor=finance_officer,
        )


# --- Stipend list role gating -----------------------------------------------

@pytest.mark.django_db
def test_stipend_list_allows_grants_manager(client, grants_manager):
    client.force_login(grants_manager)
    resp = client.get(reverse("rims_scholarships:stipend_list"))
    assert resp.status_code == 200


@pytest.mark.django_db
def test_stipend_list_allows_finance_officer(client, finance_officer):
    client.force_login(finance_officer)
    resp = client.get(reverse("rims_scholarships:stipend_list"))
    assert resp.status_code == 200


@pytest.mark.django_db
def test_stipend_list_denies_scholar_role(client, applicant_user):
    """SCHOLAR is the role on applicant_user (per conftest)."""
    client.force_login(applicant_user)
    resp = client.get(reverse("rims_scholarships:stipend_list"))
    assert resp.status_code == 403


@pytest.mark.django_db
def test_stipend_list_htmx_returns_partial(client, finance_officer, scholar, finance_user):
    Stipend.objects.create(
        scholar=scholar,
        amount=Decimal("1000"),
        currency="UGX",
        paid_on=date.today(),
        reference="X",
    )
    client.force_login(finance_officer)
    resp = client.get(
        reverse("rims_scholarships:stipend_list"),
        HTTP_HX_REQUEST="true",
    )
    assert resp.status_code == 200
    body = resp.content.decode()
    assert "<html" not in body
    assert "<tr" in body


# --- Progress report ownership ----------------------------------------------

@pytest.mark.django_db
def test_progress_report_submit_only_by_scholar(client, scholar, applicant_user):
    """The scholar themselves can render the submission form."""
    client.force_login(applicant_user)
    resp = client.get(
        reverse("rims_scholarships:progress_report_create", kwargs={"scholar_pk": scholar.pk})
    )
    assert resp.status_code == 200


@pytest.mark.django_db
def test_progress_report_submit_blocked_for_other_user(client, scholar, other_scholar):
    """A different SCHOLAR-role user cannot submit on behalf of someone else."""
    client.force_login(other_scholar)
    resp = client.get(
        reverse("rims_scholarships:progress_report_create", kwargs={"scholar_pk": scholar.pk})
    )
    assert resp.status_code == 403


@pytest.mark.django_db
def test_progress_report_submit_persists_record(client, scholar, applicant_user):
    client.force_login(applicant_user)
    resp = client.post(
        reverse(
            "rims_scholarships:progress_report_create",
            kwargs={"scholar_pk": scholar.pk},
        ),
        {
            "period_label": "2026-Q1",
            "notes": "Hello",
            "file": SimpleUploadedFile("rep.pdf", b"%PDF data", content_type="application/pdf"),
        },
    )
    assert resp.status_code == 302
    assert ProgressReport.objects.filter(scholar=scholar, period_label="2026-Q1").exists()


# --- HTMX scholar search ----------------------------------------------------

@pytest.mark.django_db
def test_scholar_list_htmx_search_filters(client, grants_manager, scholar):
    # A second scholar that should NOT match the q filter below.
    other_user = User.objects.create_user(email="zzz@example.com", password="x")
    Scholar.objects.create(user=other_user, scholarship=scholar.scholarship)

    client.force_login(grants_manager)
    resp = client.get(
        reverse("rims_scholarships:scholar_list") + f"?q={scholar.user.email[:6]}",
        HTTP_HX_REQUEST="true",
    )
    assert resp.status_code == 200
    body = resp.content.decode()
    assert "<html" not in body
    assert scholar.user.email in body
    assert "zzz@example.com" not in body
