"""PRD §5.1 FRFA-AA005 + FRFA-AM022–023 — award acknowledgement and review
deadline reminder tasks."""
from datetime import date, timedelta
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model
from django.utils import timezone

from apps.core.permissions.roles import UserRole
from apps.rims.grants.models import Application, Award, GrantCall, Review
from apps.rims.grants.services import (
    acknowledge_award,
    award_application,
    shortlist_application,
    submit_application,
)
from apps.rims.grants.tasks import (
    remind_award_acknowledgements,
    remind_overdue_reviews,
)

User = get_user_model()


def _awarded_setup(applicant_user, institution, suffix):
    call = GrantCall.objects.create(
        title="Ack call",
        slug=f"ack-{suffix}-{int(timezone.now().timestamp() * 1000) % 10_000_000}",
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=10),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    submit_application(app)
    app = Application.objects.get(pk=app.pk)
    shortlist_application(app)
    award_application(app, Decimal("1000"), date.today() + timedelta(days=180), narrative="")
    return Application.objects.get(pk=app.pk).award


@pytest.mark.django_db
def test_acknowledge_award_is_idempotent(applicant_user, institution):
    award = _awarded_setup(applicant_user, institution, "idem")
    acknowledge_award(award)
    first_ack = Award.objects.get(pk=award.pk).acknowledged_at
    # Second call should leave the original timestamp in place.
    acknowledge_award(Award.objects.get(pk=award.pk))
    assert Award.objects.get(pk=award.pk).acknowledged_at == first_ack


@pytest.mark.django_db
def test_remind_award_acknowledgements_skips_awards_inside_grace_period(
    applicant_user, institution
):
    award = _awarded_setup(applicant_user, institution, "grace")
    sent = remind_award_acknowledgements(grace_days=7)
    assert sent == 0
    award.refresh_from_db()
    assert award.acknowledgement_reminders_sent == 0


@pytest.mark.django_db
def test_remind_award_acknowledgements_sends_after_grace(applicant_user, institution):
    award = _awarded_setup(applicant_user, institution, "past")
    # Backdate the award into the past.
    Award.objects.filter(pk=award.pk).update(awarded_at=timezone.now() - timedelta(days=15))
    sent = remind_award_acknowledgements(grace_days=7, max_reminders=3)
    assert sent == 1
    award.refresh_from_db()
    assert award.acknowledgement_reminders_sent == 1


@pytest.mark.django_db
def test_remind_award_acknowledgements_stops_after_max_reminders(
    applicant_user, institution
):
    award = _awarded_setup(applicant_user, institution, "max")
    Award.objects.filter(pk=award.pk).update(
        awarded_at=timezone.now() - timedelta(days=30),
        acknowledgement_reminders_sent=3,
    )
    sent = remind_award_acknowledgements(grace_days=7, max_reminders=3)
    assert sent == 0


@pytest.mark.django_db
def test_remind_overdue_reviews_reminds_before_and_after_due_at(
    applicant_user, institution
):
    call = GrantCall.objects.create(
        title="Rev reminders",
        slug=f"revrem-{int(timezone.now().timestamp())}",
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=10),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    submit_application(app)
    reviewer = User.objects.create_user(email="rev-reminder@example.com", password="x")
    reviewer.role = UserRole.REVIEWER
    reviewer.save()
    # Approaching deadline review.
    Review.objects.create(
        application=Application.objects.get(pk=app.pk),
        reviewer=reviewer,
        due_at=timezone.now() + timedelta(days=1),
    )
    # Far-out review — should not be reminded.
    other_reviewer = User.objects.create_user(email="rev-far@example.com", password="x")
    other_reviewer.role = UserRole.REVIEWER
    other_reviewer.save()
    Review.objects.create(
        application=Application.objects.get(pk=app.pk),
        reviewer=other_reviewer,
        due_at=timezone.now() + timedelta(days=30),
    )
    reminded = remind_overdue_reviews(warn_days_before=3)
    assert reminded == 1
