"""PRD §5.4 FRFM048 — bank statement upload, parse, auto-match."""
from __future__ import annotations

from datetime import date, timedelta
from decimal import Decimal
from unittest.mock import patch

import pytest
from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils import timezone

from apps.core.audit.models import AuditLog
from apps.core.audit.tasks import persist_audit_log
from apps.rims.finance.models import (
    BankStatement,
    BankStatementLine,
    Budget,
    BudgetLine,
    DisbursementExecution,
    DisbursementRequest,
)
from apps.rims.finance.services_reconciliation import (
    auto_match_bank_lines,
    import_bank_statement,
    parse_bank_statement_lines,
)
from apps.rims.grants.models import Application, Award, GrantCall


@pytest.fixture(autouse=True)
def _sync_audit():
    with patch("apps.core.audit.tasks.write_audit_log.delay", side_effect=lambda **kw: persist_audit_log(**kw)):
        with patch(
            "apps.core.audit.mixins.transaction.on_commit",
            side_effect=lambda fn: fn(),
        ):
            yield


def _executed_with_amount(applicant_user, institution, finance_user, amount: Decimal):
    call = GrantCall.objects.create(
        title=f"6F-bank call {timezone.now().timestamp()}",
        slug=f"6f-bank-{int(timezone.now().timestamp() * 1000) % 10_000_000}",
        call_type="grant",
        opens_at=timezone.now() - timedelta(days=200),
        closes_at=timezone.now() + timedelta(days=10),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    award = Award.objects.create(
        application=app,
        amount=Decimal("5000"),
        currency="USD",
        awarded_at=timezone.now(),
        project_end_date=timezone.now().date() + timedelta(days=365),
    )
    budget = Budget.objects.create(award=award, name="Bank budget", currency="USD")
    BudgetLine.objects.create(budget=budget, label="L", amount=Decimal("5000"))
    request = DisbursementRequest.objects.create(
        budget=budget,
        amount=amount,
        requested_by=finance_user,
        status=DisbursementRequest.Status.APPROVED,
    )
    return DisbursementExecution.objects.create(
        request=request,
        status=DisbursementExecution.Status.EXECUTED,
        executed_at=timezone.now(),
    )


@pytest.mark.django_db
def test_import_statement_emits_upload_audit(applicant_user, institution, finance_user):
    """PRD §5.4 FRFM048 — uploading a statement creates the BankStatement
    row and emits BANK_STATEMENT_UPLOADED audit."""
    csv_bytes = b"value_date,description,amount\n2026-05-15,Some payment,500.00\n"
    statement = import_bank_statement(
        actor=finance_user,
        file=SimpleUploadedFile("may.csv", csv_bytes, content_type="text/csv"),
        name="Test Statement",
        period_start=date(2026, 5, 1),
        period_end=date(2026, 5, 31),
        account_reference="TEST-001",
        currency="USD",
    )
    assert statement.pk
    assert AuditLog.objects.filter(action="BANK_STATEMENT_UPLOADED").exists()


@pytest.mark.django_db
def test_parse_then_match_links_to_execution(applicant_user, institution, finance_user):
    """PRD §5.4 FRFM048 — parsing populates lines; auto-match links amount + date."""
    today = timezone.now().date()
    execution = _executed_with_amount(applicant_user, institution, finance_user, Decimal("500.00"))
    csv_bytes = f"value_date,description,amount\n{today.isoformat()},Auto-match candidate,500.00\n".encode()
    statement = import_bank_statement(
        actor=finance_user,
        file=SimpleUploadedFile("today.csv", csv_bytes, content_type="text/csv"),
        name="Today statement",
        period_start=today - timedelta(days=5),
        period_end=today + timedelta(days=5),
        account_reference="TEST-002",
        currency="USD",
    )
    parse_bank_statement_lines(statement)
    assert statement.lines.count() == 1
    counts = auto_match_bank_lines(statement)
    assert counts["matched"] == 1
    assert counts["unmatched"] == 0
    line = statement.lines.first()
    assert line.match_status == BankStatementLine.MatchStatus.MATCHED
    assert line.matched_execution_id == execution.pk
    assert AuditLog.objects.filter(action="BANK_STATEMENT_RECONCILED").exists()
