# -*- coding: utf-8 -*-
"""Location: ./tests/fuzz/test_api_schema_fuzz.py
Copyright 2026
SPDX-License-Identifier: Apache-2.0
Authors: Mihai Criveti

Schemathesis-based API endpoint fuzzing.
"""

# Third-Party
from fastapi.testclient import TestClient
import pytest

# First-Party
from mcpgateway.main import app


class TestAPIEndpointFuzzing:
    """API endpoint fuzzing without schema dependency."""

    @pytest.mark.skip("Schemathesis schema loading requires auth configuration - use manual testing for now")
    def test_api_schema_fuzzing_placeholder(self):
        """Placeholder for future schema-based fuzzing."""
        pass


class TestAPIFuzzingCustom:
    """Custom API fuzzing scenarios not covered by schema."""

    def test_authentication_fuzzing(self):
        """Test authentication with various malformed credentials."""
        client = TestClient(app)

        auth_variants = [
            "Basic invalid",
            "Bearer token123",
            "Basic " + "x" * 1000,  # Very long auth
            "Digest username=test",
            "Negotiate token",
            "",
            None,
            "Basic",  # Incomplete
            "Basic " + ":" * 100,  # Many colons
            "Basic " + "=" * 50,  # Many equals
        ]

        for auth in auth_variants:
            headers = {"Authorization": auth} if auth else {}
            response = client.get("/admin/tools", headers=headers)
            # Should return 401 or handle gracefully
            assert response.status_code in [401, 400, 422], f"Unexpected status for auth '{auth}': {response.status_code}"

    def test_large_payload_fuzzing(self):
        """Test endpoints with very large payloads."""
        client = TestClient(app)

        # Very large tool creation payload
        large_payload = {
            "name": "test_tool",
            "url": "http://example.com",
            "description": "x" * 10000,  # 10KB description
            "headers": {f"header_{i}": f"value_{i}" * 100 for i in range(50)},  # Many headers
            "tags": [f"tag_{i}" for i in range(1000)],  # Many tags
        }

        response = client.post("/admin/tools", json=large_payload, headers={"Authorization": "Basic YWRtaW46Y2hhbmdlbWU="})

        # Should handle large payloads gracefully (may reject or accept)
        assert response.status_code in [200, 201, 400, 401, 413, 422]

    def test_malformed_json_fuzzing(self):
        """Test endpoints with malformed JSON."""
        client = TestClient(app)

        malformed_json_cases = [
            '{"incomplete":',
            '{"key": "value",}',  # Trailing comma
            '{"key": value}',  # Unquoted value
            '{key: "value"}',  # Unquoted key
            '{"nested": {"incomplete"}',
            "[]",  # Array instead of object
            '"string"',  # String instead of object
            "123",  # Number instead of object
            "null",  # Null instead of object
            '{"unicode": "\\uXXXX"}',  # Invalid unicode
        ]

        for malformed in malformed_json_cases:
            response = client.post("/admin/tools", data=malformed, headers={"Authorization": "Basic YWRtaW46Y2hhbmdlbWU=", "Content-Type": "application/json"})

            # Should handle malformed JSON gracefully
            assert response.status_code in [400, 401, 422], f"Unexpected status for malformed JSON: {response.status_code}"

    def test_unicode_fuzzing(self):
        """Test endpoints with various unicode characters."""
        client = TestClient(app)

        unicode_test_cases = [
            {"name": "test_ñäme", "url": "http://example.com"},
            {"name": "测试工具", "url": "http://example.com"},
            {"name": "🚀🔧⚡", "url": "http://example.com"},  # Emoji
            {"name": "\x00\x01\x02", "url": "http://example.com"},  # Control chars
            {"name": "A" * 1000, "url": "http://example.com"},  # Long ASCII
            {"name": "ñ" * 1000, "url": "http://example.com"},  # Long unicode
        ]

        for test_case in unicode_test_cases:
            response = client.post("/admin/tools", json=test_case, headers={"Authorization": "Basic YWRtaW46Y2hhbmdlbWU="})

            # Should handle unicode gracefully
            assert response.status_code in [200, 201, 400, 401, 422]

    def test_concurrent_request_fuzzing(self):
        """Test concurrent requests to check for race conditions."""
        # Standard
        import threading
        import time

        client = TestClient(app)
        results = []

        def make_request():
            try:
                response = client.post("/admin/tools", json={"name": f"tool_{time.time()}", "url": "http://example.com"}, headers={"Authorization": "Basic YWRtaW46Y2hhbmdlbWU="})
                results.append(response.status_code)
            except Exception as e:
                results.append(f"Exception: {e}")

        # Start multiple threads
        threads = []
        for _ in range(10):
            thread = threading.Thread(target=make_request)
            threads.append(thread)
            thread.start()

        # Wait for all threads to complete
        for thread in threads:
            thread.join()

        # All requests should complete successfully or with expected errors
        for result in results:
            if isinstance(result, int):
                assert result in [200, 201, 400, 401, 422, 409], f"Unexpected concurrent status: {result}"
            else:
                # No exceptions should occur
                pytest.fail(f"Concurrent request failed: {result}")

    def test_content_type_fuzzing(self):
        """Test endpoints with various Content-Type headers."""
        client = TestClient(app)

        content_types = [
            "application/json",
            "application/json; charset=utf-8",
            "text/plain",
            "application/xml",
            "multipart/form-data",
            "application/x-www-form-urlencoded",
            "text/html",
            "image/png",
            "",
            None,
            "application/json; boundary=something",
            "application/json" + "x" * 1000,  # Very long
        ]

        test_data = '{"name": "test", "url": "http://example.com"}'

        for content_type in content_types:
            headers = {"Authorization": "Basic YWRtaW46Y2hhbmdlbWU="}
            if content_type is not None:
                headers["Content-Type"] = content_type

            response = client.post("/admin/tools", data=test_data, headers=headers)

            # Should handle various content types gracefully
            assert response.status_code in [200, 201, 400, 401, 415, 422]
