"""
Tests for async concurrency behavior.

Verifies that the async architecture does NOT block the event loop
when multiple operations are performed concurrently.
"""

from __future__ import annotations

import asyncio
import base64
import time
from pathlib import Path

import pytest


class TestAsyncConcurrency:
    """Test that async operations run concurrently without blocking."""

    @pytest.mark.asyncio
    async def test_concurrent_reads_faster_than_sequential(self, temp_sandbox: Path) -> None:
        """
        Multiple async reads should complete faster than sequential.

        If reads were blocking, 10 reads would take 10x as long.
        With async, they run concurrently.
        """
        from aperion_fsal.core.async_io import read_async

        # Create 10 test files
        for i in range(10):
            (temp_sandbox / f"file_{i}.txt").write_text(f"content {i}" * 1000)

        # Sequential timing baseline
        start = time.perf_counter()
        for i in range(10):
            await read_async(f"file_{i}.txt", root=temp_sandbox)
        seq_time = time.perf_counter() - start

        # Concurrent timing
        start = time.perf_counter()
        tasks = [read_async(f"file_{i}.txt", root=temp_sandbox) for i in range(10)]
        await asyncio.gather(*tasks)
        conc_time = time.perf_counter() - start

        # Concurrent should be significantly faster (at least 2x)
        # Allow for I/O variance - just verify it's not purely sequential
        assert conc_time < seq_time * 0.8 or conc_time < 0.1  # Fast enough

    @pytest.mark.asyncio
    async def test_concurrent_writes_complete_correctly(self, temp_sandbox: Path) -> None:
        """
        Multiple concurrent atomic writes should all complete correctly.

        Each file should contain its expected content with no corruption.
        """
        from aperion_fsal.core.async_io import atomic_write_async, read_async

        # Write 20 files concurrently
        tasks = []
        for i in range(20):
            content = f"file {i} content " * 100
            tasks.append(
                atomic_write_async(
                    f"concurrent_{i}.txt",
                    content.encode(),
                    root=temp_sandbox,
                )
            )
        await asyncio.gather(*tasks)

        # Verify all files exist with correct content
        for i in range(20):
            expected = f"file {i} content " * 100
            actual = await read_async(f"concurrent_{i}.txt", root=temp_sandbox)
            assert actual.decode() == expected

    @pytest.mark.asyncio
    async def test_mixed_operations_no_deadlock(self, temp_sandbox: Path) -> None:
        """
        Mixed read/write/list operations should not deadlock.
        """
        from aperion_fsal.core.async_io import (
            atomic_write_async,
            listdir_async,
            read_async,
        )

        # Create initial files
        for i in range(5):
            (temp_sandbox / f"init_{i}.txt").write_text(f"init {i}")

        # Mix of operations
        async def mixed_ops():
            await listdir_async(".", root=temp_sandbox)
            await atomic_write_async("new.txt", b"new", root=temp_sandbox)
            await read_async("init_0.txt", root=temp_sandbox)
            await listdir_async(".", root=temp_sandbox)

        # Run multiple times concurrently
        tasks = [mixed_ops() for _ in range(10)]

        # Should complete without hanging (timeout would fail the test)
        await asyncio.wait_for(
            asyncio.gather(*tasks),
            timeout=10.0,
        )


class TestSyncWrappers:
    """Test sync wrappers work from non-async context."""

    def test_read_sync_from_sync_context(self, temp_sandbox: Path) -> None:
        """read_sync works when called from sync code."""
        from aperion_fsal.core.async_io import read_sync

        (temp_sandbox / "sync_test.txt").write_bytes(b"sync content")

        result = read_sync("sync_test.txt", root=temp_sandbox)
        assert result == b"sync content"

    def test_listdir_sync_from_sync_context(self, temp_sandbox: Path) -> None:
        """listdir_sync works when called from sync code."""
        from aperion_fsal.core.async_io import listdir_sync

        (temp_sandbox / "a.txt").touch()
        (temp_sandbox / "b.txt").touch()

        entries = listdir_sync(".", root=temp_sandbox)
        names = {e["path"] for e in entries}
        assert "a.txt" in names
        assert "b.txt" in names

    def test_write_sync_from_sync_context(self, temp_sandbox: Path) -> None:
        """write_sync works when called from sync code."""
        from aperion_fsal.core.async_io import write_sync

        content_b64 = base64.b64encode(b"test content").decode()
        write_sync("sync_write.txt", content_b64, root=temp_sandbox)

        assert (temp_sandbox / "sync_write.txt").read_bytes() == b"test content"


class TestExecutorManagement:
    """Test executor lifecycle management."""

    def test_executor_recreates_after_shutdown(self, temp_sandbox: Path) -> None:
        """Executor recreates itself if accessed after shutdown."""
        from aperion_fsal.core import async_io

        # Shutdown
        async_io.shutdown_executor()

        # Should still work (auto-recreates)
        (temp_sandbox / "after_shutdown.txt").write_bytes(b"test")
        result = async_io.read_sync("after_shutdown.txt", root=temp_sandbox)
        assert result == b"test"
