from unittest.mock import MagicMock, call, mock_open, patch

import pytest

from mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1 import (
    datasetsizei24,
    get_chip_prog_values,
    get_prog_num,
    load_motion_program_data,
    start_i24,
)

params = {
    "chip_name": "chip",
    "visit": "foo",
    "sub_dir": "bar",
    "n_exposures": 1,
    "chip_type": 0,
    "map_type": 1,
    "pump_repeat": 0,
    "pumpexptime": 0,
    "pumpdelay": 0,
    "exptime": 0.01,
    "dcdetdist": 100,
    "prepumpexptime": 0,
    "det_type": "eiger",
}


chipmap_str = """01status    P3011       1
02status    P3021       0
03status    P3031       0
04status    P3041       0"""


@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.caput")
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.caget")
@patch(
    "mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.scrape_parameter_file"
)
def test_datasetsizei24_for_one_block_and_two_exposures(
    fake_params, fake_caget, fake_caput
):
    fake_params.return_value = tuple(params.values())
    fake_caget.return_value = 2
    with patch(
        "mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.open",
        mock_open(read_data=chipmap_str),
    ):
        tot_num_imgs = datasetsizei24()
    assert tot_num_imgs == 800


def test_get_chip_prog_values():
    chip_dict = get_chip_prog_values(
        0,
        0,
        0,
        0,
        0,
        n_exposures=2,
    )
    assert isinstance(chip_dict, dict)
    assert chip_dict["X_NUM_STEPS"][1] == 20 and chip_dict["X_NUM_BLOCKS"][1] == 8
    assert chip_dict["PUMP_REPEAT"][1] == 0
    assert chip_dict["N_EXPOSURES"][1] == 2


@pytest.mark.parametrize(
    "chip_type, map_type, pump_repeat, expected_prog",
    [
        (0, 0, 0, 11),  # Oxford chip, full chip, no pump
        (0, 1, 0, 12),  # Oxford chip, map generated by mapping lite, no pump
        (2, "", 0, 11),  # Custom chip, map type not needed(full assumed), no pump
        (0, "", 2, 14),  # Oxford chip, assumes mapping lite, pump 2
        (3, "", 0, 11),  # Minichip, no map type, no pump probe
    ],
)
def test_get_prog_number(chip_type, map_type, pump_repeat, expected_prog):
    assert get_prog_num(chip_type, map_type, pump_repeat) == expected_prog


@pytest.mark.parametrize(
    "map_type, pump_repeat, checker, expected_calls",
    [
        (0, 0, False, ["P1100=1"]),  # Full chip, no pump probe, no checker
        (1, 0, False, ["P1200=1"]),  # Mapping lite, no pp, no checker
        (
            1,
            2,
            False,
            ["P1439=0", "P1400=1"],
        ),  # Map irrelevant, pp to Repeat1, no checker
        (
            0,
            3,
            True,
            ["P1439=0", "P1439=1", "P1400=1"],
        ),  # Map irrelevant, pp to Repeat2, checker enabled
    ],
)
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.i24.pmac")
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.caget")
def test_load_motion_program_data_(
    fake_caget, fake_pmac, map_type, pump_repeat, checker, expected_calls
):
    test_dict = {"N_EXPOSURES": [0, 1]}
    fake_caget.return_value = checker
    fake_pmac.pmac_string = MagicMock()
    load_motion_program_data(fake_pmac, test_dict, map_type, pump_repeat)
    call_list = [call.set(i) for i in expected_calls]
    fake_pmac.pmac_string.assert_has_calls(call_list)


@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.datasetsizei24")
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.DCID")
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.caput")
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.caget")
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.sup")
@patch(
    "mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Collect_py3v1.scrape_parameter_file"
)
def test_start_i24_with_eiger(
    fake_params, fake_sup, fake_caget, fake_caput, fake_dcid, fake_size
):
    fake_size.return_value = 800
    fake_params.return_value = tuple(params.values())
    start_i24()
    assert fake_sup.beamline.call_count == 2
    assert fake_sup.eiger.call_count == 1
    # Pilatus gets called for hack to create directory
    assert fake_sup.pilatus.call_count == 2
    assert fake_dcid.call_count == 1
