"""
Tests for authentication, CSRF, login enumeration, and rate limiting.
"""
import pytest, json


class TestLoginEnumeration:
    def test_login_page_has_no_user_list(self, client):
        """Login page must NOT expose a user dropdown or any user names."""
        r = client.get('/login')
        assert r.status_code == 200
        html = r.data.decode()
        # Old behaviour leaked names like 'Tom Otieno' in an <option> tag
        assert 'Tom Otieno' not in html
        assert 'select' not in html.lower() or 'name="user_id"' not in html
        # Must have a text input for username
        assert 'name="username"' in html

    def test_invalid_credentials_generic_message(self, client):
        """Failed login must return the same message regardless of whether user exists."""
        r = client.post('/login', data={'username': 'nonexistent_user_xyz', 'password': 'wrong'})
        assert b'Invalid credentials' in r.data
        # Must not reveal whether the user exists
        assert b'user not found' not in r.data.lower()
        assert b'wrong password' not in r.data.lower()

    def test_valid_credentials_redirect(self, client):
        r = client.post('/login', data={'username': 'General Manager', 'password': 'gm123'},
                        follow_redirects=False)
        assert r.status_code in (301, 302)


class TestCSRF:
    def test_api_post_without_csrf_rejected(self, client):
        """State-changing API calls without CSRF token must be rejected."""
        # Log in first to get a session
        client.post('/login', data={'username': 'General Manager', 'password': 'gm123'})
        # Remove CSRF header (don't send X-CSRFToken)
        r = client.post('/api/products',
                        data=json.dumps({'name': 'Test', 'price': 10, 'category': 'Food'}),
                        content_type='application/json')
        assert r.status_code == 403
        data = json.loads(r.data)
        assert 'csrf' in data.get('error', '').lower()

    def test_api_post_with_csrf_accepted(self, gm_client):
        """State-changing API calls with valid CSRF token must proceed."""
        r = gm_client.post('/api/outlets',
                           data=json.dumps({'name': 'Test Outlet', 'location': 'CBD'}),
                           content_type='application/json',
                           headers={'X-CSRFToken': 'test-csrf-token'})
        # 200 or 201 means it got past CSRF check
        assert r.status_code in (200, 201)


class TestBruteForce:
    def test_lockout_after_max_attempts(self, client):
        """After LOGIN_MAX_ATTEMPTS failures, further attempts must be blocked."""
        from app import LOGIN_MAX_ATTEMPTS
        for _ in range(LOGIN_MAX_ATTEMPTS + 1):
            client.post('/login', data={'username': 'locktest_user', 'password': 'wrong'})
        r = client.post('/login', data={'username': 'locktest_user', 'password': 'wrong'})
        assert b'Too many failed attempts' in r.data or b'locked' in r.data.lower()


class TestSessionSecurity:
    def test_protected_route_requires_login(self, client):
        r = client.get('/dashboard', follow_redirects=False)
        assert r.status_code in (301, 302)
        assert '/login' in r.headers.get('Location', '')

    def test_logout_clears_session(self, client):
        client.post('/login', data={'username': 'General Manager', 'password': 'gm123'})
        client.get('/logout')
        r = client.get('/dashboard', follow_redirects=False)
        assert r.status_code in (301, 302)
