"""PRD §4.3.4 — Scholar lifecycle FSM tests."""
from __future__ import annotations

import pytest
from django.core.exceptions import ValidationError

from apps.core.audit.models import AuditLog
from apps.rims.scholarships.models import GraduationRecord, Scholar, Scholarship
from apps.rims.scholarships.workflows import transition_scholar


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


@pytest.mark.django_db
def test_default_status_is_enrolled(scholar):
    assert scholar.status == Scholar.Status.ENROLLED


@pytest.mark.django_db
def test_valid_transition_persists_and_audits(scholar, grants_manager_user):
    transition_scholar(scholar, to_status=Scholar.Status.ACTIVE, actor=grants_manager_user)
    scholar.refresh_from_db()
    assert scholar.status == Scholar.Status.ACTIVE
    assert scholar.status_changed_at is not None
    assert scholar.status_changed_by == grants_manager_user
    assert AuditLog.objects.filter(
        target_app="rims_scholarships",
        target_model="Scholar",
        object_id=str(scholar.pk),
        action="SCHOLAR_ACTIVE",
    ).exists()


@pytest.mark.django_db
def test_invalid_transition_raises(scholar):
    with pytest.raises(ValidationError):
        transition_scholar(scholar, to_status=Scholar.Status.COMPLETED)


@pytest.mark.django_db
def test_withdrawal_requires_reason(scholar):
    transition_scholar(scholar, to_status=Scholar.Status.ACTIVE)
    scholar.refresh_from_db()
    with pytest.raises(ValidationError):
        transition_scholar(scholar, to_status=Scholar.Status.WITHDRAWN)


@pytest.mark.django_db
def test_withdrawal_records_reason(scholar):
    transition_scholar(scholar, to_status=Scholar.Status.ACTIVE)
    scholar.refresh_from_db()
    transition_scholar(
        scholar, to_status=Scholar.Status.WITHDRAWN, reason="Health-related deferral"
    )
    scholar.refresh_from_db()
    assert scholar.status == Scholar.Status.WITHDRAWN
    assert scholar.withdrawn_reason == "Health-related deferral"


@pytest.mark.django_db
def test_completed_is_terminal(scholar):
    transition_scholar(scholar, to_status=Scholar.Status.ACTIVE)
    scholar.refresh_from_db()
    transition_scholar(scholar, to_status=Scholar.Status.COMPLETED)
    scholar.refresh_from_db()
    with pytest.raises(ValidationError):
        transition_scholar(scholar, to_status=Scholar.Status.ACTIVE)


@pytest.mark.django_db
def test_reactivation_from_withdrawn_clears_reason(scholar):
    transition_scholar(scholar, to_status=Scholar.Status.ACTIVE)
    scholar.refresh_from_db()
    transition_scholar(
        scholar, to_status=Scholar.Status.WITHDRAWN, reason="Personal leave"
    )
    scholar.refresh_from_db()
    assert scholar.withdrawn_reason == "Personal leave"
    transition_scholar(scholar, to_status=Scholar.Status.ACTIVE)
    scholar.refresh_from_db()
    assert scholar.status == Scholar.Status.ACTIVE
    assert scholar.withdrawn_reason == ""


@pytest.mark.django_db
def test_graduation_signal_completes_scholar_without_state_guard(scholar):
    """Graduation should complete the scholar even if they were never moved to ACTIVE."""
    # Scholar still in ENROLLED (which transition_scholar would refuse → COMPLETED).
    # The signal handler bypasses the guard intentionally.
    GraduationRecord.objects.create(scholar=scholar, graduated_on="2026-05-01")
    scholar.refresh_from_db()
    assert scholar.status == Scholar.Status.COMPLETED
    assert AuditLog.objects.filter(
        action="SCHOLAR_COMPLETED",
        object_id=str(scholar.pk),
    ).exists()
