from __future__ import annotations

from datetime import date
from decimal import Decimal

from quartermaster import month_service, service
from quartermaster.models import Section


def _apply(db, month, entry_id, amount):
    return month_service.add_posting(
        db, month, entry_id, date.today(), Decimal(str(amount)), description="test"
    )


def test_zero_tone_classification():
    assert service.zero_tone(Decimal("0")) == "zero"
    assert service.zero_tone(Decimal("0.00")) == "zero"
    assert service.zero_tone(Decimal("1.00")) == "positive"
    assert service.zero_tone(Decimal("-0.01")) == "negative"


def test_budget_zero_empty_is_zero(db):
    assert service.budget_zero(db) == Decimal("0.00")


def test_budget_zero_positive_when_unassigned(db):
    service.add_entry(db, Section.income, "Paycheck", Decimal("2500.00"))
    service.add_entry(db, Section.fixed_bill, "Rent", Decimal("1200.00"))
    assert service.budget_zero(db) == Decimal("1300.00")


def test_budget_zero_negative_when_overbudget(db):
    service.add_entry(db, Section.income, "Paycheck", Decimal("1000.00"))
    service.add_entry(db, Section.fixed_bill, "Rent", Decimal("1200.00"))
    assert service.budget_zero(db) == Decimal("-200.00")


def test_budget_zero_exactly_zero(db):
    service.add_entry(db, Section.income, "Paycheck", Decimal("1500.00"))
    service.add_entry(db, Section.fixed_bill, "Rent", Decimal("1000.00"))
    service.add_entry(db, Section.food, "Groceries", Decimal("500.00"))
    assert service.budget_zero(db) == Decimal("0.00")


def test_budget_zero_ignores_debt_target(db):
    # target is a pointer, not a totalable section; the math does not touch it
    dm = service.add_entry(db, Section.debt_minimum, "Card A", Decimal("40.00"))
    service.add_entry(db, Section.income, "Paycheck", Decimal("100.00"))
    service.set_debt_target(db, dm.id)
    assert service.budget_zero(db) == Decimal("60.00")


def _seed_budget(db):
    service.add_entry(db, Section.income, "Paycheck", Decimal("2500.00"))
    service.add_entry(db, Section.fixed_bill, "Rent", Decimal("1200.00"))
    service.add_entry(db, Section.debt_minimum, "Card A", Decimal("40.00"))


def test_month_zero_starts_planned_nonzero_applied_zero(db):
    _seed_budget(db)
    month = month_service.create_month(db, "2026-04")
    z = month_service.month_zero(month)
    # income 2500 - (1200 + 40) = 1260
    assert z.planned == Decimal("1260.00")
    # applied all 0 on fresh snapshot
    assert z.applied == Decimal("2500.00") * 0 - Decimal("0")
    assert z.applied == Decimal("0.00")


def test_month_zero_reflects_applied_updates(db):
    _seed_budget(db)
    month = month_service.create_month(db, "2026-04")
    income_entry = next(e for e in month.entries if e.origin_name == "Paycheck")
    rent_entry = next(e for e in month.entries if e.origin_name == "Rent")
    _apply(db, month, income_entry.id, "2500.00")
    _apply(db, month, rent_entry.id, "1200.00")
    db.refresh(month)
    z = month_service.month_zero(month)
    # applied income 2500 - applied (1200 + 0) = 1300
    assert z.applied == Decimal("1300.00")


def test_add_entry_returns_zero_widget_oob(client):
    response = client.post(
        "/sections/income/entries",
        data={"name": "Paycheck", "amount": "2500.00"},
    )
    assert response.status_code == 200
    assert 'id="zero-widget"' in response.text
    assert 'hx-swap-oob="outerHTML"' in response.text
    assert "tone-positive" in response.text


def test_delete_entry_returns_zero_widget_oob(client):
    client.post(
        "/sections/income/entries",
        data={"name": "Paycheck", "amount": "2500.00"},
    )
    response = client.delete("/entries/1")
    assert response.status_code == 200
    assert 'id="zero-widget"' in response.text
    assert "tone-zero" in response.text


def test_budget_page_renders_zero_widget(client):
    response = client.get("/")
    assert response.status_code == 200
    assert 'id="zero-widget"' in response.text
    assert "Zero Amount" in response.text


def test_month_entry_update_returns_zero_widget_oob(client):
    client.post(
        "/sections/income/entries",
        data={"name": "Paycheck", "amount": "2500.00"},
    )
    client.post(
        "/sections/fixed_bill/entries",
        data={"name": "Rent", "amount": "1200.00"},
    )
    client.post("/month/2026-04/create")
    # posting rent applied via the ledger should bring the month zero widget back with new values
    response = client.post(
        "/month/2026-04/entries/2/postings",
        data={"occurred_on": "2026-04-01", "amount": "1200.00"},
    )
    assert response.status_code == 200
    assert 'id="zero-widget"' in response.text
    # applied: 0 - 1200 = -1200, negative tone
    assert "tone-negative" in response.text


def test_month_page_renders_paired_zero_widget(client):
    client.post(
        "/sections/income/entries",
        data={"name": "Paycheck", "amount": "2500.00"},
    )
    client.post("/month/2026-04/create")
    response = client.get("/month/2026-04")
    assert response.status_code == 200
    assert 'id="zero-widget"' in response.text
    assert response.text.count("Planned") >= 1
    assert response.text.count("Applied") >= 1
