"""Unit tests for budget_recommender.fifty_thirty_twenty — Story 4.3."""
from decimal import Decimal

import pytest

from app.services.insights.budget_recommender import (
    BudgetRecommendation,
    fifty_thirty_twenty,
)

NEEDS_NAMES = {'Housing', 'Utilities', 'Groceries', 'Transportation', 'Healthcare', 'Debt Payments'}
WANTS_NAMES = {'Dining', 'Entertainment', 'Shopping', 'Subscriptions', 'Personal Care'}


class TestFiftyThirtyTwenty:
    def test_returns_list(self):
        assert isinstance(fifty_thirty_twenty(Decimal('5000')), list)

    def test_returns_budget_recommendation_objects(self):
        recs = fifty_thirty_twenty(Decimal('5000'))
        assert all(isinstance(r, BudgetRecommendation) for r in recs)

    def test_recommendation_has_required_fields(self):
        r = fifty_thirty_twenty(Decimal('1000'))[0]
        assert hasattr(r, 'category_name')
        assert hasattr(r, 'suggested_amount')
        assert hasattr(r, 'bucket')

    def test_amounts_are_decimal_not_float(self):
        recs = fifty_thirty_twenty(Decimal('3000'))
        for r in recs:
            assert isinstance(r.suggested_amount, Decimal)

    def test_buckets_are_valid_values(self):
        recs = fifty_thirty_twenty(Decimal('3000'))
        for r in recs:
            assert r.bucket in ('needs', 'wants', 'savings')

    def test_needs_categories_present(self):
        recs = fifty_thirty_twenty(Decimal('3000'))
        names = {r.category_name for r in recs if r.bucket == 'needs'}
        assert names == NEEDS_NAMES

    def test_wants_categories_present(self):
        recs = fifty_thirty_twenty(Decimal('3000'))
        names = {r.category_name for r in recs if r.bucket == 'wants'}
        assert names == WANTS_NAMES

    def test_exactly_one_savings_entry(self):
        recs = fifty_thirty_twenty(Decimal('5000'))
        savings = [r for r in recs if r.bucket == 'savings']
        assert len(savings) == 1

    def test_savings_gets_20_percent(self):
        income = Decimal('1000')
        recs = fifty_thirty_twenty(income)
        savings = next(r for r in recs if r.bucket == 'savings')
        assert savings.suggested_amount == Decimal('200.00')

    def test_needs_total_is_50_percent(self):
        # Use income divisible by 6 for exact check
        income = Decimal('6000')
        recs = fifty_thirty_twenty(income)
        total = sum(r.suggested_amount for r in recs if r.bucket == 'needs')
        assert total == income * Decimal('0.50')

    def test_wants_total_is_30_percent(self):
        # Use income divisible by 5 for exact check
        income = Decimal('5000')
        recs = fifty_thirty_twenty(income)
        total = sum(r.suggested_amount for r in recs if r.bucket == 'wants')
        assert total == income * Decimal('0.30')

    def test_zero_income_all_zeros(self):
        recs = fifty_thirty_twenty(Decimal('0'))
        assert all(r.suggested_amount == Decimal('0') for r in recs)

    def test_total_count(self):
        # 6 needs + 5 wants + 1 savings = 12
        recs = fifty_thirty_twenty(Decimal('3000'))
        assert len(recs) == 12
