import shutil
from io import StringIO
from unittest.mock import patch

import pytest
from dopt_basics.result_pattern import wrap_result

from dopt_sensor_anomalies import _interface, constants


def test_print_error_state_WrongState():
    from dopt_basics.result_pattern import STATUS_HANDLER

    MSG = "to treat state as error"
    with pytest.raises(RuntimeError, match=MSG):
        _interface._print_error_state(STATUS_HANDLER.SUCCESS, out_stream=StringIO())


def test_print_error_state(tmp_path):
    @wrap_result(100)
    def error_func() -> None:
        # do something
        raise RuntimeError("Oops, error occurred")

    err_state = error_func().status
    output_file = tmp_path / "t_output.txt"
    output_file.touch()

    with open(output_file, "w") as stream:
        _interface._print_error_state(err_state, stream)

    lines: list[str]
    with open(output_file, "r") as file:
        lines = file.readlines()
        try:
            lines.remove("\n")
        except ValueError:  # pragma: no cover
            pass

    assert "following exception" in lines[0]
    assert "Type: RuntimeError" in lines[1]
    assert "Description:" in lines[2]
    assert "Message: Oops, error occurred" in lines[3]


@patch("dopt_sensor_anomalies._find_paths.STOP_FOLDER_NAME", "lib")
def test_sensor_anomalies_detection_FailImagePath(setup_temp_dir):
    img_path = str(setup_temp_dir / "not-existing.bmp")

    pixels_per_metric_X: float = 0.251
    pixels_per_metric_Y: float = 0.251

    MESSAGE = "The provided path seems not to exist"

    with patch("sys.stderr", new_callable=StringIO) as mock_err:
        ret = _interface.sensor_anomalies_detection(
            img_path, pixels_per_metric_X, pixels_per_metric_Y
        )
        captured = mock_err.getvalue()
        assert ret != 0
        assert "FileNotFoundError" in captured
        assert MESSAGE in captured


@patch("dopt_sensor_anomalies._find_paths.STOP_FOLDER_NAME", "lib")
def test_sensor_anomalies_detection_FailElectrodeCount(path_img_with_failure_ElectrodeCount):
    img_path = str(path_img_with_failure_ElectrodeCount)
    pixels_per_metric_X: float = 0.251
    pixels_per_metric_Y: float = 0.251

    MESSAGE = "Number of counted electrodes does not match the"

    with patch("sys.stderr", new_callable=StringIO) as mock_err:
        ret = _interface.sensor_anomalies_detection(
            img_path, pixels_per_metric_X, pixels_per_metric_Y
        )
        captured = mock_err.getvalue()
        assert ret != 0
        assert "InvalidElectrodeCount" in captured
        assert MESSAGE in captured


@patch("dopt_sensor_anomalies._find_paths.STOP_FOLDER_NAME", "lib")
def test_sensor_anomalies_detection_Success(
    results_folder, path_img_with_failure_TrainedModel
):
    # paths: check files for existence and delete because of other tests
    root_img = path_img_with_failure_TrainedModel.parent
    file_stem = path_img_with_failure_TrainedModel.stem
    csv_file = root_img / f"{file_stem}.csv"
    heatmap_file = root_img / f"{file_stem}{constants.HEATMAP_FILENAME_SUFFIX}.png"
    if csv_file.exists():
        csv_file.unlink()
    if heatmap_file.exists():
        heatmap_file.unlink()

    img_path = str(path_img_with_failure_TrainedModel)
    pixels_per_metric_X: float = 0.251
    pixels_per_metric_Y: float = 0.251

    ret = _interface.sensor_anomalies_detection(
        img_path, pixels_per_metric_X, pixels_per_metric_Y
    )

    assert ret == 0
    assert csv_file.exists()
    assert heatmap_file.exists()
    target_folder = results_folder / "csharp_interface"
    target_folder.mkdir(exist_ok=True)
    shutil.copy(csv_file, (target_folder / csv_file.name))
    shutil.copy(heatmap_file, (target_folder / heatmap_file.name))
