"""Old-system parity: legacy scholarships.ApplicantHouseHoldSurvey
("Psychometric Test Model").

Covers:
- PsychometricAssessment only valid for scholarship-type applications
- Attempt tracking (SRS §4.1.4 — max 3 attempts)
- auto_screen_eligibility flags scholarship applications with no
  psychometric_score as non-compliant (SRS: "all scholarship applications
  MUST have accompanying psychometric tests")
"""
import datetime
import uuid
from decimal import Decimal

import pytest
from django.core.exceptions import ValidationError
from django.utils import timezone

from apps.rims.grants.models import (
    Application,
    GrantCall,
    MAX_PSYCHOMETRIC_ATTEMPTS,
    PsychometricAssessment,
)
from apps.rims.grants.services import auto_screen_eligibility, record_psychometric_attempt


def _call(call_type, **kwargs):
    defaults = {
        "title": "Psychometric test call",
        "slug": f"psych-{uuid.uuid4().hex[:8]}",
        "call_type": call_type,
        "opens_at": timezone.now(),
        "closes_at": timezone.now() + datetime.timedelta(days=30),
        "status": GrantCall.Status.PUBLISHED,
    }
    defaults.update(kwargs)
    return GrantCall.objects.create(**defaults)


@pytest.mark.django_db
def test_create_assessment_for_scholarship_application(applicant_user, institution):
    call = _call(GrantCall.CallType.SCHOLARSHIP)
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    assessment = PsychometricAssessment.objects.create(
        application=app,
        surname="Doe",
        other_names="Jane",
        to_be_the_best=3,
        entrepreneurship_drives_profit=2,
        adaptable=8,
    )
    assessment.full_clean()
    assert assessment.to_be_the_best == 3
    assert assessment.entrepreneurship_drives_profit == 2
    assert assessment.adaptable == 8


@pytest.mark.django_db
def test_assessment_rejects_non_scholarship_application(applicant_user, institution):
    call = _call(GrantCall.CallType.GRANT)
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    assessment = PsychometricAssessment(application=app)
    with pytest.raises(ValidationError):
        assessment.full_clean()


@pytest.mark.django_db
def test_attempt_cap_enforced_at_service_level(applicant_user, institution):
    call = _call(GrantCall.CallType.SCHOLARSHIP)
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    assessment = PsychometricAssessment.objects.create(application=app)
    for _ in range(MAX_PSYCHOMETRIC_ATTEMPTS):
        record_psychometric_attempt(assessment)
    assert assessment.attempts.count() == MAX_PSYCHOMETRIC_ATTEMPTS
    with pytest.raises(ValidationError):
        record_psychometric_attempt(assessment)


@pytest.mark.django_db
def test_eligibility_screening_flags_missing_psychometric_score(applicant_user, institution):
    call = _call(GrantCall.CallType.SCHOLARSHIP, closes_at=timezone.now() - datetime.timedelta(days=1))
    app = Application.objects.create(
        call=call,
        applicant=applicant_user,
        institution=institution,
        status=Application.Status.SUBMITTED,
    )
    auto_screen_eligibility(app)
    # Application.status is a protected FSMField — refresh_from_db() can't
    # touch it (django-fsm blocks any direct setattr, including the one
    # refresh_from_db does internally), so re-fetch instead.
    app = Application.objects.get(pk=app.pk)
    assert app.eligibility_passed is False
    assert "Psychometric test not completed" in app.eligibility_notes
    reasons = [row["reason"] for row in app.eligibility_trace]
    assert "Psychometric test not completed" in reasons


@pytest.mark.django_db
def test_eligibility_screening_passes_with_recorded_score(applicant_user, institution, grants_manager_user):
    call = _call(GrantCall.CallType.SCHOLARSHIP)
    app = Application.objects.create(
        call=call,
        applicant=applicant_user,
        institution=institution,
        status=Application.Status.SUBMITTED,
        psychometric_score=Decimal("75.00"),
        psychometric_recorded_by=grants_manager_user,
        psychometric_recorded_at=timezone.now(),
    )
    auto_screen_eligibility(app)
    app = Application.objects.get(pk=app.pk)
    reasons = [row["reason"] for row in app.eligibility_trace]
    assert "Psychometric test not completed" not in reasons


@pytest.mark.django_db
def test_grant_application_not_flagged_for_psychometric(applicant_user, institution):
    call = _call(GrantCall.CallType.GRANT)
    app = Application.objects.create(
        call=call,
        applicant=applicant_user,
        institution=institution,
        status=Application.Status.SUBMITTED,
    )
    auto_screen_eligibility(app)
    app = Application.objects.get(pk=app.pk)
    reasons = [row["reason"] for row in app.eligibility_trace]
    assert "Psychometric test not completed" not in reasons
