import json
from datetime import date

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


def test_housekeeper_reports_initial_payment_and_open_claims(tmp_path) -> None:
    repository = MemberRepository(tmp_path)
    repository.initialize()
    member = repository.create_member(first_name="Test", last_name="Person", birth_date="1990-01-01")
    member.status = "accepted_pending_payment"
    member.accepted_at = "2026-01-01"
    repository.save_member(member)
    repository.save_contributions(
        member.member_id,
        ContributionData(
            claims=[
                {
                    "claim_id": "claim-1",
                    "title": "Mitgliedsbeitrag 2026",
                    "amount": "150.00",
                    "due_date": "2026-01-31",
                    "status": "open",
                }
            ]
        ),
    )

    findings = Housekeeper(repository).run(today=date(2026, 2, 10))
    # Both claims are more than the default 7-day grace period overdue, so the dunning
    # escalation ("reminder_due") is the single active finding for each -- no separate,
    # redundant "overdue" finding alongside it.
    assert {finding.code for finding in findings} == {"reminder_due"}


def test_housekeeper_reports_birthdays_before_today_and_after(tmp_path) -> None:
    repository = MemberRepository(tmp_path)
    repository.initialize()
    dates = ("1990-06-20", "1990-06-21", "1990-06-22")
    for index, birth_date in enumerate(dates):
        member = repository.create_member(
            first_name=f"Birthday{index}",
            last_name="Member",
            birth_date=birth_date,
        )
        member.status = "active"
        repository.save_member(member)
    settings = HousekeeperSettings.from_values(
        birthday_days_before=2,
        birthday_days_after=2,
        anniversary_days_before=0,
        anniversary_days_after=0,
        anniversary_intervals="1Y",
    )

    findings = [
        finding
        for finding in Housekeeper(repository, settings).run(today=date(2026, 6, 21))
        if finding.code == "birthday"
    ]
    assert {finding.title for finding in findings} == {
        "Birthday0 Member hatte vor 1 Tag Geburtstag",
        "Birthday1 Member hat heute Geburtstag",
        "Birthday2 Member hat in 1 Tag Geburtstag",
    }


def test_housekeeper_reports_day_month_and_year_anniversaries(tmp_path) -> None:
    repository = MemberRepository(tmp_path)
    repository.initialize()
    starts = ("2026-05-22", "2026-04-21", "2025-06-21", "2016-06-22")
    for index, started_at in enumerate(starts):
        member = repository.create_member(first_name=f"Anniversary{index}", last_name="Member")
        member.status = "active"
        member.membership_started_at = started_at
        repository.save_member(member)
    settings = HousekeeperSettings.from_values(
        birthday_days_before=0,
        birthday_days_after=0,
        anniversary_days_before=2,
        anniversary_days_after=2,
        anniversary_intervals="30D;2M;1Y;10Y",
    )

    findings = [
        finding
        for finding in Housekeeper(repository, settings).run(today=date(2026, 6, 21))
        if finding.code == "membership_anniversary"
    ]
    assert {finding.title for finding in findings} == {
        "Anniversary0 Member hat heute 30-Tage-Mitgliedsjubiläum",
        "Anniversary1 Member hat heute 2-Monats-Mitgliedsjubiläum",
        "Anniversary2 Member hat heute 1-jähriges Mitgliedsjubiläum",
        "Anniversary3 Member hat in 1 Tag 10-jähriges Mitgliedsjubiläum",
    }


def test_housekeeper_reports_invalid_member_dates(tmp_path) -> None:
    repository = MemberRepository(tmp_path)
    repository.initialize()
    member = repository.create_member(first_name="Broken", last_name="Date")
    member_path = repository.members_root / member.member_id / "member.json"
    raw = json.loads(member_path.read_text(encoding="utf-8"))
    raw["person"]["birth_date"] = "31/12/2000"
    member_path.write_text(json.dumps(raw), encoding="utf-8")

    findings = Housekeeper(repository).run(today=date(2026, 6, 21))
    invalid = [finding for finding in findings if finding.code == "invalid_member_dates"]

    assert len(invalid) == 1
    assert invalid[0].member_id == member.member_id
    assert "Geburtsdatum" in invalid[0].detail


def test_housekeeper_can_treat_selected_member_fields_as_optional(tmp_path) -> None:
    repository = MemberRepository(tmp_path)
    repository.initialize()
    member = repository.create_member(first_name="Optional", last_name="Fields")
    member.status = "active"
    repository.save_member(member)
    settings = HousekeeperSettings.from_values(
        birthday_days_before=0,
        birthday_days_after=0,
        anniversary_days_before=0,
        anniversary_days_after=0,
        anniversary_intervals="1Y",
        optional_member_fields=("nickname", "email", "phone", "birth_date"),
    )

    findings = Housekeeper(repository, settings).run(today=date(2026, 6, 21))

    codes = {finding.code for finding in findings}
    assert "missing_birth_date" not in codes
    assert "missing_member_field:nickname" not in codes
    assert "missing_member_field:email" not in codes
    assert "missing_member_field:phone" not in codes
    assert "missing_member_field:street" in codes
