import os

import py

from .exceptions import UsageError


def exists(path, ignore=EnvironmentError):
    try:
        return path.check()
    except ignore:
        return False


def getcfg(args, config=None):
    """
    Search the list of arguments for a valid ini-file for pytest,
    and return a tuple of (rootdir, inifile, cfg-dict).

    note: config is optional and used only to issue warnings explicitly (#2891).
    """
    from _pytest.deprecated import CFG_PYTEST_SECTION

    inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
    args = [x for x in args if not str(x).startswith("-")]
    if not args:
        args = [py.path.local()]
    for arg in args:
        arg = py.path.local(arg)
        for base in arg.parts(reverse=True):
            for inibasename in inibasenames:
                p = base.join(inibasename)
                if exists(p):
                    iniconfig = py.iniconfig.IniConfig(p)
                    if "pytest" in iniconfig.sections:
                        if inibasename == "setup.cfg" and config is not None:
                            from _pytest.warnings import _issue_config_warning
                            from _pytest.warning_types import RemovedInPytest4Warning

                            _issue_config_warning(
                                RemovedInPytest4Warning(
                                    CFG_PYTEST_SECTION.format(filename=inibasename)
                                ),
                                config=config,
                                stacklevel=2,
                            )
                        return base, p, iniconfig["pytest"]
                    if (
                        inibasename == "setup.cfg"
                        and "tool:pytest" in iniconfig.sections
                    ):
                        return base, p, iniconfig["tool:pytest"]
                    elif inibasename == "pytest.ini":
                        # allowed to be empty
                        return base, p, {}
    return None, None, None


def get_common_ancestor(paths):
    common_ancestor = None
    for path in paths:
        if not path.exists():
            continue
        if common_ancestor is None:
            common_ancestor = path
        else:
            if path.relto(common_ancestor) or path == common_ancestor:
                continue
            elif common_ancestor.relto(path):
                common_ancestor = path
            else:
                shared = path.common(common_ancestor)
                if shared is not None:
                    common_ancestor = shared
    if common_ancestor is None:
        common_ancestor = py.path.local()
    elif common_ancestor.isfile():
        common_ancestor = common_ancestor.dirpath()
    return common_ancestor


def get_dirs_from_args(args):
    def is_option(x):
        return str(x).startswith("-")

    def get_file_part_from_node_id(x):
        return str(x).split("::")[0]

    def get_dir_from_path(path):
        if path.isdir():
            return path
        return py.path.local(path.dirname)

    # These look like paths but may not exist
    possible_paths = (
        py.path.local(get_file_part_from_node_id(arg))
        for arg in args
        if not is_option(arg)
    )

    return [get_dir_from_path(path) for path in possible_paths if path.exists()]


def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None):
    dirs = get_dirs_from_args(args)
    if inifile:
        iniconfig = py.iniconfig.IniConfig(inifile)
        is_cfg_file = str(inifile).endswith(".cfg")
        sections = ["tool:pytest", "pytest"] if is_cfg_file else ["pytest"]
        for section in sections:
            try:
                inicfg = iniconfig[section]
                if is_cfg_file and section == "pytest" and config is not None:
                    from _pytest.deprecated import CFG_PYTEST_SECTION
                    from _pytest.warnings import _issue_config_warning

                    # TODO: [pytest] section in *.cfg files is deprecated. Need refactoring once
                    # the deprecation expires.
                    _issue_config_warning(
                        CFG_PYTEST_SECTION.format(filename=str(inifile)),
                        config,
                        stacklevel=2,
                    )
                break
            except KeyError:
                inicfg = None
        rootdir = get_common_ancestor(dirs)
    else:
        ancestor = get_common_ancestor(dirs)
        rootdir, inifile, inicfg = getcfg([ancestor], config=config)
        if rootdir is None:
            for rootdir in ancestor.parts(reverse=True):
                if rootdir.join("setup.py").exists():
                    break
            else:
                rootdir, inifile, inicfg = getcfg(dirs, config=config)
                if rootdir is None:
                    rootdir = get_common_ancestor([py.path.local(), ancestor])
                    is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/"
                    if is_fs_root:
                        rootdir = ancestor
    if rootdir_cmd_arg:
        rootdir_abs_path = py.path.local(os.path.expandvars(rootdir_cmd_arg))
        if not os.path.isdir(str(rootdir_abs_path)):
            raise UsageError(
                "Directory '{}' not found. Check your '--rootdir' option.".format(
                    rootdir_abs_path
                )
            )
        rootdir = rootdir_abs_path
    return rootdir, inifile, inicfg or {}
