from decimal import Decimal

import pytest

from ccma.domain.contributions import (
    claim_balance,
    claim_items,
    claim_status,
    claim_total,
    payment_allocated_total,
)
from ccma.domain.models import ContributionData
from ccma.storage.repository import MemberRepository, RepositoryError


def _repository_with_claim(tmp_path, *, amount="100.00", claim_id="claim-1"):
    repository = MemberRepository(tmp_path)
    repository.initialize()
    member = repository.create_member(first_name="Claim", last_name="Test")
    repository.save_contributions(
        member.member_id,
        ContributionData(
            claims=[
                {
                    "claim_id": claim_id,
                    "claim_key": "test-claim",
                    "title": "Testforderung",
                    "amount": amount,
                    "due_date": "2026-12-31",
                    "status": "open",
                }
            ]
        ),
    )
    return repository, member


def test_legacy_claim_becomes_itemized_when_position_is_added(tmp_path) -> None:
    repository, member = _repository_with_claim(tmp_path)
    data, claim = repository.get_claim(member.member_id, "claim-1")
    assert claim_total(claim) == Decimal("100.00")
    assert claim_items(claim)[0]["item_id"] == "legacy-base"

    repository.add_claim_item(
        member.member_id,
        "claim-1",
        description="Gutschrift",
        quantity="1",
        unit_price="-10,00",
        item_type="credit",
    )
    data, claim = repository.get_claim(member.member_id, "claim-1")

    assert len(claim["items"]) == 2
    assert claim_total(claim) == Decimal("90.00")
    assert claim["amount"] == "90.00"
    assert claim_balance(data, claim) == Decimal("90.00")


def test_payment_can_be_split_across_multiple_claims(tmp_path) -> None:
    repository, member = _repository_with_claim(tmp_path)
    data = repository.get_contributions(member.member_id)
    data.claims.append(
        {
            "claim_id": "claim-2",
            "claim_key": "second-claim",
            "title": "Zweite Forderung",
            "amount": "50.00",
            "due_date": "2026-12-31",
            "status": "open",
        }
    )
    repository.save_contributions(member.member_id, data)

    payment = repository.record_payment(
        member.member_id,
        "claim-1",
        payment_date="2026-06-21",
        amount="150.00",
        allocation_amount="100.00",
        gnucash_transaction_id="TX-42",
        reference="Jahreszahlung",
    )
    repository.allocate_payment(
        member.member_id,
        "claim-2",
        payment_id=payment["payment_id"],
        amount="50.00",
    )
    data, first = repository.get_claim(member.member_id, "claim-1")
    _data, second = repository.get_claim(member.member_id, "claim-2")

    assert claim_status(data, first) == "paid"
    assert claim_status(data, second) == "paid"
    assert payment_allocated_total(data, payment["payment_id"]) == Decimal("150.00")
    with pytest.raises(RepositoryError, match="nur 0.00 EUR"):
        repository.allocate_payment(
            member.member_id,
            "claim-2",
            payment_id=payment["payment_id"],
            amount="1.00",
        )


def test_reminder_fee_increases_claim_and_is_audited(tmp_path) -> None:
    repository, member = _repository_with_claim(tmp_path)

    reminder = repository.create_reminder_draft(
        member.member_id,
        "claim-1",
        level=1,
        name="Zahlungserinnerung",
        payment_deadline_days=14,
        detail="Per E-Mail versandt",
        fee="5.00",
    )
    data, claim = repository.get_claim(member.member_id, "claim-1")

    assert claim_total(claim) == Decimal("100.00")
    assert reminder["status"] == "draft"
    assert reminder["fee_item_id"] is None

    repository.mark_reminder_sent(member.member_id, "claim-1", reminder["reminder_id"])
    data, claim = repository.get_claim(member.member_id, "claim-1")
    sent = data.reminders[0]

    assert claim_total(claim) == Decimal("105.00")
    assert sent["status"] == "sent"
    assert sent["payment_deadline"]
    assert sent["fee_item_id"]
    assert data.reminders[0]["detail"] == "Per E-Mail versandt"
    assert repository.get_events(member.member_id)[-1].event_type == "reminder_sent"


def test_claim_with_payment_cannot_be_cancelled(tmp_path) -> None:
    repository, member = _repository_with_claim(tmp_path)
    repository.record_payment(
        member.member_id,
        "claim-1",
        payment_date="2026-06-21",
        amount="10.00",
        allocation_amount="10.00",
    )

    with pytest.raises(RepositoryError, match="Zahlungszuordnungen"):
        repository.cancel_claim(member.member_id, "claim-1")


def test_gnucash_id_is_unique_across_member_store(tmp_path) -> None:
    repository, first_member = _repository_with_claim(tmp_path / "store")
    repository.record_payment(
        first_member.member_id,
        "claim-1",
        payment_date="2026-06-21",
        amount="100.00",
        allocation_amount="100.00",
        gnucash_transaction_id="UNIQUE-1",
    )
    second = repository.create_member(first_name="Second", last_name="Member")
    repository.save_contributions(
        second.member_id,
        ContributionData(
            claims=[
                {
                    "claim_id": "other-claim",
                    "title": "Andere Forderung",
                    "amount": "10.00",
                    "due_date": "2026-12-31",
                }
            ]
        ),
    )

    with pytest.raises(RepositoryError, match="GnuCash-ID bereits verwendet"):
        repository.record_payment(
            second.member_id,
            "other-claim",
            payment_date="2026-06-21",
            amount="10.00",
            allocation_amount="10.00",
            gnucash_transaction_id="unique-1",
        )
