"""
Category management view tests — Story 3.2.
Tests cover: list, create, rename, soft-delete, and guards.
"""
import pytest
from app.models.category import Category
from app.models.transaction import Transaction
from app.models.account import Account
from app.extensions import db as _db


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

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


def seed_custom(name='Custom'):
    cat = Category(name=name, is_system=False, is_active=True)
    _db.session.add(cat)
    _db.session.commit()
    return cat


# ── List page ─────────────────────────────────────────────────────────────────

class TestCategoriesList:
    def test_page_returns_200(self, client, db):
        response = client.get('/settings/categories')
        assert response.status_code == 200

    def test_shows_system_badge(self, client, db):
        seed_system('Groceries')
        response = client.get('/settings/categories')
        assert b'System' in response.data

    def test_system_category_has_no_delete_button(self, client, db):
        cat = seed_system('Groceries')
        response = client.get('/settings/categories')
        assert f'/settings/categories/{cat.id}/delete'.encode() not in response.data

    def test_custom_category_has_delete_button(self, client, db):
        cat = seed_custom('Travel')
        response = client.get('/settings/categories')
        assert f'/settings/categories/{cat.id}/delete'.encode() in response.data

    def test_inactive_categories_not_shown(self, client, db):
        cat = Category(name='Hidden', is_system=False, is_active=False)
        _db.session.add(cat)
        _db.session.commit()
        response = client.get('/settings/categories')
        assert b'Hidden' not in response.data

    def test_shows_add_form(self, client, db):
        response = client.get('/settings/categories')
        assert b'New Category' in response.data or b'Add Category' in response.data


# ── Create ────────────────────────────────────────────────────────────────────

class TestCreateCategory:
    def test_valid_post_creates_category(self, client, db):
        response = client.post('/settings/categories', data={'name': 'Travel'},
                               follow_redirects=False)
        assert response.status_code == 302
        assert Category.query.filter_by(name='Travel').first() is not None

    def test_redirects_to_list(self, client, db):
        response = client.post('/settings/categories', data={'name': 'Travel'},
                               follow_redirects=False)
        assert '/settings/categories' in response.headers['Location']

    def test_flashes_success(self, client, db):
        response = client.post('/settings/categories', data={'name': 'Travel'},
                               follow_redirects=True)
        assert b'Category added' in response.data

    def test_empty_name_shows_error(self, client, db):
        response = client.post('/settings/categories', data={'name': ''},
                               follow_redirects=False)
        assert response.status_code == 200
        assert Category.query.count() == 0

    def test_name_too_long_rejected(self, client, db):
        response = client.post('/settings/categories', data={'name': 'x' * 51},
                               follow_redirects=False)
        assert response.status_code == 200
        assert Category.query.count() == 0

    def test_duplicate_name_flashes_error(self, client, db):
        seed_custom('Travel')
        response = client.post('/settings/categories', data={'name': 'Travel'},
                               follow_redirects=True)
        assert b'already exists' in response.data


# ── Edit / Rename ─────────────────────────────────────────────────────────────

class TestEditCategory:
    def test_edit_get_returns_200(self, client, db):
        cat = seed_custom('Travel')
        response = client.get(f'/settings/categories/{cat.id}/edit')
        assert response.status_code == 200

    def test_edit_prefills_name(self, client, db):
        cat = seed_custom('Travel')
        response = client.get(f'/settings/categories/{cat.id}/edit')
        assert b'Travel' in response.data

    def test_valid_rename_updates(self, client, db):
        cat = seed_custom('Old Name')
        client.post(f'/settings/categories/{cat.id}/edit', data={'name': 'New Name'},
                    follow_redirects=False)
        _db.session.refresh(cat)
        assert cat.name == 'New Name'

    def test_valid_rename_flashes_success(self, client, db):
        cat = seed_custom('Old Name')
        response = client.post(f'/settings/categories/{cat.id}/edit',
                               data={'name': 'New Name'}, follow_redirects=True)
        assert b'Category updated' in response.data

    def test_duplicate_rename_flashes_error(self, client, db):
        seed_custom('Existing')
        cat = seed_custom('Target')
        response = client.post(f'/settings/categories/{cat.id}/edit',
                               data={'name': 'Existing'}, follow_redirects=True)
        assert b'already exists' in response.data

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


# ── Delete ────────────────────────────────────────────────────────────────────

class TestDeleteCategory:
    def test_delete_unused_custom_soft_deletes(self, client, db):
        cat = seed_custom('Unused')
        client.post(f'/settings/categories/{cat.id}/delete')
        _db.session.refresh(cat)
        assert cat.is_active is False

    def test_delete_flashes_success(self, client, db):
        cat = seed_custom('Unused')
        response = client.post(f'/settings/categories/{cat.id}/delete',
                               follow_redirects=True)
        assert b'Category deleted' in response.data

    def test_delete_system_category_returns_403(self, client, db):
        cat = seed_system('Groceries')
        response = client.post(f'/settings/categories/{cat.id}/delete')
        assert response.status_code == 403

    def test_delete_with_transactions_flashes_error(self, client, db):
        acct = Account(name='Bank', type='checking', is_active=True)
        cat = seed_custom('Used')
        _db.session.add(acct)
        _db.session.commit()
        txn = Transaction(
            date='2026-05-01', merchant_normalized='Shop',
            amount='10.00', account_id=acct.id, category_id=cat.id,
        )
        _db.session.add(txn)
        _db.session.commit()
        response = client.post(f'/settings/categories/{cat.id}/delete',
                               follow_redirects=True)
        assert b'in use' in response.data
        _db.session.refresh(cat)
        assert cat.is_active is True

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