import shutil
from pathlib import Path
from unittest.mock import patch

import numpy as np
import pytest
from dopt_basics import result_pattern

import dopt_sensor_anomalies._find_paths
import dopt_sensor_anomalies.detection as detect
import dopt_sensor_anomalies.types as t
from dopt_sensor_anomalies import constants, errors


@pytest.fixture(scope="module")
def single_img_path() -> Path:
    img_folder = Path(__file__).parent / "_img"
    if not img_folder.exists():
        raise FileNotFoundError("Img path not existing")
    img_paths = tuple(img_folder.glob("*_12.bmp"))
    if not img_paths:
        raise ValueError("No images found")
    return img_paths[0]


def test_midpoint():
    ptA = np.array([1.0, 2.0])
    ptB = np.array([3.0, 4.0])
    mid_x, mid_y = detect.midpoint(ptA, ptB)
    assert mid_x == 2.0
    assert mid_y == 3.0


def test_check_box_redundancy_SameBox():
    box_1: t.Box = (
        (64.70450592041016, 505.09185791015625),
        (32.305301666259766, 162.31748962402344),
        2.385944128036499,
    )
    box_2: t.Box = (
        (64.5450592041016, 504.59185791015625),
        (32.305301666259766, 163.01748962402344),
        2.415944128036499,
    )
    assert detect.check_box_redundancy(box_1, box_2)


def test_check_box_redundancy_DifferentBox():
    box_1: t.Box = (
        (64.70450592041016, 505.09185791015625),
        (32.305301666259766, 162.31748962402344),
        2.385944128036499,
    )
    box_2: t.Box = (
        (84.5450592041016, 104.59185791015625),
        (31.305301666259766, 150.01748962402344),
        2.35944128036499,
    )
    assert not detect.check_box_redundancy(box_1, box_2)


def test_measure_length(single_img_path):
    pixels_per_metric_X: float = 0.251
    pixels_per_metric_Y: float = 0.251
    data, imgs = detect.measure_length(
        single_img_path,
        pixels_per_metric_X,
        pixels_per_metric_Y,
    )
    assert len(data) == 18
    assert isinstance(data[0], str)
    assert float(data[0].replace(",", ".")) == pytest.approx(1266.932)
    img_left = imgs["left"]
    assert 235 < img_left.shape[0] < 260
    assert 910 < img_left.shape[1] < 960
    assert img_left.shape[2] == 3
    img_right = imgs["right"]
    assert 235 < img_right.shape[0] < 260
    assert 910 < img_right.shape[1] < 960
    assert img_right.shape[2] == 3


@patch("dopt_sensor_anomalies._find_paths.STOP_FOLDER_NAME", "lib")
def test_isolated_pipeline(results_folder, path_img_with_failure_TrainedModel):
    pixels_per_metric_X: float = 0.251
    pixels_per_metric_Y: float = 0.251
    MODEL_FOLDER = dopt_sensor_anomalies._find_paths.get_model_folder()
    assert MODEL_FOLDER.exists(), "model folder not existing"
    DETECTION_MODELS = dopt_sensor_anomalies._find_paths.get_detection_models(MODEL_FOLDER)
    assert DETECTION_MODELS["left"].exists()
    assert DETECTION_MODELS["right"].exists()
    data_csv, sensor_images = detect.measure_length(
        path_img_with_failure_TrainedModel,
        pixels_per_metric_X,
        pixels_per_metric_Y,
    )
    print(">>>>>>> Data: ", data_csv)
    # measured sizes
    assert len(data_csv) == 18
    assert sensor_images["left"] is not None
    assert sensor_images["right"] is not None
    detect.anomaly_detection(
        img_path=path_img_with_failure_TrainedModel,
        detection_models=DETECTION_MODELS,
        data_csv=data_csv,
        sensor_images=sensor_images,
    )
    # check files for existence
    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"
    assert csv_file.exists()
    assert heatmap_file.exists()
    shutil.copy(csv_file, (results_folder / csv_file.name))
    shutil.copy(heatmap_file, (results_folder / heatmap_file.name))


@patch("dopt_sensor_anomalies._find_paths.STOP_FOLDER_NAME", "lib")
def test_full_pipeline_wrapped_FailImagePath(setup_temp_dir):
    img_path = str(setup_temp_dir / "not-existing.bmp")
    MESSAGE = "The provided path seems not to exist"

    pixels_per_metric_X: float = 0.251
    pixels_per_metric_Y: float = 0.251

    ret = detect.pipeline(img_path, pixels_per_metric_X, pixels_per_metric_Y)
    assert ret.status != result_pattern.STATUS_HANDLER.SUCCESS
    assert ret.status.ExceptionType is FileNotFoundError
    assert ret.status.message == MESSAGE
    with pytest.raises(FileNotFoundError, match=MESSAGE):
        _ = ret.unwrap()


@patch("dopt_sensor_anomalies._find_paths.STOP_FOLDER_NAME", "lib")
def test_full_pipeline_wrapped_FailElectrodeCount(path_img_with_failure_ElectrodeCount):
    img_path = str(path_img_with_failure_ElectrodeCount)
    MESSAGE = "Number of counted electrodes does not match the"

    pixels_per_metric_X: float = 0.251
    pixels_per_metric_Y: float = 0.251

    ret = detect.pipeline(img_path, pixels_per_metric_X, pixels_per_metric_Y)
    assert ret.status != result_pattern.STATUS_HANDLER.SUCCESS
    assert ret.status.ExceptionType is errors.InvalidElectrodeCount
    assert MESSAGE in ret.status.message
    with pytest.raises(errors.InvalidElectrodeCount, match=MESSAGE):
        _ = ret.unwrap()


@patch("dopt_sensor_anomalies._find_paths.STOP_FOLDER_NAME", "lib")
def test_full_pipeline_wrapped_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 = detect.pipeline(img_path, pixels_per_metric_X, pixels_per_metric_Y)
    assert ret.status == result_pattern.STATUS_HANDLER.SUCCESS
    assert ret.status.code == 0
    assert ret.status.ExceptionType is None

    assert csv_file.exists()
    assert heatmap_file.exists()
    shutil.copy(csv_file, (results_folder / csv_file.name))
    shutil.copy(heatmap_file, (results_folder / heatmap_file.name))
