"""Sprites end-to-end tests — mirrors sprites-e2e-runner.ts.

Tests the sprites adapter against a real Fly Sprites instance.
Uses a pre-existing sprite (SPRITES_NAME env var) — does NOT create or
destroy sprites.
"""

from __future__ import annotations

import pytest

from tests.e2e.conftest import ENV, skip_no_sprites

from agentsh_secure_sandbox.adapters import sprites, sprites_defaults

pytestmark = [pytest.mark.e2e, pytest.mark.timeout(60), skip_no_sprites]

# ─── Module-scoped adapter ────────────────────────────────


@pytest.fixture(scope="module")
def adapter():  # type: ignore[no-untyped-def]
    from sprites import SpritesClient  # type: ignore[import-untyped]

    token = ENV["SPRITES_TOKEN"]
    if not token and ENV["FLY_API_TOKEN"] and ENV["SPRITES_ORG"]:
        token = SpritesClient.create_token(ENV["FLY_API_TOKEN"], ENV["SPRITES_ORG"])

    client = SpritesClient(token)
    sprite = client.sprite(ENV["SPRITES_NAME"])
    return sprites(sprite)


# ─── Adapter: exec ────────────────────────────────────────


class TestSpritesExec:
    async def test_simple_command(self, adapter) -> None:  # type: ignore[no-untyped-def]
        result = await adapter.exec("echo", ["hello"])
        assert result.exit_code == 0
        assert result.stdout.strip() == "hello"

    async def test_nonzero_exit_on_failure(self, adapter) -> None:  # type: ignore[no-untyped-def]
        result = await adapter.exec("ls", ["/nonexistent-path-xyz"])
        assert result.exit_code != 0
        assert result.stdout or result.stderr

    async def test_sudo_prefix(self, adapter) -> None:  # type: ignore[no-untyped-def]
        from agentsh_secure_sandbox.core.types import ExecOpts

        result = await adapter.exec("whoami", [], ExecOpts(sudo=True))
        assert isinstance(result.exit_code, int)


# ─── Adapter: writeFile + readFile ────────────────────────


class TestSpritesFileIO:
    async def test_write_creates_file(self, adapter) -> None:  # type: ignore[no-untyped-def]
        await adapter.write_file("/tmp/sprites-e2e-test.txt", "hello from e2e")
        result = await adapter.exec("cat", ["/tmp/sprites-e2e-test.txt"])
        assert result.stdout == "hello from e2e"

    async def test_read_file(self, adapter) -> None:  # type: ignore[no-untyped-def]
        await adapter.write_file("/tmp/sprites-e2e-read.txt", "read-test-content")
        content = await adapter.read_file("/tmp/sprites-e2e-read.txt")
        assert content == "read-test-content"

    async def test_roundtrip_special_chars(self, adapter) -> None:  # type: ignore[no-untyped-def]
        content = 'line1\nline2\ttab\n"quotes" & <brackets>'
        await adapter.write_file("/tmp/sprites-e2e-special.txt", content)
        read = await adapter.read_file("/tmp/sprites-e2e-special.txt")
        assert read == content

    async def test_read_missing_file_raises(self, adapter) -> None:  # type: ignore[no-untyped-def]
        with pytest.raises(RuntimeError, match="readFile failed"):
            await adapter.read_file("/tmp/sprites-e2e-nonexistent-xyz.txt")

    async def test_file_exists(self, adapter) -> None:  # type: ignore[no-untyped-def]
        await adapter.write_file("/tmp/sprites-e2e-exists.txt", "x")
        assert await adapter.file_exists("/tmp/sprites-e2e-exists.txt") is True
        assert await adapter.file_exists("/tmp/sprites-e2e-nope.txt") is False


# ─── Adapter: detached exec ──────────────────────────────


class TestSpritesDetached:
    async def test_returns_immediately(self, adapter) -> None:  # type: ignore[no-untyped-def]
        import time

        from agentsh_secure_sandbox.core.types import ExecOpts

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


# ─── sprites_defaults ────────────────────────────────────


class TestSpritesDefaults:
    def test_values(self) -> None:
        cfg = sprites_defaults()
        assert cfg.install_strategy == "preinstalled"
        assert cfg.skip_integrity_check is True
        assert cfg.real_paths is True
        assert cfg.workspace == "/home/sprite"


# ─── Cleanup ─────────────────────────────────────────────


@pytest.fixture(scope="module", autouse=True)
async def cleanup(adapter) -> None:  # type: ignore[no-untyped-def,misc]
    yield  # type: ignore[misc]
    await adapter.exec(
        "rm",
        [
            "-f",
            "/tmp/sprites-e2e-test.txt",
            "/tmp/sprites-e2e-read.txt",
            "/tmp/sprites-e2e-special.txt",
            "/tmp/sprites-e2e-exists.txt",
        ],
    )
