from pathlib import Path

from ploomber.util.dotted_path import load_dotted_path


class EntryPoint:
    """
    Handles common operations on the 4 types of entry points. Exposes a
    pathlib.Path-like interface
    """
    Directory = 'directory'
    Pattern = 'pattern'
    File = 'file'
    DottedPath = 'dotted-path'
    ModulePath = 'module-path'

    def __init__(self, value):
        self.value = value
        self.type = find_entry_point_type(value)

    def exists(self):
        if self.type == self.Pattern:
            return True
        elif self.type in {self.Directory, self.File}:
            return Path(self.value).exists()
        elif self.type == self.DottedPath:
            return load_dotted_path(self.value, raise_=False) is not None

    def is_dir(self):
        return self.type == self.Directory

    @property
    def suffix(self):
        return None if self.type not in {self.File, self.ModulePath} else Path(
            self.value).suffix

    def __repr__(self):
        return repr(self.value)

    def __str__(self):
        return str(self.value)


# TODO: this only used to override default behavior
# when using the jupyter extension, but they have to be integrated with the CLI
# to provide consistent behavior. The problem is that logic implemented
# in _process_file_dir_or_glob and _process_factory_dotted_path
# also contains some CLI specific parts that we don't require here
def find_entry_point_type(entry_point):
    """
    Step 1: If not ENTRY_POINT is defined nor a value is passed, a default
    value is used (pipeline.yaml for CLI, recursive lookup for Jupyter client).
    If ENTRY_POINT is defined, this simply overrides the default value, but
    passing a value overrides the default value. Once the value is determined.

    Step 2: If value is a valid directory, DAG is loaded from such directory,
    if it's a file, it's loaded from that file (spec), finally, it's
    interpreted as a dotted path
    """
    type_ = try_to_find_entry_point_type(entry_point)

    if type_:
        return type_
    else:
        if Path(entry_point).suffix in {'.yaml', '.yml'}:
            raise ValueError(
                'Could not determine the entry point type from value: '
                f'{entry_point!r}. The file does not exist.')
        else:
            raise ValueError(
                'Could not determine the entry point type from value: '
                f'{entry_point!r}. Expected '
                'an existing file with extension .yaml or .yml, '
                'existing directory, glob-like pattern '
                '(i.e., *.py) or dotted path '
                '(i.e., module.sub_module.factory_function).')


def try_to_find_entry_point_type(entry_point):
    if entry_point is None:
        return None
    elif '*' in entry_point:
        return EntryPoint.Pattern
    elif '::' in entry_point:
        return EntryPoint.ModulePath
    elif Path(entry_point).exists():
        if Path(entry_point).is_dir():
            return EntryPoint.Directory
        else:
            return EntryPoint.File
    elif '.' in entry_point and Path(entry_point).suffix not in {
            '.yaml', '.yml'
    }:
        return EntryPoint.DottedPath
