# SPDX-License-Identifier: Apache-2.0
# Standard
from pathlib import Path
from unittest.mock import MagicMock
import json

# Third Party
from fastapi.testclient import TestClient
import pytest

# First Party
from lmcache.v1.config import LMCacheEngineConfig
from lmcache.v1.internal_api_server.api_server import app


def _get_test_scripts_static():
    """Static function to get test scripts for parametrize."""
    script_dir = Path(__file__).parent / "test_scripts"
    scripts = []
    for script_file in sorted(script_dir.glob("*.py")):
        content = script_file.read_text()
        # Find TEST_METADATA line
        lines = content.split("\n")
        metadata_lines = []
        in_metadata = False
        brace_count = 0

        for line in lines:
            if line.strip().startswith("# TEST_METADATA: {"):
                in_metadata = True
                # Extract JSON part after ': '
                json_part = line.split("# TEST_METADATA: ", 1)[1]
                metadata_lines.append(json_part)
                brace_count = json_part.count("{") - json_part.count("}")
            elif in_metadata and line.strip().startswith("#"):
                # Continuation line in comment
                json_part = line.strip()[1:].strip()  # Remove '#' and trim
                metadata_lines.append(json_part)
                brace_count += json_part.count("{") - json_part.count("}")

                # Check if we've closed all braces
                if brace_count == 0 and "}" in json_part:
                    in_metadata = False
            elif in_metadata:
                # Not a comment line, metadata block ended
                in_metadata = False

        if metadata_lines:
            json_str = " ".join(metadata_lines)
            metadata = json.loads(json_str)
            scripts.append((script_file.name, metadata))
    return scripts


class TestRunScriptAPI:
    """Test suite for the /run_script API endpoint."""

    @pytest.fixture
    def mock_lmcache_adapter_with_config(self):
        """Create a mock LMCacheConnectorV1Impl adapter with config."""
        adapter = MagicMock()
        config = LMCacheEngineConfig.from_defaults()
        adapter.config = config
        return adapter

    @pytest.fixture
    def client_with_adapter(self, mock_lmcache_adapter_with_config):
        """Create a test client with mocked adapter."""
        app.state.lmcache_adapter = mock_lmcache_adapter_with_config
        app.state.lmcache_engine = mock_lmcache_adapter_with_config.lmcache_engine
        return TestClient(app)

    @pytest.fixture
    def script_dir(self):
        """Get the test scripts directory."""
        return Path(__file__).parent / "test_scripts"

    @pytest.mark.parametrize(
        "script_name,metadata",
        [
            pytest.param(name, meta, id=name.replace(".py", ""))
            for name, meta in _get_test_scripts_static()
        ],
    )
    def test_run_script_from_file(
        self,
        client_with_adapter,
        script_dir,
        script_name,
        metadata,
    ):
        """Test script execution by directly uploading script files."""
        script_path = script_dir / script_name

        # Configure allowed imports
        allowed_imports = metadata.get("allowed_imports", [])
        client_with_adapter.app.state.lmcache_adapter.config.script_allowed_imports = (
            allowed_imports
        )

        # Upload the script file directly
        with open(script_path, "rb") as f:
            files = {"script": (script_name, f, "text/plain")}
            response = client_with_adapter.post("/run_script", files=files)

        # Verify status code
        expected_status = metadata.get("expected_status", 200)
        assert response.status_code == expected_status, (
            f"Expected status {expected_status}, got {response.status_code}"
        )

        # Verify response content
        if "expected_result" in metadata:
            assert response.text == metadata["expected_result"], (
                f"Expected '{metadata['expected_result']}', got '{response.text}'"
            )

        if "expected_contains" in metadata:
            contains_list = metadata["expected_contains"]
            if isinstance(contains_list, str):
                contains_list = [contains_list]
            for expected_text in contains_list:
                assert expected_text in response.text, (
                    f"Expected '{expected_text}' in response"
                )

        if "expected_min_length" in metadata:
            assert len(response.text) >= metadata["expected_min_length"], (
                f"Response too short: {len(response.text)}"
            )

    def test_run_script_no_file(self, client_with_adapter):
        """Test run_script with no file provided."""
        response = client_with_adapter.post("/run_script")

        assert response.status_code == 400
        assert "No script file provided" in response.text
