"""
Integration tests for Redis-dependent features.

These tests verify that the Redis connection works properly with:
- Job Queue (Redis backend)
- Result Cache (Redis Cloud)
- Persistent Output Buffer (Redis + Supabase)

To run these tests:
1. Set up your Redis credentials in .env file:
   REDIS_URL=redis://default:YOUR_PASSWORD@YOUR_HOST.redislabs.com:YOUR_PORT

2. Run: python3 -m pytest tests/test_redis_integration.py -v

These tests are marked with @pytest.mark.integration and will be skipped
if REDIS_URL is not configured.
"""

import os
import time

import pytest

# Check if Redis is configured
REDIS_URL = os.getenv("REDIS_URL")
REDIS_AVAILABLE = REDIS_URL is not None and REDIS_URL != "" and "YOUR_" not in REDIS_URL

# Skip marker for integration tests
requires_redis = pytest.mark.skipif(
    not REDIS_AVAILABLE,
    reason="REDIS_URL not configured. Set it in .env to run Redis integration tests."
)


class TestRedisConnection:
    """Test Redis connection and basic operations."""

    @requires_redis
    def test_redis_ping(self):
        """Test basic Redis connectivity."""
        from urllib.parse import urlparse

        import redis

        parsed = urlparse(REDIS_URL)
        client = redis.Redis(
            host=parsed.hostname,
            port=parsed.port or 6379,
            password=parsed.password,
            decode_responses=True,
        )

        result = client.ping()
        assert result is True

        # Clean up
        client.close()

    @requires_redis
    def test_redis_set_get(self):
        """Test Redis set and get operations."""
        from urllib.parse import urlparse

        import redis

        parsed = urlparse(REDIS_URL)
        client = redis.Redis(
            host=parsed.hostname,
            port=parsed.port or 6379,
            password=parsed.password,
            decode_responses=True,
        )

        # Set a test key
        test_key = "gateway:test:integration"
        test_value = f"test_value_{time.time()}"

        client.setex(test_key, 60, test_value)  # 60 second TTL

        # Get it back
        result = client.get(test_key)
        assert result == test_value

        # Clean up
        client.delete(test_key)
        client.close()


class TestRedisJobQueue:
    """Test RedisJobQueue with real Redis."""

    @requires_redis
    @pytest.mark.asyncio
    async def test_redis_queue_send_receive(self):
        """Test sending and receiving messages through Redis queue."""
        from verlex_server.gateway.core.job_queue import JobPriority, RedisJobQueue

        queue = RedisJobQueue(
            name="test_integration",
            redis_url=REDIS_URL,
        )

        # Clean up any previous test data
        await queue.purge()

        # Send a message
        message = await queue.send(
            job_id="test-job-001",
            user_id="test-user-001",
            payload={"function": "test", "args": [1, 2, 3]},
            priority=JobPriority.NORMAL,
        )

        assert message.job_id == "test-job-001"
        assert message.message_id is not None

        # Receive the message
        received = await queue.receive(max_messages=1, visibility_timeout=30)

        assert len(received) == 1
        assert received[0].job_id == "test-job-001"
        assert received[0].payload["function"] == "test"

        # Delete the message
        deleted = await queue.delete(received[0].message_id)
        assert deleted is True

        # Verify queue is empty
        stats = await queue.get_stats()
        assert stats.visible_messages == 0

    @requires_redis
    @pytest.mark.asyncio
    async def test_redis_queue_priority(self):
        """Test priority ordering in Redis queue."""
        from verlex_server.gateway.core.job_queue import JobPriority, RedisJobQueue

        queue = RedisJobQueue(
            name="test_priority",
            redis_url=REDIS_URL,
        )

        await queue.purge()

        # Send messages with different priorities
        await queue.send(
            job_id="low-priority",
            user_id="test-user",
            payload={"priority": "low"},
            priority=JobPriority.LOW,
        )

        await queue.send(
            job_id="high-priority",
            user_id="test-user",
            payload={"priority": "high"},
            priority=JobPriority.HIGH,
        )

        await queue.send(
            job_id="normal-priority",
            user_id="test-user",
            payload={"priority": "normal"},
            priority=JobPriority.NORMAL,
        )

        # Receive should get highest priority first
        received = await queue.receive(max_messages=3)

        assert len(received) == 3
        assert received[0].job_id == "high-priority"
        assert received[1].job_id == "normal-priority"
        assert received[2].job_id == "low-priority"

        # Clean up
        for msg in received:
            await queue.delete(msg.message_id)


