"""
Account setup tests — Story 3.1.

Tests cover: list, create, edit, delete, and delete-with-transactions guard.
All tests use the db fixture to set up in-memory tables.
"""
import pytest
from app.models.account import Account
from app.models.transaction import Transaction
from app.extensions import db as _db


# ── Helpers ──────────────────────────────────────────────────────────────────

def make_account(name='Test Bank', type_='checking', institution=None):
    acct = Account(name=name, type=type_, institution_name=institution, is_active=True)
    _db.session.add(acct)
    _db.session.commit()
    return acct


# ── Settings landing ──────────────────────────────────────────────────────────

class TestSettingsIndex:
    def test_settings_index_returns_200(self, client, db):
        response = client.get('/settings/')
        assert response.status_code == 200

    def test_settings_index_links_to_accounts(self, client, db):
        response = client.get('/settings/')
        assert b'/settings/accounts' in response.data


# ── Accounts list ─────────────────────────────────────────────────────────────

class TestAccountsList:
    def test_accounts_page_returns_200(self, client, db):
        response = client.get('/settings/accounts')
        assert response.status_code == 200

    def test_accounts_page_shows_add_form(self, client, db):
        response = client.get('/settings/accounts')
        assert b'Add Account' in response.data

    def test_accounts_page_lists_existing_accounts(self, client, db):
        make_account(name='Chase Checking', type_='checking', institution='Chase')
        response = client.get('/settings/accounts')
        assert b'Chase Checking' in response.data
        assert b'Chase' in response.data

    def test_accounts_page_shows_type(self, client, db):
        make_account(name='My Savings', type_='savings')
        response = client.get('/settings/accounts')
        assert b'My Savings' in response.data


# ── Create account ────────────────────────────────────────────────────────────

class TestCreateAccount:
    def test_valid_post_creates_account(self, client, db):
        response = client.post('/settings/accounts', data={
            'name': 'Chase Checking',
            'type': 'checking',
            'institution_name': 'Chase',
        }, follow_redirects=False)
        assert response.status_code == 302
        acct = Account.query.filter_by(name='Chase Checking').first()
        assert acct is not None
        assert acct.type == 'checking'
        assert acct.institution_name == 'Chase'
        assert acct.is_active is True

    def test_valid_post_redirects_to_list(self, client, db):
        response = client.post('/settings/accounts', data={
            'name': 'Savings',
            'type': 'savings',
        }, follow_redirects=False)
        assert response.status_code == 302
        assert '/settings/accounts' in response.headers['Location']

    def test_valid_post_flashes_success(self, client, db):
        response = client.post('/settings/accounts', data={
            'name': 'Cash',
            'type': 'checking',
        }, follow_redirects=True)
        assert b'Account added' in response.data

    def test_missing_name_shows_error(self, client, db):
        response = client.post('/settings/accounts', data={
            'name': '',
            'type': 'checking',
        })
        assert response.status_code == 200
        assert Account.query.count() == 0

    def test_institution_name_optional(self, client, db):
        response = client.post('/settings/accounts', data={
            'name': 'Wallet',
            'type': 'checking',
        }, follow_redirects=False)
        assert response.status_code == 302
        acct = Account.query.filter_by(name='Wallet').first()
        assert acct is not None
        assert acct.institution_name is None


# ── Edit account ──────────────────────────────────────────────────────────────

class TestEditAccount:
    def test_edit_get_returns_200(self, client, db):
        acct = make_account()
        response = client.get(f'/settings/accounts/{acct.id}/edit')
        assert response.status_code == 200

    def test_edit_get_prefills_form(self, client, db):
        acct = make_account(name='My Bank', type_='savings')
        response = client.get(f'/settings/accounts/{acct.id}/edit')
        assert b'My Bank' in response.data

    def test_edit_post_updates_account(self, client, db):
        acct = make_account(name='Old Name')
        response = client.post(f'/settings/accounts/{acct.id}/edit', data={
            'name': 'New Name',
            'type': 'savings',
            'institution_name': 'Wells Fargo',
        }, follow_redirects=False)
        assert response.status_code == 302
        _db.session.refresh(acct)
        assert acct.name == 'New Name'
        assert acct.type == 'savings'

    def test_edit_post_flashes_success(self, client, db):
        acct = make_account()
        response = client.post(f'/settings/accounts/{acct.id}/edit', data={
            'name': 'Updated',
            'type': 'checking',
        }, follow_redirects=True)
        assert b'Account updated' in response.data

    def test_edit_nonexistent_returns_404(self, client, db):
        response = client.get('/settings/accounts/9999/edit')
        assert response.status_code == 404


# ── Delete account ────────────────────────────────────────────────────────────

class TestDeleteAccount:
    def test_delete_no_transactions_hard_deletes(self, client, db):
        acct = make_account()
        acct_id = acct.id
        response = client.post(f'/settings/accounts/{acct_id}/delete',
                               follow_redirects=False)
        assert response.status_code == 302
        assert Account.query.get(acct_id) is None

    def test_delete_flashes_success(self, client, db):
        acct = make_account()
        response = client.post(f'/settings/accounts/{acct.id}/delete',
                               follow_redirects=True)
        assert b'Account deleted' in response.data

    def test_delete_redirects_to_list(self, client, db):
        acct = make_account()
        response = client.post(f'/settings/accounts/{acct.id}/delete',
                               follow_redirects=False)
        assert '/settings/accounts' in response.headers['Location']

    def test_delete_account_with_transactions_is_blocked(self, client, db):
        from app.models.category import Category
        acct = make_account()
        cat = Category(name='Groceries', is_system=True, is_active=True)
        _db.session.add(cat)
        _db.session.commit()
        txn = Transaction(
            date='2026-05-01',
            merchant_normalized='ALDI',
            amount='29.99',
            account_id=acct.id,
            category_id=cat.id,
            is_manual=True,
        )
        _db.session.add(txn)
        _db.session.commit()
        response = client.post(f'/settings/accounts/{acct.id}/delete',
                               follow_redirects=True)
        assert b'Account in use' in response.data
        assert Account.query.get(acct.id) is not None

    def test_delete_nonexistent_returns_404(self, client, db):
        response = client.post('/settings/accounts/9999/delete')
        assert response.status_code == 404
