from os import path

from nabu.pipeline.config_validators import (
    convert_to_int,
    is_writeable,
    name_range_checker,
    validator,
)

from ..utils import ai_units


# TODO move to nabu
@validator
def optional_integer_validator(val):
    if isinstance(val, int):
        return val
    elif len(val.strip()) >= 1:
        val_int, error = convert_to_int(val)
        if error is not None:
            raise ValueError("Invalid number")
    else:
        val_int = None
    return val_int


def convert_to_tuple_of_floats(val):
    if len(val.strip()) == 0:
        return None
    try:
        res = tuple(float(x) for x in val.strip("()[]").split(","))
    except Exception:
        res = None
    return res


# @validator
# def optional_tuple_of_floats_validator(val):
#     res = convert_to_tuple_of_floats(val)
#     if res is None:
#         raise ValueError("Expected a tuple of numbers, but got %s" % val)
#     return res


@validator
def optional_tuple_of_two_floats_validator(val):
    val = val.strip()
    if val == "":
        return None
    res = convert_to_tuple_of_floats(val)
    if res is None or len(res) != 2:
        raise ValueError("Expected a tuple of numbers, but got %s" % val)
    return res


def check_writeable_output(val):
    if path.isdir(val):
        dirname = val
    else:
        dirname = path.dirname(val)
    if not is_writeable(dirname):
        raise ValueError("Directory %s must be writeable" % dirname)


@validator
def output_file_or_directory_validator(val):
    val = val.strip()
    return val


@validator
def optional_output_file_or_directory_validator(val):
    val = val.strip()
    if val.strip() != "":
        check_writeable_output(val)
        return val
    return None


@validator
def optional_directory_validator(location):
    if len(location.strip()) > 0:
        if not path.isdir(location):
            raise ValueError("Directory must exist")
        return location
    return None


# ------------------------


@validator
def xrdct_layout_validator(val):
    val = val.strip()
    valid_layouts = {
        "id15": "id15a",
        "id15a": "id15a",
        "id11": "id11",
        "id31": "id31",
    }
    return name_range_checker(
        val.lower(),
        valid_layouts,
        "folders layout",
    )


@validator
def xrdct_integration_unit_validator(val):
    return name_range_checker(
        val,
        ai_units,
        "radial unit",
    )


@validator
def xrdct_error_model_validator(val):
    error_models = {
        "poisson": "poisson",
        "hybrid": "hybrid",
        "none": None,
        "": None,
    }
    return name_range_checker(
        val.lower(),
        error_models,
        "error model",
    )


@validator
def xrdct_azim_range_validator(val):
    res = convert_to_tuple_of_floats(val)
    if res is None:
        raise ValueError("Could not convert to tuple of floats: %s" % val)
    return res


@validator
def xrdct_ai_method_validator(val):
    ai_methods = {
        "opencl": "opencl",
    }
    return name_range_checker(
        val.lower(),
        ai_methods,
        "AI method",
    )


@validator
def xrdct_existing_output_validator(val):
    behaviors = ["skip", "overwrite", "raise", "reprocess_if_conffile_more_recent"]
    return name_range_checker(val.lower(), behaviors, "existing output behavior")


@validator
def xrdct_trim_method_validator(val):
    trim_methods = {
        "": None,
        None: None,
        "default": None,
        "median": "median",
        "medfilt": "median",
        "sigmaclip": "sigma_clip",
        "sigma_clip": "sigma_clip",
        "sigma-clip": "sigma_clip",
    }
    return name_range_checker(
        val,
        trim_methods,
        "trim method",
    )


@validator
def xrdct_trim_bounds_validator(val):
    if val == "":
        return None
    res = convert_to_tuple_of_floats(val)
    if res is None:
        raise ValueError("Could not convert to float or tuple of floats: %s" % val)
    return res


@validator
def pixel_splitting_validator(val):
    split_methods = {
        "": "no",
        "no": "no",
        "0": "no",
        "BBox": "BBox",
        "pseudo": "pseudo",
        "full": "full",
    }
    return name_range_checker(
        val,
        split_methods,
        "pixel splitting method",
    )


@validator
def average_xy_validator(val):
    reduction_methods = {
        "": None,
        "no": None,
        "0": None,
        "false": None,
        "1": "mean",
        "mean": "mean",
        "median": "median",
    }
    return name_range_checker(
        val.lower(),
        reduction_methods,
        "average_xy method",
    )
