"""Phase 1 — Programme Officer screening RBAC.

Locks in the fix to ``ProgramOfficerAccessMixin.allowed_roles`` so the
``programme_officer`` role can reach the PO-screen endpoint. Prior to this
fix the dedicated SRS §5.1.1.2/3 actor was 403'd; only Grants Manager /
Program Manager / Program Director could screen.

Also guards the seed contract: the four Phase 1 persona emails must be
present in ``seed_rims.DEMO_USER_IDENTITIES`` so the directory enrichment
pass picks them up.
"""
from __future__ import annotations

from datetime import timedelta

import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils import timezone

from apps.core.permissions.roles import UserRole
from apps.rims.grants.management.commands.seed_rims import DEMO_USER_IDENTITIES
from apps.rims.grants.models import Application, GrantCall
from apps.rims.grants.services import ProgramOfficerDecision, submit_application

User = get_user_model()


def _user(role: str, email: str) -> "User":
    user = User.objects.create_user(email=email, password="pw-phase1")
    user.role = role
    user.save()
    return user


@pytest.fixture
def programme_officer_user(db):
    return _user(UserRole.PROGRAMME_OFFICER, "po.phase1@example.com")


@pytest.fixture
def submitted_application(applicant_user, institution):
    """Application sitting at SUBMITTED with eligibility_passed=True — the
    only state in which ``program_officer_advance`` is willing to act.

    Achieved by submitting against a call with no blocking eligibility rules
    and ``programme_officer_screening_enabled=True`` so auto_screen_eligibility
    leaves the app at SUBMITTED awaiting PO action.
    """
    call = GrantCall.objects.create(
        title="Phase 1 PO screening",
        slug=f"phase1-po-{int(timezone.now().timestamp() * 1000) % 10_000_000}",
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=10),
        status=GrantCall.Status.PUBLISHED,
        programme_officer_screening_enabled=True,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    submit_application(app)
    # FSMField cannot be set via refresh_from_db; reload from DB instead.
    app = Application.objects.get(pk=app.pk)
    assert app.status == Application.Status.SUBMITTED
    assert app.eligibility_passed is True
    return app


@pytest.mark.django_db
def test_programme_officer_can_reach_po_screen(client, programme_officer_user, submitted_application):
    """SRS FRFA-AM014 — the dedicated PROGRAMME_OFFICER role is allowed to
    reach the screening endpoint. Pre-fix this returned 403 because the
    mixin's allowed_roles set did not include the role.
    """
    client.force_login(programme_officer_user)
    url = reverse("rims_grants:application_po_screen", kwargs={"pk": submitted_application.pk})
    resp = client.post(url, data={"decision": ProgramOfficerDecision.ELIGIBLE_READY})
    # Redirect on success regardless of detail-page outcome; gate passed.
    assert resp.status_code in (200, 302), (
        f"Expected PROGRAMME_OFFICER to clear the gate, got {resp.status_code}."
    )


@pytest.mark.django_db
def test_grants_manager_still_reaches_po_screen(client, grants_manager_user, submitted_application):
    """Regression — widening the mixin must not drop the existing tier."""
    client.force_login(grants_manager_user)
    url = reverse("rims_grants:application_po_screen", kwargs={"pk": submitted_application.pk})
    resp = client.post(url, data={"decision": ProgramOfficerDecision.ELIGIBLE_READY})
    assert resp.status_code in (200, 302)


@pytest.mark.django_db
def test_applicant_blocked_from_po_screen(client, applicant_user, submitted_application):
    """Applicants must not be able to screen — RBAC negative case."""
    client.force_login(applicant_user)
    url = reverse("rims_grants:application_po_screen", kwargs={"pk": submitted_application.pk})
    resp = client.post(url, data={"decision": ProgramOfficerDecision.ELIGIBLE_READY})
    assert resp.status_code == 403


@pytest.mark.django_db
def test_seed_rims_registers_four_phase1_personas():
    """Guard the seed contract — the four SRS §5.1/§5.4 personas missing
    from the pre-Phase-1 set must be in ``DEMO_USER_IDENTITIES`` so the
    seeder's directory enrichment pass populates first_name / last_name /
    phone / bio on each.
    """
    expected = {
        "finance.manager@demo.local",
        "program.coordinator@demo.local",
        "project.manager@demo.local",
        "programme.officer@demo.local",
    }
    missing = expected - set(DEMO_USER_IDENTITIES)
    assert not missing, f"Phase 1 personas missing from DEMO_USER_IDENTITIES: {sorted(missing)}"
