"""PRD §5.1 FRFA-AM009 — structured eligibility trace persisted on submit.

Each rule the call defines must produce one row in
``Application.eligibility_trace`` with rule_id, rule_label, expected,
actual, passed, evaluated_at, reason. The legacy free-text
``eligibility_notes`` field is composed from this trace.
"""

from __future__ import annotations

from datetime import timedelta

import pytest
from django.utils import timezone

from apps.rims.grants.models import Application, EligibilityRule, GrantCall
from apps.rims.grants.services import submit_application


@pytest.fixture
def applicant_with_profile(db, applicant_user):
    profile = getattr(applicant_user, "profile", None)
    if profile is not None:
        profile.country = "Uganda"
        profile.degree_level = "masters"
        profile.save()
    return applicant_user


def _call(**overrides) -> GrantCall:
    base = {
        "title": "Telemetry call",
        "slug": "telemetry-call",
        "opens_at": timezone.now() - timedelta(days=1),
        "closes_at": timezone.now() + timedelta(days=14),
        "status": GrantCall.Status.PUBLISHED,
    }
    base.update(overrides)
    return GrantCall.objects.create(**base)


@pytest.mark.django_db
def test_passing_rules_produce_passed_trace_rows(applicant_with_profile, institution):
    """FRFA-AM009 — every rule produces a trace row with passed=True when satisfied."""
    call = _call()
    EligibilityRule.objects.create(
        call=call, rule_type=EligibilityRule.RuleType.NATIONALITY,
        params={"countries": ["Uganda", "Kenya"]},
    )
    app = Application.objects.create(
        call=call, applicant=applicant_with_profile, institution=institution
    )
    submit_application(app)
    app = Application.objects.get(pk=app.pk)

    assert isinstance(app.eligibility_trace, list)
    assert len(app.eligibility_trace) == 1
    row = app.eligibility_trace[0]
    assert row["rule_type"] == "nationality"
    assert row["passed"] is True
    assert row["expected"]
    assert row["actual"]
    assert row["evaluated_at"]
    assert row["reason"] == ""
    assert app.eligibility_passed is True


@pytest.mark.django_db
def test_failing_rule_produces_failed_trace_row_and_reason(
    applicant_with_profile, institution
):
    """FRFA-AM009 — failed rules carry a human-readable reason in the trace.

    Eligibility is advisory: a failing rule *flags* the application
    (eligibility_passed=False) but does NOT reject it — it still enters the
    review queue so a human can screen the supporting documents.
    """
    call = _call(slug="telemetry-call-fail")
    EligibilityRule.objects.create(
        call=call, rule_type=EligibilityRule.RuleType.NATIONALITY,
        params={"countries": ["Zimbabwe", "Nigeria"]},  # applicant is Uganda
    )
    app = Application.objects.create(
        call=call, applicant=applicant_with_profile, institution=institution
    )
    submit_application(app)
    app = Application.objects.get(pk=app.pk)

    assert app.eligibility_passed is False
    # Flagged, not rejected — routed into the normal review queue.
    assert app.status == Application.Status.UNDER_REVIEW
    row = app.eligibility_trace[0]
    assert row["passed"] is False
    assert row["reason"]
    assert "nationality" in row["reason"].lower() or "nationality" in row["rule_label"].lower()
    # Legacy notes string still populated for back-compat, framed as a flag.
    assert "nationality" in app.eligibility_notes.lower()
    assert "flagged" in app.eligibility_notes.lower()


@pytest.mark.django_db
def test_no_rules_means_empty_trace_but_still_passes(
    applicant_with_profile, institution
):
    """When no blocking rules exist, the trace is empty and the app still passes."""
    call = _call(slug="telemetry-call-norules")
    app = Application.objects.create(
        call=call, applicant=applicant_with_profile, institution=institution
    )
    submit_application(app)
    app = Application.objects.get(pk=app.pk)
    assert app.eligibility_trace == []
    assert app.eligibility_passed is True


@pytest.mark.django_db
def test_multiple_rules_produce_one_row_each(applicant_with_profile, institution):
    """Order is preserved (by rule pk) and the count matches blocking rules."""
    call = _call(slug="telemetry-call-multi")
    EligibilityRule.objects.create(
        call=call, rule_type=EligibilityRule.RuleType.NATIONALITY,
        params={"countries": ["Uganda", "Kenya"]},
    )
    EligibilityRule.objects.create(
        call=call, rule_type=EligibilityRule.RuleType.DEGREE,
        params={"levels": ["masters", "phd"]},
    )
    app = Application.objects.create(
        call=call, applicant=applicant_with_profile, institution=institution
    )
    submit_application(app)
    app = Application.objects.get(pk=app.pk)
    assert len(app.eligibility_trace) == 2
    types = [r["rule_type"] for r in app.eligibility_trace]
    assert types == ["nationality", "degree"]
