"""Integration tests for JavaScript optimization API endpoints.

These tests call the actual API endpoints to verify end-to-end functionality.

NOTE: These tests require authentication mocking which is complex due to Django
middleware being instantiated at startup. The tests are marked to skip by default.
Run with: pytest -m integration tests/optimizer/test_javascript_api.py

For actual API testing, use curl or httpie against a running dev server with a valid
API key.
"""

import json
import uuid
from unittest.mock import patch

import pytest
from django.test import AsyncClient

# Mark all tests in this module as integration tests that skip by default
pytestmark = [
    pytest.mark.integration,
    pytest.mark.skip(reason="Integration tests require running server - run with -m integration --runintegration"),
]


@pytest.fixture
def api_client():
    """Create an async test client."""
    return AsyncClient()


@pytest.fixture
def valid_trace_id():
    """Generate a valid UUIDv4 trace ID."""
    return str(uuid.uuid4())


@pytest.fixture
def mock_auth(settings):
    """Mock authentication by patching the AuthBearer class method.

    Note: This mock is applied but Django middleware is instantiated at startup,
    so the mock may not take effect unless the server is restarted after patching.
    """
    from authapp.auth import AuthBearer

    # Store original authenticate method
    original_authenticate = AuthBearer.authenticate

    async def fake_authenticate(self, request, token):
        request.user = "test-user-123"
        request.tier = "pro"
        request.api_key_id = 1
        request.organization_id = "test-org"
        request.should_log_features = False
        return token

    # Create a mock subscription
    class FakeSubscription:
        subscription_status = "active"
        optimizations_used = 0
        optimizations_limit = 1000
        total_lifetime_optimizations = 0
        plan_type = "pro"

        async def asave(self, *args, **kwargs):
            return None

    class FakeFilter:
        async def afirst(self):
            return FakeSubscription()

        async def aupdate(self, **kwargs):
            return 1

    # Patch the method on the class (affects all instances)
    AuthBearer.authenticate = fake_authenticate  # type: ignore[method-assign]

    with patch("aiservice.middleware.track_usage_middleware.Subscriptions.objects.filter", return_value=FakeFilter()):
        yield

    # Restore original method
    AuthBearer.authenticate = original_authenticate


@pytest.fixture
def auth_headers():
    """Headers with authentication token."""
    return {"HTTP_AUTHORIZATION": "Bearer test-token-123"}


class TestOptimizeJavaScriptRouting:
    """Test that JavaScript optimization requests are properly routed."""

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_javascript_language_routes_correctly(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that language=javascript routes to JavaScript optimizer."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "function add(a, b) { return a + b; }",
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "language": "javascript",
                    "language_version": "ES2022",
                    "n_candidates": 1,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        # The request should be processed (may fail due to LLM mocking,
        # but should not fail due to routing)
        assert response.status_code in [200, 400, 500]

        # If it fails, check that it's not a "not implemented" error
        if response.status_code != 200:
            data = response.json()
            assert "not yet implemented" not in data.get("error", "").lower()

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_typescript_language_routes_correctly(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that language=typescript routes to JavaScript optimizer."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "function add(a: number, b: number): number { return a + b; }",
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "language": "typescript",
                    "language_version": "ES2022",
                    "n_candidates": 1,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code in [200, 400, 500]

        if response.status_code != 200:
            data = response.json()
            assert "not yet implemented" not in data.get("error", "").lower()

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_python_still_works(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that Python optimization still works (backward compatibility)."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "def add(a, b): return a + b",
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "language": "python",
                    "python_version": "3.11",
                    "n_candidates": 1,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code in [200, 400, 500]

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_default_language_is_python(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that omitting language defaults to Python."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "def add(a, b): return a + b",
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "python_version": "3.11",
                    "n_candidates": 1,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        # Should work as Python (the default)
        assert response.status_code in [200, 400, 500]


class TestOptimizeJavaScriptValidation:
    """Test JavaScript-specific validation in the API."""

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_invalid_javascript_syntax_rejected(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that invalid JavaScript syntax is rejected."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "function broken( { return; }",  # Invalid syntax
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "language": "javascript",
                    "n_candidates": 1,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code == 400
        data = response.json()
        assert "error" in data

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_empty_source_code_rejected(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that empty source code is rejected."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "",
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "language": "javascript",
                    "n_candidates": 1,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code == 400

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_invalid_trace_id_rejected(self, api_client, mock_auth, auth_headers):
        """Test that invalid trace ID is rejected."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "function add(a, b) { return a + b; }",
                    "dependency_code": None,
                    "trace_id": "not-a-valid-uuid",
                    "language": "javascript",
                    "n_candidates": 1,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code == 400
        data = response.json()
        assert "trace" in data.get("error", "").lower() or "uuid" in data.get("error", "").lower()


class TestTestGenJavaScriptRouting:
    """Test that JavaScript test generation requests are properly routed."""

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_javascript_testgen_routes_correctly(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that language=javascript routes to JavaScript testgen."""
        response = await api_client.post(
            "/ai/testgen/",
            data=json.dumps(
                {
                    "source_code_being_tested": "function add(a, b) { return a + b; }",
                    "function_to_optimize": {
                        "function_name": "add",
                        "file_path": "/test.js",
                        "parents": [],
                        "starting_line": 1,
                        "ending_line": 1,
                    },
                    "module_path": "./math",
                    "test_module_path": "./math.test",
                    "test_framework": "jest",
                    "test_timeout": 30,
                    "trace_id": valid_trace_id,
                    "language": "javascript",
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code in [200, 400, 500]

        if response.status_code != 200:
            data = response.json()
            assert "not yet implemented" not in data.get("error", "").lower()

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_invalid_test_framework_rejected(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that invalid test framework is rejected for JavaScript."""
        response = await api_client.post(
            "/ai/testgen/",
            data=json.dumps(
                {
                    "source_code_being_tested": "function add(a, b) { return a + b; }",
                    "function_to_optimize": {
                        "function_name": "add",
                        "file_path": "/test.js",
                        "parents": [],
                        "starting_line": 1,
                        "ending_line": 1,
                    },
                    "module_path": "./math",
                    "test_module_path": "./math.test",
                    "test_framework": "pytest",  # Wrong framework for JS
                    "test_timeout": 30,
                    "trace_id": valid_trace_id,
                    "language": "javascript",
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code == 400
        data = response.json()
        assert "framework" in data.get("error", "").lower()


class TestSchemaValidation:
    """Test that API schema validation works correctly."""

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_language_field_accepted(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that the language field is accepted in the API."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "const x = 1;",
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "language": "javascript",
                    "language_version": "ES2022",
                    "n_candidates": 0,  # 0 candidates to avoid LLM call
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        # Should not fail schema validation (may fail elsewhere)
        assert response.status_code != 422  # 422 = schema validation error

    @pytest.mark.asyncio
    @pytest.mark.django_db
    async def test_language_version_field_accepted(self, api_client, valid_trace_id, mock_auth, auth_headers):
        """Test that the language_version field is accepted in the API."""
        response = await api_client.post(
            "/ai/optimize/",
            data=json.dumps(
                {
                    "source_code": "const x = 1;",
                    "dependency_code": None,
                    "trace_id": valid_trace_id,
                    "language": "javascript",
                    "language_version": "ES2020",
                    "n_candidates": 0,
                }
            ),
            content_type="application/json",
            **auth_headers,
        )

        assert response.status_code != 422
