from datetime import date, timedelta

import pytest

from ccma.domain.models import ContributionData
from ccma.services.housekeeper import Housekeeper
from ccma.storage.repository import MemberRepository, RepositoryError


def _overdue_claim_repository(tmp_path):
    repository = MemberRepository(tmp_path)
    repository.initialize()
    member = repository.create_member(first_name="Reminder", last_name="Test", birth_date="1990-01-01")
    repository.save_contributions(
        member.member_id,
        ContributionData(
            claims=[
                {
                    "claim_id": "claim-1",
                    "claim_key": "overdue-test",
                    "title": "Offene Forderung",
                    "amount": "100.00",
                    "due_date": "2026-01-31",
                    "status": "open",
                }
            ]
        ),
    )
    return repository, member


def test_reminder_rule_progresses_only_after_sent_deadline(tmp_path) -> None:
    repository, member = _overdue_claim_repository(tmp_path)
    housekeeper = Housekeeper(repository)
    findings = housekeeper.run(today=date(2026, 2, 10))
    reminder_task = next(item for item in findings if item.code == "reminder_due")
    assert "Zahlungserinnerung" in reminder_task.title

    draft = repository.create_reminder_draft(
        member.member_id,
        "claim-1",
        level=1,
        name="Zahlungserinnerung",
        payment_deadline_days=14,
        fee="0.00",
    )
    findings = housekeeper.run(today=date(2026, 2, 10))
    reminder_task = next(item for item in findings if item.code == "reminder_due")
    assert "wartet auf Versand" in reminder_task.title

    sent = repository.mark_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
    deadline = date.fromisoformat(sent["payment_deadline"])
    assert not any(
        item.code == "reminder_due" for item in housekeeper.run(today=deadline - timedelta(days=1))
    )
    findings = housekeeper.run(today=deadline)
    next_task = next(item for item in findings if item.code == "reminder_due")
    assert "Erste Mahnung" in next_task.title


def test_dunning_hold_suppresses_and_then_restores_task(tmp_path) -> None:
    repository, member = _overdue_claim_repository(tmp_path)
    housekeeper = Housekeeper(repository)
    repository.set_dunning_hold(
        member.member_id,
        "claim-1",
        active=True,
        reason="Betrag wird geklärt",
    )

    assert not any(item.code == "reminder_due" for item in housekeeper.run(today=date(2026, 2, 10)))
    with pytest.raises(RepositoryError, match="Mahnsperre aktiv"):
        repository.create_reminder_draft(
            member.member_id,
            "claim-1",
            level=1,
            name="Zahlungserinnerung",
            payment_deadline_days=14,
        )
    repository.set_dunning_hold(member.member_id, "claim-1", active=False)
    assert any(item.code == "reminder_due" for item in housekeeper.run(today=date(2026, 2, 10)))


def test_draft_can_be_cancelled_but_sent_reminder_cannot(tmp_path) -> None:
    repository, member = _overdue_claim_repository(tmp_path)
    draft = repository.create_reminder_draft(
        member.member_id,
        "claim-1",
        level=1,
        name="Zahlungserinnerung",
        payment_deadline_days=14,
    )
    repository.cancel_reminder(member.member_id, "claim-1", draft["reminder_id"])
    data = repository.get_contributions(member.member_id)
    assert data.reminders[0]["status"] == "cancelled"

    second = repository.create_reminder_draft(
        member.member_id,
        "claim-1",
        level=1,
        name="Zahlungserinnerung",
        payment_deadline_days=14,
    )
    repository.mark_reminder_sent(member.member_id, "claim-1", second["reminder_id"])
    with pytest.raises(RepositoryError, match="bereits versandte"):
        repository.cancel_reminder(member.member_id, "claim-1", second["reminder_id"])


def test_reminder_levels_cannot_be_skipped(tmp_path) -> None:
    repository, member = _overdue_claim_repository(tmp_path)

    with pytest.raises(RepositoryError, match="Mahnstufe 1 wurde noch nicht versandt"):
        repository.create_reminder_draft(
            member.member_id,
            "claim-1",
            level=2,
            name="Erste Mahnung",
            payment_deadline_days=14,
            fee="5.00",
        )


def test_payment_resolves_open_reminder_task(tmp_path) -> None:
    repository, member = _overdue_claim_repository(tmp_path)
    housekeeper = Housekeeper(repository)
    assert any(item.code == "reminder_due" for item in housekeeper.run(today=date(2026, 2, 10)))
    repository.record_payment(
        member.member_id,
        "claim-1",
        payment_date="2026-02-10",
        amount="100.00",
        allocation_amount="100.00",
    )

    findings = housekeeper.run(today=date(2026, 2, 10))
    assert not any(item.code == "reminder_due" for item in findings)
