"""
Tests for VS Code stop propagation to cloud.

Verifies that:
1. When a job is cancelled via API, Docker containers are killed
2. The cancellation flow works end-to-end
3. Both scheduler and direct cancellation paths kill containers
"""

from unittest.mock import AsyncMock, patch

import pytest


class TestCancellationKillsContainers:
    """Test that job cancellation properly kills Docker containers."""

    @pytest.mark.asyncio
    async def test_local_job_handler_cancel_kills_container(self):
        """Test LocalJobHandler.cancel() kills Docker container."""
        from verlex_server.gateway.backend.scheduler_service import LocalJobHandler

        handler = LocalJobHandler()
        job_id = "test-job-12345678-1234"

        # Mock asyncio.create_subprocess_exec
        mock_proc = AsyncMock()
        mock_proc.wait = AsyncMock(return_value=None)
        mock_proc.returncode = 0

        with patch('asyncio.create_subprocess_exec', return_value=mock_proc) as mock_exec:
            result = await handler.cancel(job_id)

            # Should have tried to kill the container
            # Either via tracked container or by convention
            assert mock_exec.called or not result  # May not be in _running_jobs

    @pytest.mark.asyncio
    async def test_local_job_handler_kill_container_method(self):
        """Test _kill_container method directly."""
        from verlex_server.gateway.backend.scheduler_service import LocalJobHandler

        handler = LocalJobHandler()
        container_name = "gateway-testjob123"

        mock_proc = AsyncMock()
        mock_proc.wait = AsyncMock(return_value=None)
        mock_proc.returncode = 0

        with patch('asyncio.create_subprocess_exec', return_value=mock_proc) as mock_exec:
            result = await handler._kill_container(container_name)

            # Verify docker kill was called with correct container name
            mock_exec.assert_called_once()
            call_args = mock_exec.call_args[0]
            assert "docker" in call_args
            assert "kill" in call_args
            assert container_name in call_args
            assert result

    @pytest.mark.asyncio
    async def test_scheduler_cancel_job_kills_container(self):
        """Test SchedulerService.cancel_job() kills Docker container."""
        from verlex_server.gateway.backend.scheduler_service import (
            SchedulerConfig,
            SchedulerService,
        )

        # Create scheduler with mocked dependencies
        with patch('verlex_server.gateway.backend.scheduler_service.get_result_manager'), \
             patch('verlex_server.gateway.backend.scheduler_service.get_state_manager'), \
             patch('verlex_server.gateway.backend.scheduler_service.get_queue_manager'):

            config = SchedulerConfig()
            scheduler = SchedulerService(config=config)

            job_id = "test-cancel-job-123456"

            # Mock the docker kill subprocess
            mock_proc = AsyncMock()
            mock_proc.wait = AsyncMock(return_value=None)
            mock_proc.returncode = 0

            # Mock state_manager.get_job to return None (job not in state machine)
            scheduler.state_manager.get_job = AsyncMock(return_value=None)

            with patch('asyncio.create_subprocess_exec', return_value=mock_proc):
                result = await scheduler.cancel_job(job_id)

                # Should return False (job not found) but still tried to kill container
                # The _kill_container_by_job_id is called when job is in _active_jobs
                # or in the state manager path
                assert not result  # Job wasn't in active jobs or state manager

    @pytest.mark.asyncio
    async def test_kill_container_by_job_id_uses_convention_name(self):
        """Test that container name follows gateway-{job_id[:12]} convention."""
        from verlex_server.gateway.backend.scheduler_service import (
            SchedulerConfig,
            SchedulerService,
        )

        with patch('verlex_server.gateway.backend.scheduler_service.get_result_manager'), \
             patch('verlex_server.gateway.backend.scheduler_service.get_state_manager'), \
             patch('verlex_server.gateway.backend.scheduler_service.get_queue_manager'):

            scheduler = SchedulerService(config=SchedulerConfig())

            job_id = "abcdef123456-7890-full-uuid"
            expected_container = f"gateway-{job_id[:12]}"  # gateway-abcdef123456

            mock_proc = AsyncMock()
            mock_proc.wait = AsyncMock(return_value=None)
            mock_proc.returncode = 0

            with patch('asyncio.create_subprocess_exec', return_value=mock_proc) as mock_exec:
                await scheduler._kill_container_by_job_id(job_id)

                # Verify correct container name was used
                mock_exec.assert_called_once()
                call_args = mock_exec.call_args[0]
                assert expected_container in call_args


class TestAPIFallbackCancellation:
    """Test the API cancel endpoint fallback container killing."""

    def test_cancel_endpoint_exists(self):
        """Verify the cancel endpoint is registered."""
        from verlex_server.gateway.backend.api import create_app
        app = create_app()

        routes = [r.path for r in app.routes if hasattr(r, 'path')]
        assert "/v1/jobs/{job_id}/cancel" in routes

    def test_cancel_endpoint_is_post(self):
        """Verify cancel uses POST method."""
        from verlex_server.gateway.backend.api import create_app
        app = create_app()

        for route in app.routes:
            if hasattr(route, 'path') and route.path == "/v1/jobs/{job_id}/cancel":
                assert "POST" in route.methods
                break


class TestVSCodeExtensionDeactivate:
    """Test that VS Code extension cancellation triggers cloud stop."""

    @pytest.fixture
    def extension_js_path(self):
        """Get the path to the compiled extension.js file."""
        import os
        # Try relative path from project root
        project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        return os.path.join(project_root, "vscode-extension", "out", "extension.js")

    def test_deactivate_function_cancels_active_jobs(self, extension_js_path):
        """Verify deactivate() in extension.ts cancels active jobs."""
        import os
        import subprocess

        if not os.path.exists(extension_js_path):
            pytest.skip("VSCode extension not compiled (extension.js not found)")

        # Read the compiled extension to verify cancellation logic
        result = subprocess.run(
            ["grep", "-c", "cancelJob", extension_js_path],
            capture_output=True,
            text=True,
        )

        # Should have multiple references to cancelJob (in deactivate and elsewhere)
        count = int(result.stdout.strip()) if result.returncode == 0 else 0
        assert count >= 2, f"Expected multiple cancelJob calls, found {count}"

    def test_active_jobs_tracking_exists(self, extension_js_path):
        """Verify activeJobs Set is defined for tracking."""
        import os
        import subprocess

        if not os.path.exists(extension_js_path):
            pytest.skip("VSCode extension not compiled (extension.js not found)")

        result = subprocess.run(
            ["grep", "-c", "activeJobs", extension_js_path],
            capture_output=True,
            text=True,
        )

        count = int(result.stdout.strip()) if result.returncode == 0 else 0
        assert count >= 5, f"Expected multiple activeJobs references, found {count}"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
