"""SRS FRFA-CS012/CS013 gap closure — evaluates the 8 new EligibilityRule
types end-to-end via auto_screen_eligibility(), the same automated-screening
entry point already covered for nationality/degree/institution. Each test
checks both a passing and a failing case so a regression that silently makes
a rule permissive (always True) is caught, not just one that makes it always
block.
"""
import datetime
import uuid
from decimal import Decimal

import pytest
from django.utils import timezone

from apps.rims.grants.models import (
    Application,
    ApplicationDocument,
    ChallengeApplicationProfile,
    EligibilityRule,
    FellowshipApplicationProfile,
    GrantCall,
    RequiredDocument,
)
from apps.rims.grants.services import auto_screen_eligibility


def _call(call_type=GrantCall.CallType.GRANT, **kwargs):
    return GrantCall.objects.create(
        title="Extended eligibility rule call",
        slug=f"ext-elig-{uuid.uuid4().hex[:8]}",
        call_type=call_type,
        opens_at=timezone.now(),
        closes_at=timezone.now() + datetime.timedelta(days=30),
        status=GrantCall.Status.PUBLISHED,
        **kwargs,
    )


def _submitted_application(call, applicant_user, institution, **kwargs):
    return Application.objects.create(
        call=call, applicant=applicant_user, institution=institution,
        status=Application.Status.SUBMITTED, **kwargs,
    )


def _reasons(app):
    app = Application.objects.get(pk=app.pk)
    return [row["reason"] for row in app.eligibility_trace]


@pytest.mark.django_db
def test_sector_rule_pass_and_fail(applicant_user, institution):
    call = _call()
    EligibilityRule.objects.create(call=call, rule_type="sector", params={"tags": ["agritech"]})
    app = _submitted_application(call, applicant_user, institution)

    applicant_user.profile.areas_of_interest = "fintech, health"
    applicant_user.profile.save()
    auto_screen_eligibility(app)
    assert any("Sector" in r for r in _reasons(app))

    applicant_user.profile.areas_of_interest = "agritech, climate"
    applicant_user.profile.save()
    app2 = _submitted_application(_call(), applicant_user, institution)
    EligibilityRule.objects.create(call=app2.call, rule_type="sector", params={"tags": ["agritech"]})
    auto_screen_eligibility(app2)
    assert not any("Sector" in r for r in _reasons(app2))


@pytest.mark.django_db
def test_age_rule_pass_and_fail(applicant_user, institution):
    call = _call()
    EligibilityRule.objects.create(call=call, rule_type="age", params={"min_age": 18, "max_age": 35})
    app = _submitted_application(call, applicant_user, institution)

    today = timezone.now().date()
    applicant_user.profile.date_of_birth = today.replace(year=today.year - 40)
    applicant_user.profile.save()
    auto_screen_eligibility(app)
    assert any("Age" in r for r in _reasons(app))

    applicant_user.profile.date_of_birth = today.replace(year=today.year - 25)
    applicant_user.profile.save()
    app2 = _submitted_application(_call(), applicant_user, institution)
    EligibilityRule.objects.create(call=app2.call, rule_type="age", params={"min_age": 18, "max_age": 35})
    auto_screen_eligibility(app2)
    assert not any("Age" in r for r in _reasons(app2))


@pytest.mark.django_db
def test_documentation_rule_pass_and_fail(applicant_user, institution):
    call = _call()
    EligibilityRule.objects.create(
        call=call, rule_type="documentation", params={"required_labels": ["CV"]}
    )
    app_fail = _submitted_application(call, applicant_user, institution)
    auto_screen_eligibility(app_fail)
    assert any("document" in r.lower() for r in _reasons(app_fail))

    call2 = _call()
    EligibilityRule.objects.create(
        call=call2, rule_type="documentation", params={"required_labels": ["CV"]}
    )
    app_pass = _submitted_application(call2, applicant_user, institution)
    required_doc = RequiredDocument.objects.create(call=call2, label="CV")
    ApplicationDocument.objects.create(
        application=app_pass, required_document=required_doc, label="CV", file="docs/cv.pdf"
    )
    auto_screen_eligibility(app_pass)
    assert not any("document" in r.lower() for r in _reasons(app_pass))


@pytest.mark.django_db
def test_scholarship_university_rule_pass_and_fail(applicant_user, institution):
    call = _call(GrantCall.CallType.SCHOLARSHIP)
    allowed = institution
    EligibilityRule.objects.create(
        call=call, rule_type="scholarship_university", params={"institution_ids": [allowed.pk]}
    )
    other_call = _call(GrantCall.CallType.SCHOLARSHIP)
    app_fail = _submitted_application(call, applicant_user, institution, scholarship_university_choice=None)
    auto_screen_eligibility(app_fail)
    assert any("university" in r.lower() for r in _reasons(app_fail))

    app_pass = _submitted_application(other_call, applicant_user, institution, scholarship_university_choice=allowed)
    EligibilityRule.objects.create(
        call=other_call, rule_type="scholarship_university", params={"institution_ids": [allowed.pk]}
    )
    auto_screen_eligibility(app_pass)
    assert not any("university" in r.lower() for r in _reasons(app_pass))


