"""Modal end-to-end tests — mirrors modal-e2e-runner.ts."""

from __future__ import annotations

import time

import pytest
import pytest_asyncio

from tests.e2e.conftest import skip_no_modal

from agentsh_secure_sandbox import SecuredSandbox, secure_sandbox
from agentsh_secure_sandbox.adapters import modal, modal_defaults
from agentsh_secure_sandbox.core.types import ExecOpts

pytestmark = [pytest.mark.e2e, pytest.mark.timeout(600), skip_no_modal, pytest.mark.asyncio(loop_scope="module")]

# ─── Module-scoped setup / teardown ────────────────────────


@pytest_asyncio.fixture(scope="module", loop_scope="module")
async def modal_sandbox():  # type: ignore[misc]
    """Create a raw Modal sandbox for adapter tests."""
    import modal  # type: ignore[import-untyped]

    app = modal.App.lookup("agentsh-e2e", create_if_missing=True)
    image = (
        modal.Image.debian_slim(python_version="3.11")
        .apt_install("ca-certificates", "curl", "bash", "git", "sudo", "libseccomp2", "fuse3")
    )
    sb = modal.Sandbox.create(app=app, image=image, timeout=60 * 15)

    yield sb

    try:
        sb.terminate()
    except Exception:
        pass


@pytest_asyncio.fixture(scope="module", loop_scope="module")
async def adapter(modal_sandbox):  # type: ignore[misc]
    """Wrap Modal sandbox in our adapter."""
    return modal(modal_sandbox)


@pytest_asyncio.fixture(scope="module", loop_scope="module")
async def secured(adapter) -> SecuredSandbox:  # type: ignore[misc]
    """Provision a secured sandbox via the Modal adapter."""
    sec = await secure_sandbox(adapter, modal_defaults())

    yield sec  # type: ignore[misc]

    await sec.stop()


# ─── Adapter tests ───────────────────────────────────────────


class TestModalAdapter:
    async def test_exec_simple_command(self, adapter) -> None:
        result = await adapter.exec("echo", ["hello"])
        assert result.exit_code == 0
        assert result.stdout.strip() == "hello"

    async def test_exec_nonzero_exit(self, adapter) -> None:
        result = await adapter.exec("ls", ["/nonexistent-path-xyz"])
        assert result.exit_code != 0

    async def test_write_read_roundtrip(self, adapter) -> None:
        await adapter.write_file("/tmp/modal-e2e-test.txt", "hello from e2e")
        content = await adapter.read_file("/tmp/modal-e2e-test.txt")
        assert content == "hello from e2e"

    async def test_write_read_special_chars(self, adapter) -> None:
        content = 'line1\nline2\ttab\n"quotes" & <brackets>'
        await adapter.write_file("/tmp/modal-e2e-special.txt", content)
        read = await adapter.read_file("/tmp/modal-e2e-special.txt")
        assert read == content

    async def test_exec_with_env_vars(self, adapter) -> None:
        result = await adapter.exec("sh", ["-c", "echo $TEST_VAR"],
                                    ExecOpts(env={"TEST_VAR": "modal-e2e"}))
        assert result.exit_code == 0
        assert result.stdout.strip() == "modal-e2e"

    async def test_readfile_raises_on_missing(self, adapter) -> None:
        with pytest.raises(Exception, match="readFile failed"):
            await adapter.read_file("/tmp/modal-e2e-nonexistent-xyz.txt")

    async def test_detached_exec(self, adapter) -> None:
        start = time.time()
        result = await adapter.exec("sleep", ["10"], ExecOpts(detached=True))
        elapsed = time.time() - start
        assert result.exit_code == 0
        assert elapsed < 5.0, f"detached took too long: {elapsed:.1f}s"


# ─── Smoke tests ──────────────────────────────────────────


class TestModalSmoke:
    async def test_provisions_session_id(self, secured: SecuredSandbox) -> None:
        assert secured.session_id
        assert isinstance(secured.session_id, str)

    async def test_reports_valid_security_mode(self, secured: SecuredSandbox) -> None:
        assert secured.security_mode in ("full", "ptrace", "landlock", "landlock-only", "minimal")

    async def test_exec_simple_command(self, secured: SecuredSandbox) -> None:
        result = await secured.exec("echo hello")
        assert result.exit_code == 0
        assert result.stdout.strip() == "hello"

    async def test_write_read_roundtrip(self, secured: SecuredSandbox) -> None:
        path = "/workspace/e2e-test-file.txt"
        content = f"e2e-roundtrip-{int(time.time() * 1000)}"

        write_result = await secured.write_file(path, content)
        assert write_result.success is True

        read_result = await secured.read_file(path)
        assert read_result.success is True
        if read_result.success:
            assert read_result.content.strip() == content


# ─── Policy enforcement ───────────────────────────────────


class TestModalPolicy:
    async def test_blocks_sudo(self, secured: SecuredSandbox) -> None:
        if secured.security_mode in ("ptrace", "full"):
            result = await secured.exec("sudo whoami")
            assert result.exit_code != 0

    async def test_blocks_env_command(self, secured: SecuredSandbox) -> None:
        if secured.security_mode in ("ptrace", "full"):
            result = await secured.exec("env")
            assert result.exit_code != 0

    async def test_allows_npm_registry(self, secured: SecuredSandbox) -> None:
        result = await secured.exec(
            'curl -s -o /dev/null -w "%{http_code}" https://registry.npmjs.org/'
            ' || echo "no-curl"'
        )
        if "no-curl" not in result.stdout:
            assert result.stdout.strip() == "200"

    async def test_blocks_unauthorized_network(self, secured: SecuredSandbox) -> None:
        result = await secured.exec(
            "curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5"
            " https://evil.example.com 2>&1"
        )
        assert result.stdout.strip() != "200"
