"""
Integration tests for GPU/CPU frame compatibility.

These tests verify that all primitives work correctly with both:
- numpy arrays (CPU frames)
- CuPy arrays (GPU frames)
- GPUFrame wrapper objects

Run with: pytest tests/test_frame_compatibility.py -v
"""

import pytest
import numpy as np
import sys
import os

# Add parent to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Try to import CuPy
try:
    import cupy as cp
    HAS_GPU = True
except ImportError:
    cp = None
    HAS_GPU = False


def create_test_frame(on_gpu=False):
    """Create a test frame (100x100 RGB)."""
    frame = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
    if on_gpu and HAS_GPU:
        return cp.asarray(frame)
    return frame


class MockGPUFrame:
    """Mock GPUFrame for testing without full GPU stack."""
    def __init__(self, data):
        self._data = data

    @property
    def cpu(self):
        if HAS_GPU and hasattr(self._data, 'get'):
            return self._data.get()
        return self._data

    @property
    def gpu(self):
        if HAS_GPU:
            if hasattr(self._data, 'get'):
                return self._data
            return cp.asarray(self._data)
        raise RuntimeError("No GPU available")


class TestColorOps:
    """Test color_ops primitives with different frame types."""

    def test_shift_hsv_numpy(self):
        """shift-hsv should work with numpy arrays."""
        from sexp_effects.primitive_libs.color_ops import prim_shift_hsv
        frame = create_test_frame(on_gpu=False)
        result = prim_shift_hsv(frame, h=30, s=1.2, v=0.9)
        assert isinstance(result, np.ndarray)
        assert result.shape == frame.shape

    @pytest.mark.skipif(not HAS_GPU, reason="No GPU")
    def test_shift_hsv_cupy(self):
        """shift-hsv should work with CuPy arrays."""
        from sexp_effects.primitive_libs.color_ops import prim_shift_hsv
        frame = create_test_frame(on_gpu=True)
        result = prim_shift_hsv(frame, h=30, s=1.2, v=0.9)
        assert isinstance(result, np.ndarray)  # Should return numpy

    def test_shift_hsv_gpuframe(self):
        """shift-hsv should work with GPUFrame objects."""
        from sexp_effects.primitive_libs.color_ops import prim_shift_hsv
        frame = MockGPUFrame(create_test_frame(on_gpu=False))
        result = prim_shift_hsv(frame, h=30, s=1.2, v=0.9)
        assert isinstance(result, np.ndarray)

    def test_invert_numpy(self):
        """invert-img should work with numpy arrays."""
        from sexp_effects.primitive_libs.color_ops import prim_invert_img
        frame = create_test_frame(on_gpu=False)
        result = prim_invert_img(frame)
        assert isinstance(result, np.ndarray)

    def test_adjust_numpy(self):
        """adjust should work with numpy arrays."""
        from sexp_effects.primitive_libs.color_ops import prim_adjust
        frame = create_test_frame(on_gpu=False)
        result = prim_adjust(frame, brightness=10, contrast=1.2)
        assert isinstance(result, np.ndarray)


class TestGeometry:
    """Test geometry primitives with different frame types."""

    def test_rotate_numpy(self):
        """rotate should work with numpy arrays."""
        from sexp_effects.primitive_libs.geometry import prim_rotate
        frame = create_test_frame(on_gpu=False)
        result = prim_rotate(frame, 45)
        assert isinstance(result, np.ndarray)

    def test_scale_numpy(self):
        """scale should work with numpy arrays."""
        from sexp_effects.primitive_libs.geometry import prim_scale
        frame = create_test_frame(on_gpu=False)
        result = prim_scale(frame, 1.5)
        assert isinstance(result, np.ndarray)


class TestBlending:
    """Test blending primitives with different frame types."""

    def test_blend_numpy(self):
        """blend should work with numpy arrays."""
        from sexp_effects.primitive_libs.blending import prim_blend
        frame_a = create_test_frame(on_gpu=False)
        frame_b = create_test_frame(on_gpu=False)
        result = prim_blend(frame_a, frame_b, 0.5)
        assert isinstance(result, np.ndarray)


class TestInterpreterConversion:
    """Test the interpreter's frame conversion."""

    def test_maybe_to_numpy_none(self):
        """_maybe_to_numpy should handle None."""
        from streaming.stream_sexp_generic import StreamInterpreter
        # Create minimal interpreter
        import tempfile
        with tempfile.NamedTemporaryFile(mode='w', suffix='.sexp', delete=False) as f:
            f.write('(stream "test" :fps 30 :width 100 :height 100 (frame frame))')
            f.flush()
            interp = StreamInterpreter(f.name)

        assert interp._maybe_to_numpy(None) is None

    def test_maybe_to_numpy_numpy(self):
        """_maybe_to_numpy should pass through numpy arrays."""
        from streaming.stream_sexp_generic import StreamInterpreter
        import tempfile
        with tempfile.NamedTemporaryFile(mode='w', suffix='.sexp', delete=False) as f:
            f.write('(stream "test" :fps 30 :width 100 :height 100 (frame frame))')
            f.flush()
            interp = StreamInterpreter(f.name)

        frame = create_test_frame(on_gpu=False)
        result = interp._maybe_to_numpy(frame)
        assert result is frame  # Should be same object

    @pytest.mark.skipif(not HAS_GPU, reason="No GPU")
    def test_maybe_to_numpy_cupy(self):
        """_maybe_to_numpy should convert CuPy to numpy."""
        from streaming.stream_sexp_generic import StreamInterpreter
        import tempfile
        with tempfile.NamedTemporaryFile(mode='w', suffix='.sexp', delete=False) as f:
            f.write('(stream "test" :fps 30 :width 100 :height 100 (frame frame))')
            f.flush()
            interp = StreamInterpreter(f.name)

        frame = create_test_frame(on_gpu=True)
        result = interp._maybe_to_numpy(frame)
        assert isinstance(result, np.ndarray)

    def test_maybe_to_numpy_gpuframe(self):
        """_maybe_to_numpy should convert GPUFrame to numpy."""
        from streaming.stream_sexp_generic import StreamInterpreter
        import tempfile
        with tempfile.NamedTemporaryFile(mode='w', suffix='.sexp', delete=False) as f:
            f.write('(stream "test" :fps 30 :width 100 :height 100 (frame frame))')
            f.flush()
            interp = StreamInterpreter(f.name)

        frame = MockGPUFrame(create_test_frame(on_gpu=False))
        result = interp._maybe_to_numpy(frame)
        assert isinstance(result, np.ndarray)


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