import numpy as np
from PIL import Image

from src.utils import core


def test_should_capture_configured_screen_as_grayscale(mocker):
    """Letzte Rueckfallebene: PIL, wenn beide schnellen Wege ausfallen."""
    image = Image.fromarray(np.array([[[255, 0, 0], [0, 255, 0]]], dtype=np.uint8))
    mocker.patch('src.utils.core._captureDirect', side_effect=OSError)
    mocker.patch('src.utils.core._captureObs', side_effect=OSError)
    grab = mocker.patch('src.utils.core.ImageGrab.grab', return_value=image)
    mocker.patch('src.utils.core._captureRegion', return_value=(10, 20, 30, 40))

    screenshot = core.getScreenshot()

    grab.assert_called_once_with(bbox=(10, 20, 30, 40), all_screens=True)
    assert screenshot.shape == (1, 2)


def test_should_prefer_direct_capture(mocker):
    expected = np.array([[10, 20]], dtype=np.uint8)
    mocker.patch('src.utils.core._captureDirect', return_value=expected)
    grab = mocker.patch('src.utils.core.ImageGrab.grab')

    screenshot = core.getScreenshot()

    grab.assert_not_called()
    assert np.array_equal(screenshot, expected)


def test_should_create_configured_direct_capture(mocker):
    frame = np.array([[[0, 0, 255, 255], [0, 255, 0, 255]]], dtype=np.uint8)
    camera = mocker.Mock()
    camera.grab.return_value = frame
    create = mocker.patch.object(core, '_createDirectCamera', return_value=camera)
    mocker.patch.object(core, 'camera', None)

    screenshot = core._captureDirect()

    create.assert_called_once_with()
    assert screenshot.shape == (1, 2)


def test_should_use_configured_output(mocker):
    dxcam = mocker.Mock()
    camera = mocker.Mock()
    dxcam.create.return_value = camera
    mocker.patch.dict('sys.modules', {'dxcam': dxcam})
    mocker.patch.dict('os.environ', {'TIBIAPILOT_OUTPUT_IDX': '2'})

    assert core._createDirectCamera() is camera
    dxcam.create.assert_called_once_with(
        device_idx=0, output_idx=2, output_color='BGRA',
    )


def test_should_reject_empty_direct_frame(mocker):
    camera = mocker.Mock()
    camera.grab.return_value = None
    mocker.patch.object(core, 'camera', camera)

    with np.testing.assert_raises(RuntimeError):
        core._captureDirect()
