"""Budget actuals view — Story 4.4."""
from decimal import Decimal
from datetime import date

import pytest

from app.extensions import db as _db
from app.models.account import Account
from app.models.budget import Budget
from app.models.category import Category
from app.models.transaction import Transaction


def _make_account():
    acct = Account(name='Checking', type='checking', is_active=True)
    _db.session.add(acct)
    _db.session.commit()
    return acct


def _make_category(name='Groceries'):
    cat = Category(name=name, is_system=True, is_active=True)
    _db.session.add(cat)
    _db.session.commit()
    return cat


def _make_budget(cat, amount='500.00'):
    today = date.today()
    b = Budget(category_id=cat.id, month=today.month, year=today.year, amount=Decimal(amount))
    _db.session.add(b)
    _db.session.commit()
    return b


def _make_txn(acct, cat, amount='100.00', is_credit=False, month_offset=0):
    today = date.today()
    month = today.month + month_offset
    year = today.year
    if month < 1:
        month += 12
        year -= 1
    elif month > 12:
        month -= 12
        year += 1
    d = f'{year}-{month:02d}-15'
    txn = Transaction(
        date=d,
        merchant_normalized='TestMerchant',
        amount=Decimal(amount),
        is_credit=is_credit,
        is_manual=True,
        account_id=acct.id,
        category_id=cat.id,
    )
    _db.session.add(txn)
    _db.session.commit()
    return txn


# ── GET ───────────────────────────────────────────────────────────────────────

class TestBudgetsActualsGet:
    def test_returns_200(self, client, db):
        assert client.get('/budgets/').status_code == 200

    def test_shows_budgeted_amount(self, client, db):
        cat = _make_category('Housing')
        _make_budget(cat, '1200.00')
        response = client.get('/budgets/')
        assert b'1200' in response.data

    def test_shows_spent_amount(self, client, db):
        acct = _make_account()
        cat = _make_category('Dining')
        _make_budget(cat, '300.00')
        _make_txn(acct, cat, '75.00')
        response = client.get('/budgets/')
        assert b'75' in response.data

    def test_shows_zero_spent_when_no_transactions(self, client, db):
        cat = _make_category('Utilities')
        _make_budget(cat, '200.00')
        response = client.get('/budgets/')
        assert b'0' in response.data

    def test_excludes_credit_transactions_from_spent(self, client, db):
        acct = _make_account()
        cat = _make_category('Groceries')
        _make_budget(cat, '400.00')
        _make_txn(acct, cat, '50.00', is_credit=True)
        response = client.get('/budgets/')
        # Should show $0 spent (credit transaction excluded)
        assert b'400' in response.data

    def test_excludes_prior_month_transactions(self, client, db):
        acct = _make_account()
        cat = _make_category('Shopping')
        _make_budget(cat, '300.00')
        _make_txn(acct, cat, '99.00', month_offset=-1)  # last month
        response = client.get('/budgets/')
        # Prior month transaction should not appear in "spent this month"
        assert response.status_code == 200

    def test_shows_no_budget_set_message(self, client, db):
        _make_category('Healthcare')
        response = client.get('/budgets/')
        assert b'No budget' in response.data or b'no budget' in response.data.lower()

    def test_shows_remaining_amount(self, client, db):
        acct = _make_account()
        cat = _make_category('Dining')
        _make_budget(cat, '200.00')
        _make_txn(acct, cat, '50.00')
        response = client.get('/budgets/')
        # Remaining = 200 - 50 = 150
        assert b'150' in response.data

    def test_progress_bar_rendered(self, client, db):
        cat = _make_category('Housing')
        _make_budget(cat, '500.00')
        response = client.get('/budgets/')
        assert b'fin-progress-bar' in response.data

    def test_progress_bar_safe_state_under_80_percent(self, client, db):
        acct = _make_account()
        cat = _make_category('Groceries')
        _make_budget(cat, '500.00')
        _make_txn(acct, cat, '300.00')  # 60% — safe
        response = client.get('/budgets/')
        assert b'fin-progress-bar-safe' in response.data

    def test_progress_bar_warning_state_80_to_99_percent(self, client, db):
        acct = _make_account()
        cat = _make_category('Dining')
        _make_budget(cat, '100.00')
        _make_txn(acct, cat, '85.00')  # 85% — warning
        response = client.get('/budgets/')
        assert b'fin-progress-bar-warning' in response.data

    def test_progress_bar_danger_state_at_100_percent(self, client, db):
        acct = _make_account()
        cat = _make_category('Shopping')
        _make_budget(cat, '100.00')
        _make_txn(acct, cat, '120.00')  # 120% — danger
        response = client.get('/budgets/')
        assert b'fin-progress-bar-danger' in response.data

    def test_chart_canvas_present(self, client, db):
        response = client.get('/budgets/')
        assert b'<canvas' in response.data or b'budgetChart' in response.data

    def test_recommend_link_present(self, client, db):
        response = client.get('/budgets/')
        assert b'/budgets/recommend' in response.data

    def test_active_page_is_budgets(self, client, db):
        response = client.get('/budgets/')
        assert b'budgets' in response.data