class TestRedisResultCache:
    """Test ResultCacheService with real Redis."""

    @requires_redis
    @pytest.mark.asyncio
    async def test_result_cache_store_retrieve(self):
        """Test storing and retrieving results from Redis cache."""
        from verlex_server.gateway.backend.cache import RedisClient, RedisConfig
        from verlex_server.gateway.backend.result_cache import CacheStatus, ResultCacheService

        # Create Redis client with real connection
        config = RedisConfig(url=REDIS_URL)
        redis_client = RedisClient(config)

        cache = ResultCacheService(
            redis_client=redis_client,
            default_ttl_seconds=60,
        )

        job_id = f"test-job-{time.time()}"
        result_data = b"Hello, Redis Cache!"

        # Store
        status = await cache.store(
            job_id=job_id,
            user_id="test-user",
            result_data=result_data,
            result_type="value",
        )

        assert status == CacheStatus.STORED

        # Retrieve
        data, status = await cache.retrieve(job_id)

        assert status == CacheStatus.HIT
        assert data == result_data

        # Check stats
        stats = cache.get_stats()
        assert stats.hits >= 1
        assert stats.stores >= 1

        # Invalidate
        result = await cache.invalidate(job_id)
        assert result is True

        # Verify gone
        data, status = await cache.retrieve(job_id)
        assert status == CacheStatus.MISS

    @requires_redis
    @pytest.mark.asyncio
    async def test_result_cache_dedup(self):
        """Test result deduplication in Redis cache."""
        from verlex_server.gateway.backend.cache import RedisClient, RedisConfig
        from verlex_server.gateway.backend.result_cache import CacheStatus, ResultCacheService

        config = RedisConfig(url=REDIS_URL)
        redis_client = RedisClient(config)

        cache = ResultCacheService(redis_client=redis_client)

        computation_hash = f"hash-{time.time()}"
        job_id = f"job-{time.time()}"
        result_data = b"Computed result"

        # Store with computation hash
        await cache.store(
            job_id=job_id,
            user_id="test-user",
            result_data=result_data,
            computation_hash=computation_hash,
        )

        # Retrieve by computation hash
        data, retrieved_job_id, status = await cache.retrieve_by_computation(
            computation_hash
        )

        assert status == CacheStatus.HIT
        assert data == result_data
        assert retrieved_job_id == job_id

        # Clean up
        await cache.invalidate(job_id)


class TestRedisOutputBuffer:
    """Test PersistentOutputBuffer with real Redis."""

    @requires_redis
    @pytest.mark.asyncio
    async def test_output_buffer_operations(self):
        """Test output buffer operations with Redis."""
        from verlex_server.gateway.backend.cache import RedisClient, RedisConfig
        from verlex_server.gateway.backend.output_buffer import (
            BufferStatus,
            PersistentOutputBuffer,
            StreamType,
        )

        config = RedisConfig(url=REDIS_URL)
        redis_client = RedisClient(config)

        # Create buffer without Supabase for this test
        buffer = PersistentOutputBuffer(
            redis_client=redis_client,
            persist_to_supabase=False,  # Skip Supabase
        )

        job_id = f"job-{time.time()}"
        user_id = "test-user"

        # Start job
        status = await buffer.start_job(job_id, user_id)
        assert status == BufferStatus.SUCCESS

        # Append outputs
        for i in range(5):
            index, status = await buffer.append(
                job_id=job_id,
                user_id=user_id,
                content=f"Output line {i}",
                stream=StreamType.STDOUT,
            )
            assert status == BufferStatus.SUCCESS
            assert index == i

        # Get entries
        entries = await buffer.get_entries(job_id)
        assert len(entries) == 5
        assert entries[0].content == "Output line 0"
        assert entries[4].content == "Output line 4"

        # Complete job
        status = await buffer.complete_job(job_id, has_error=False)
        assert status == BufferStatus.SUCCESS


class TestEnvironmentConfiguration:
    """Test that environment configuration works correctly."""

    @requires_redis
    def test_redis_config_from_env(self):
        """Test RedisConfig.from_env() picks up REDIS_URL."""
        from verlex_server.gateway.backend.cache import RedisConfig

        config = RedisConfig.from_env()

        assert config.url == REDIS_URL

        # Get connection kwargs
        kwargs = config.get_connection_kwargs()
        assert "host" in kwargs
        assert "port" in kwargs

    @requires_redis
    def test_job_queue_from_env(self):
        """Test RedisJobQueue picks up REDIS_URL from environment."""
        from verlex_server.gateway.core.job_queue import RedisJobQueue

        # Create queue without explicit URL - should use env
        queue = RedisJobQueue(name="test_env")

        # Should have parsed from REDIS_URL
        assert queue.host is not None
        assert queue.port is not None


def print_redis_status():
    """Print Redis configuration status."""
    print("\n" + "=" * 60)
    print("Redis Integration Test Status")
    print("=" * 60)

    if REDIS_AVAILABLE:
        print("✅ REDIS_URL is configured")
        # Don't print full URL for security
        from urllib.parse import urlparse
        parsed = urlparse(REDIS_URL)
        print(f"   Host: {parsed.hostname}")
        print(f"   Port: {parsed.port or 6379}")
    else:
        print("❌ REDIS_URL not configured or contains placeholder values")
        print("   To run Redis integration tests:")
        print("   1. Sign up at https://redis.com/try-free/")
        print("   2. Create a database")
        print("   3. Copy .env.example to .env")
        print("   4. Set REDIS_URL to your connection string")

    print("=" * 60 + "\n")


if __name__ == "__main__":
    print_redis_status()

    if REDIS_AVAILABLE:
        # Run a quick connection test
        from urllib.parse import urlparse

        import redis

        try:
            parsed = urlparse(REDIS_URL)
            client = redis.Redis(
                host=parsed.hostname,
                port=parsed.port or 6379,
                password=parsed.password,
            )
            client.ping()
            print("✅ Redis connection successful!")
            client.close()
        except Exception as e:
            print(f"❌ Redis connection failed: {e}")
    else:
        print("Skipping connection test - Redis not configured")
