from datetime import timedelta
from decimal import Decimal

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

from apps.rims.finance.models import Budget, BudgetLine, DisbursementExecution, DisbursementReconciliation, DisbursementRequest
from apps.rims.finance.services import approve_disbursement_request, reconcile_disbursement, save_execution_plan
from apps.rims.grants.models import Application, GrantCall
from apps.rims.grants.services import award_application, shortlist_application, submit_application


def _approved_request(applicant_user, institution, finance_user):
    call = GrantCall.objects.create(
        title="Execution call",
        slug=f"execution-{int(timezone.now().timestamp())}",
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=30),
        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("5000"), timezone.now().date() + timedelta(days=200), narrative="")
    budget = Budget.objects.create(award=app.award, name="Ops")
    BudgetLine.objects.create(budget=budget, label="Line", amount=Decimal("5000"))
    req = DisbursementRequest.objects.create(
        budget=budget,
        amount=Decimal("350"),
        status=DisbursementRequest.Status.SUBMITTED,
        payment_method=DisbursementRequest.PaymentMethod.BANK_TRANSFER,
        scheduled_for=timezone.now().date() + timedelta(days=3),
        account_reference="BEN-44",
    )
    approve_disbursement_request(req, actor=finance_user)
    req.refresh_from_db()
    return req


@pytest.mark.django_db
def test_approve_creates_execution_plan(applicant_user, institution, finance_user):
    req = _approved_request(applicant_user, institution, finance_user)
    assert req.status == DisbursementRequest.Status.APPROVED
    assert req.execution.status == DisbursementExecution.Status.SCHEDULED
    assert req.execution.destination_account == "BEN-44"


@pytest.mark.django_db
def test_execute_then_reconcile_paid_request(applicant_user, institution, finance_user):
    req = _approved_request(applicant_user, institution, finance_user)
    save_execution_plan(
        req,
        status=DisbursementExecution.Status.EXECUTED,
        payment_method=DisbursementRequest.PaymentMethod.BANK_TRANSFER,
        scheduled_for=req.scheduled_for,
        execution_reference="TX-001",
        destination_account="BEN-44",
        notes="Paid in bank run",
    )
    req.refresh_from_db()
    assert req.status == DisbursementRequest.Status.PAID
    assert req.execution.execution_reference == "TX-001"
    reconciliation = reconcile_disbursement(
        req,
        status=DisbursementReconciliation.Status.RECONCILED,
        notes="Matched to statement",
        actor=finance_user,
    )
    assert reconciliation.status == DisbursementReconciliation.Status.RECONCILED
    assert reconciliation.reconciled_by == finance_user


@pytest.mark.django_db
def test_reconciliation_requires_paid_status(applicant_user, institution, finance_user):
    req = _approved_request(applicant_user, institution, finance_user)
    with pytest.raises(ValidationError, match="paid requests"):
        reconcile_disbursement(
            req,
            status=DisbursementReconciliation.Status.RECONCILED,
            notes="Too early",
            actor=finance_user,
        )