@pytest.mark.django_db
def test_scholarship_course_rule_pass_and_fail(applicant_user, institution):
    call = _call(GrantCall.CallType.SCHOLARSHIP)
    EligibilityRule.objects.create(
        call=call, rule_type="scholarship_course", params={"keywords": ["agriculture"]}
    )
    app_fail = _submitted_application(call, applicant_user, institution, scholarship_course_choice="MSc Economics")
    auto_screen_eligibility(app_fail)
    assert any("course" in r.lower() for r in _reasons(app_fail))

    call2 = _call(GrantCall.CallType.SCHOLARSHIP)
    EligibilityRule.objects.create(
        call=call2, rule_type="scholarship_course", params={"keywords": ["agriculture"]}
    )
    app_pass = _submitted_application(
        call2, applicant_user, institution, scholarship_course_choice="MSc Agriculture Economics"
    )
    auto_screen_eligibility(app_pass)
    assert not any("course" in r.lower() for r in _reasons(app_pass))


@pytest.mark.django_db
def test_fellowship_host_institution_rule_pass_and_fail(applicant_user, institution):
    call = _call(GrantCall.CallType.FELLOWSHIP)
    EligibilityRule.objects.create(
        call=call, rule_type="fellowship_host_institution", params={"institutions": ["Makerere"]}
    )
    app_fail = _submitted_application(call, applicant_user, institution)
    FellowshipApplicationProfile.objects.create(application=app_fail, host_institute_name="University of Nairobi")
    auto_screen_eligibility(app_fail)
    assert any("host institution" in r.lower() for r in _reasons(app_fail))

    call2 = _call(GrantCall.CallType.FELLOWSHIP)
    EligibilityRule.objects.create(
        call=call2, rule_type="fellowship_host_institution", params={"institutions": ["Makerere"]}
    )
    app_pass = _submitted_application(call2, applicant_user, institution)
    FellowshipApplicationProfile.objects.create(application=app_pass, host_institute_name="Makerere University")
    auto_screen_eligibility(app_pass)
    assert not any("host institution" in r.lower() for r in _reasons(app_pass))


@pytest.mark.django_db
def test_fellowship_type_rule_pass_and_fail(applicant_user, institution):
    call = _call(GrantCall.CallType.FELLOWSHIP)
    EligibilityRule.objects.create(call=call, rule_type="fellowship_type", params={"types": ["lectures"]})
    app_fail = _submitted_application(call, applicant_user, institution)
    FellowshipApplicationProfile.objects.create(
        application=app_fail, form_of_service=FellowshipApplicationProfile.FormOfService.SUPERVISION
    )
    auto_screen_eligibility(app_fail)
    assert any("fellowship type" in r.lower() for r in _reasons(app_fail))

    call2 = _call(GrantCall.CallType.FELLOWSHIP)
    EligibilityRule.objects.create(call=call2, rule_type="fellowship_type", params={"types": ["lectures"]})
    app_pass = _submitted_application(call2, applicant_user, institution)
    FellowshipApplicationProfile.objects.create(
        application=app_pass, form_of_service=FellowshipApplicationProfile.FormOfService.LECTURES
    )
    auto_screen_eligibility(app_pass)
    assert not any("fellowship type" in r.lower() for r in _reasons(app_pass))


@pytest.mark.django_db
def test_challenge_innovation_stage_rule_pass_and_fail(applicant_user, institution):
    call = _call(GrantCall.CallType.CHALLENGE)
    EligibilityRule.objects.create(
        call=call, rule_type="challenge_innovation_stage", params={"stages": ["prototype"]}
    )
    app_fail = _submitted_application(call, applicant_user, institution)
    ChallengeApplicationProfile.objects.create(
        application=app_fail, innovation_stage=GrantCall.InnovationStage.IDEATION
    )
    auto_screen_eligibility(app_fail)
    assert any("innovation stage" in r.lower() for r in _reasons(app_fail))

    call2 = _call(GrantCall.CallType.CHALLENGE)
    EligibilityRule.objects.create(
        call=call2, rule_type="challenge_innovation_stage", params={"stages": ["prototype"]}
    )
    app_pass = _submitted_application(call2, applicant_user, institution)
    ChallengeApplicationProfile.objects.create(
        application=app_pass, innovation_stage=GrantCall.InnovationStage.PROTOTYPE
    )
    auto_screen_eligibility(app_pass)
    assert not any("innovation stage" in r.lower() for r in _reasons(app_pass))
