"""Applicant profile-completeness gate — "complete your profile before you
can apply" (UN-recruitment-system style), added because several eligibility
rule types (sector/age/degree/nationality) silently fail an applicant with a
blank profile rather than treating the rule as not-applicable.

Covers:
- missing_profile_fields() / is_profile_complete()
- ProfileCompleteRequiredMixin blocks the 4 apply entry points when the
  profile is incomplete, and lets a complete profile through
- profile_view's `next` param safely resumes the original destination

Uses two conftest fixtures: `applicant_user` (profile complete — the default
"ready to apply" user most of the suite relies on) and
`incomplete_profile_applicant_user` (blank profile, for the negative path).
"""
from datetime import timedelta

import pytest
from django.urls import reverse
from django.utils import timezone

from apps.core.authentication.models import UserProfile
from apps.rims.grants.models import GrantCall
from apps.rims.grants.profile_gate import is_profile_complete, missing_profile_fields


def _call(call_type=GrantCall.CallType.GRANT, **kwargs):
    return GrantCall.objects.create(
        title="Profile gate test call",
        slug=f"profile-gate-{call_type}-{int(timezone.now().timestamp() * 1000)}",
        call_type=call_type,
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=30),
        status=GrantCall.Status.PUBLISHED,
        **kwargs,
    )


@pytest.mark.django_db
def test_missing_profile_fields_lists_everything_for_blank_profile(incomplete_profile_applicant_user):
    missing = missing_profile_fields(incomplete_profile_applicant_user)
    assert "first name" in missing
    assert "country" in missing
    assert "date of birth" in missing
    assert not is_profile_complete(incomplete_profile_applicant_user)


@pytest.mark.django_db
def test_complete_profile_passes(applicant_user):
    assert missing_profile_fields(applicant_user) == []
    assert is_profile_complete(applicant_user)


@pytest.mark.django_db
def test_apply_call_view_blocks_incomplete_profile(client, incomplete_profile_applicant_user, institution):
    call = _call()
    client.force_login(incomplete_profile_applicant_user)
    resp = client.get(reverse("rims_grants:call_apply", kwargs={"slug": call.slug}))
    assert resp.status_code == 302
    assert resp.url.startswith(reverse("accounts:profile"))


@pytest.mark.django_db
def test_apply_call_view_allows_complete_profile(client, applicant_user, institution):
    call = _call()
    client.force_login(applicant_user)
    resp = client.get(reverse("rims_grants:call_apply", kwargs={"slug": call.slug}))
    # Not redirected to the profile-completion gate — whatever else happens
    # (200 render or a different redirect entirely) is fine.
    assert not (resp.status_code == 302 and resp.url.startswith(reverse("accounts:profile")))


@pytest.mark.django_db
def test_scholarship_wizard_blocks_incomplete_profile(client, incomplete_profile_applicant_user, institution):
    call = _call(GrantCall.CallType.SCHOLARSHIP)
    client.force_login(incomplete_profile_applicant_user)
    resp = client.get(reverse("rims_grants:scholarship_apply_step1", kwargs={"slug": call.slug}))
    assert resp.status_code == 302
    assert resp.url.startswith(reverse("accounts:profile"))


@pytest.mark.django_db
def test_fellowship_wizard_blocks_incomplete_profile(client, incomplete_profile_applicant_user, institution):
    call = _call(GrantCall.CallType.FELLOWSHIP)
    client.force_login(incomplete_profile_applicant_user)
    resp = client.get(reverse("rims_grants:fellowship_apply_step1", kwargs={"slug": call.slug}))
    assert resp.status_code == 302
    assert resp.url.startswith(reverse("accounts:profile"))


@pytest.mark.django_db
def test_challenge_wizard_blocks_incomplete_profile(client, incomplete_profile_applicant_user, institution):
    call = _call(GrantCall.CallType.CHALLENGE)
    client.force_login(incomplete_profile_applicant_user)
    resp = client.get(reverse("rims_grants:challenge_apply_step1", kwargs={"slug": call.slug}))
    assert resp.status_code == 302
    assert resp.url.startswith(reverse("accounts:profile"))


@pytest.mark.django_db
def test_challenge_wizard_allows_complete_profile(client, applicant_user, institution):
    call = _call(GrantCall.CallType.CHALLENGE)
    client.force_login(applicant_user)
    resp = client.get(reverse("rims_grants:challenge_apply_step1", kwargs={"slug": call.slug}))
    assert resp.status_code == 200


@pytest.mark.django_db
def test_profile_view_redirects_to_safe_next_on_save(client, incomplete_profile_applicant_user):
    client.force_login(incomplete_profile_applicant_user)
    resp = client.post(
        reverse("accounts:profile"),
        {
            "section": "profile",
            "next": "/dashboard/",
            "first_name": "Jane",
            "last_name": "Doe",
            "phone": "+256700000000",
            "country": "Uganda",
            "degree_level": UserProfile.DegreeLevel.MASTERS,
            "date_of_birth": "1995-01-01",
            "areas_of_interest": "agritech",
            "timezone": "UTC",
        },
    )
    assert resp.status_code == 302
    assert resp.url == "/dashboard/"


@pytest.mark.django_db
def test_profile_view_ignores_unsafe_next(client, incomplete_profile_applicant_user):
    client.force_login(incomplete_profile_applicant_user)
    resp = client.post(
        reverse("accounts:profile"),
        {
            "section": "profile",
            "next": "https://evil.example/phish",
            "first_name": "Jane",
            "last_name": "Doe",
            "phone": "+256700000000",
            "country": "Uganda",
            "degree_level": UserProfile.DegreeLevel.MASTERS,
            "date_of_birth": "1995-01-01",
            "areas_of_interest": "agritech",
            "timezone": "UTC",
        },
    )
    assert resp.status_code == 302
    assert resp.url == reverse("accounts:profile")
