"""
Security tests for OWASP Top 10 vulnerabilities
=================================================

Tests for common web application security vulnerabilities.
"""

import pytest

pytestmark = pytest.mark.security


class TestSQLInjection:
    """Tests for SQL injection vulnerabilities."""

    def test_search_sql_injection(self, client):
        """Search should be safe from SQL injection."""
        payloads = [
            "'; DROP TABLE companies; --",
            "1' OR '1'='1",
            "1; DELETE FROM users WHERE '1'='1",
            "' UNION SELECT * FROM users --",
            "admin'--",
        ]

        for payload in payloads:
            response = client.get(f'/search?q={payload}')
            # Should not crash - 200, 302 (redirect), or 400 are acceptable
            assert response.status_code in [200, 302, 400], f"Unexpected status for SQL injection test: {response.status_code}"

    def test_login_sql_injection(self, client):
        """Login should be safe from SQL injection."""
        payloads = [
            ("admin' OR '1'='1", "anything"),
            ("admin'--", "anything"),
            ("' OR 1=1--", "' OR 1=1--"),
        ]

        for email, password in payloads:
            response = client.post('/login', data={
                'email': email,
                'password': password,
            })
            # Should not log in with injection
            assert response.status_code in [200, 302, 400]
            # If 200, should show login page (not dashboard)


class TestXSS:
    """Tests for Cross-Site Scripting vulnerabilities."""

    def test_search_xss_escaped(self, client):
        """Search results should escape XSS payloads."""
        payloads = [
            '<script>alert("xss")</script>',
            '<img src=x onerror=alert("xss")>',
            '"><script>alert("xss")</script>',
            "javascript:alert('xss')",
        ]

        for payload in payloads:
            response = client.get(f'/search?q={payload}', follow_redirects=True)

            assert response.status_code == 200
            # Payload should be escaped, not raw
            assert b'<script>alert' not in response.data
            assert b'onerror=alert' not in response.data

    def test_company_name_xss_escaped(self, admin_client):
        """Company names should escape XSS in display."""
        # This test assumes admin can create companies
        # Testing that display escapes properly
        response = admin_client.get('/companies')

        assert response.status_code == 200
        # Should not contain unescaped script tags
        assert b'<script>alert' not in response.data


class TestCSRF:
    """Tests for Cross-Site Request Forgery protection."""

    def test_login_without_csrf_fails(self, app, client):
        """Login without CSRF token should fail when CSRF is enabled."""
        # Temporarily enable CSRF
        original = app.config.get('WTF_CSRF_ENABLED', False)
        app.config['WTF_CSRF_ENABLED'] = True

        try:
            response = client.post('/login', data={
                'email': 'test@test.pl',
                'password': 'password',
            })
            # Should fail with 400, 403, or redirect (302)
            assert response.status_code in [400, 403, 302, 200]
        finally:
            app.config['WTF_CSRF_ENABLED'] = original

    def test_forms_have_csrf_token(self, client):
        """Forms should include CSRF token."""
        response = client.get('/login')

        assert response.status_code == 200
        text = response.data.decode('utf-8').lower()
        # Check for hidden CSRF field
        assert 'csrf_token' in text or 'csrf' in text


class TestAuthentication:
    """Tests for authentication security."""

    def test_protected_routes_require_auth(self, client):
        """Protected routes should redirect unauthenticated users."""
        protected_routes = [
            '/dashboard',
            '/admin/companies',
            '/chat',
        ]

        for route in protected_routes:
            response = client.get(route)
            # Should redirect to login or return 401/403/404
            assert response.status_code in [302, 401, 403, 404], f"{route} is not protected"

    def test_admin_routes_require_admin(self, auth_client):
        """Admin routes should require admin role."""
        admin_routes = [
            '/admin/companies',
            '/admin/users',
            '/admin/security',
        ]

        for route in admin_routes:
            response = auth_client.get(route)
            # Regular user should get 403
            assert response.status_code in [302, 403], f"{route} accessible by non-admin"

    def test_password_not_in_response(self, client, admin_client):
        """Passwords should never appear in responses."""
        routes = [
            '/admin/users',
            '/api/users',
        ]

        for route in routes:
            response = admin_client.get(route)
            if response.status_code == 200:
                # Should not contain password field with value
                assert b'password' not in response.data.lower() or b'password":"' not in response.data


class TestRateLimiting:
    """Tests for rate limiting."""

    def test_login_rate_limited(self, client):
        """Login should be rate limited."""
        # Make many requests quickly
        for i in range(100):
            response = client.post('/login', data={
                'email': f'attacker{i}@test.pl',
                'password': 'wrongpassword',
            })

            if response.status_code == 429:
                # Rate limit triggered - test passes
                return

        # If we get here, no rate limit was triggered
        # This might be OK in testing mode
        pytest.skip("Rate limiting may be disabled in testing")

    def test_api_rate_limited(self, client):
        """API endpoints should be rate limited."""
        for i in range(200):
            response = client.get('/api/companies')

            if response.status_code == 429:
                return

        pytest.skip("Rate limiting may be disabled in testing")


class TestSecurityHeaders:
    """Tests for security headers."""

    def test_security_headers_present(self, client):
        """Response should include security headers."""
        response = client.get('/')

        # These headers should be present
        expected_headers = [
            # 'X-Content-Type-Options',  # nosniff
            # 'X-Frame-Options',  # DENY or SAMEORIGIN
            # 'Content-Security-Policy',
        ]

        for header in expected_headers:
            if header in response.headers:
                assert response.headers[header]
