import glob
import os.path as path
import shutil
from distutils.dir_util import copy_tree

from .subprocess_utils import simple_unix_wrapper
from .errors import ValidationError


def copy_to_dir(source_file: str, destination_path: str, verbose: bool=False):
    """ Copies a single file into the destination directory. If a directory is
    given as a source then all the contents of the directory will be copied (the
    directory itself will not be).
"""

    if source_file is None or destination_path is None:
        raise ValidationError("Error: source for copy cannot be None type")
    if destination_path is None:
        raise ValidationError("Error: copy destination cannot be None type")
    if not path.exists(source_file):
        raise ValidationError(f"Error: Copy Failure: source does not exist: {source_file}")
    if not path.exists(destination_path) or not path.isdir(destination_path):
        print("Error: Copy Failure: destination path does not exist: ", destination_path)
        raise ValidationError(f"Error: Copy Failure: destination path does not exist: {destination_path}")

    try:
        if verbose:
            print(f"Copying: {source_file} to {destination_path}")
        if path.isdir(source_file):
            copy_tree(source_file, destination_path)
        else:
            shutil.copy2(source_file, destination_path)
    except IOError as e:
        msg = f"Error: copy failure.\n  copying: {source_file}\n  to: {destination_path}"
        raise IOError(msg) from e
    return 0


def _list_checks(sources: [str], targets: [str]):
    if sources is None or targets is None:
        raise ValidationError("Error: source for copy cannot be None type")
    if targets is None:
        raise ValidationError("Error: copy destination cannot be None type")
    if type(sources) is not list or len(sources) == 0:
        raise ValidationError("Error: sources must be given as a list with at least one entry")
    if type(targets) is not list or len(targets) == 0:
        raise ValidationError("Error: targets must be given as a list with at least one entry")


def copy_multiple_items_as_one_to_one(source_paths: [str], destination_paths: [str], verbose: bool=False):
    _list_checks()

    for i in range(0, len(source_paths)):

        if not path.exists(destination_paths[i]):
            if verbose:
                print("Copy Warning: target path does not exist (skipping): ", destination_paths[i])
            continue
        if not path.isdir(destination_paths[i]):
            if verbose:
                print("Copy Warning: target path is not a directory (skipping): ", destination_paths[i])
            continue

        result = copy_to_dir(source_paths[i], destination_paths[i], verbose)
        if result != 0:
            return result


def copy_multiple_items_to_multiple_dirs(source_paths: [str], destination_paths: [str], verbose: bool=False):
    """ Copies the sources to the destination paths.

This is a generic function that just wraps shutil.copy2"""
    _list_checks(source_paths, destination_paths)

    for dpath in destination_paths:
        if not path.exists(dpath):
            if verbose:
                print("Copy Warning: target path does not exist (skipping): ", dpath)
            continue
        if not path.isdir(dpath):
            if verbose:
                print("Copy Warning: target path is not a directory (skipping): ", )
            continue

        for spath in source_paths:
            result = copy_to_dir(spath, dpath, verbose)
            if result != 0:
                return result

    return 0


def move_dirs(source_dirs: [str], destination_dirs: [str], verbose: bool=False):
    """ Moves all files from inside source paths to the destination dits.
It's important to note that it moves index to index
i.e. source[0] -> dest[0]; source[1] -> dest[1]

This is actually a pretty generic function, should be moved into a utils class.
"""

    _list_checks(source_dirs, destination_dirs)

    if len(source_dirs) != len(destination_dirs):
        raise ValidationError("Error: source and target paths are not equal; aborting file movement")

    for i in range(0, len(source_dirs)):
        # Identify files in path, move each one:
        wildcard_search = "{0}/*".format(source_dirs[i])
        filepaths = glob.glob(wildcard_search)
        if len(filepaths) == 0:
            print("Warning: directory is empty: ", source_dirs[i])
            continue
        try:
            for fp in filepaths:
                if verbose:
                    print("Moving: {0} -> {1}".format(fp, destination_dirs[i]))
                command_and_args = ["mv", fp, destination_dirs[i]]
                # shutil.move(fp, destination_dirs[i])
                simple_unix_wrapper(command_and_args)
        except IOError as e:
            print("IOError copying: ", e.filename, e.filename2)
            return 2

    return 0
