"""
:copyright: 2010-2015 by Ronny Pfannschmidt
:license: MIT
"""
import os
import warnings

from ._version_cls import NonNormalizedVersion
from ._version_cls import Version
from .config import Configuration
from .config import DEFAULT_LOCAL_SCHEME
from .config import DEFAULT_TAG_REGEX
from .config import DEFAULT_VERSION_SCHEME
from .discover import iter_matching_entrypoints
from .utils import function_has_arg
from .utils import trace
from .version import format_version
from .version import meta

PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION"
PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}"

TEMPLATES = {
    ".py": """\
# coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
version = {version!r}
version_tuple = {version_tuple!r}
""",
    ".txt": "{version}",
}


def version_from_scm(root):
    warnings.warn(
        "version_from_scm is deprecated please use get_version",
        category=DeprecationWarning,
        stacklevel=2,
    )
    config = Configuration(root=root)
    # TODO: Is it API?
    return _version_from_entrypoints(config)


def _call_entrypoint_fn(root, config, fn):
    if function_has_arg(fn, "config"):
        return fn(root, config=config)
    else:
        warnings.warn(
            f"parse function {fn.__module__}.{fn.__name__}"
            " are required to provide a named argument"
            " 'config', setuptools_scm>=8.0 will remove support.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        return fn(root)


def _version_from_entrypoints(config: Configuration, fallback=False):
    if fallback:
        entrypoint = "setuptools_scm.parse_scm_fallback"
        root = config.fallback_root
    else:
        entrypoint = "setuptools_scm.parse_scm"
        root = config.absolute_root

    for ep in iter_matching_entrypoints(root, entrypoint, config):
        version = _call_entrypoint_fn(root, config, ep.load())
        trace(ep, version)
        if version:
            return version


def dump_version(root, version, write_to, template=None):
    assert isinstance(version, str)
    if not write_to:
        return
    target = os.path.normpath(os.path.join(root, write_to))
    ext = os.path.splitext(target)[1]
    template = template or TEMPLATES.get(ext)

    if template is None:
        raise ValueError(
            "bad file format: '{}' (of {}) \nonly *.txt and *.py are supported".format(
                os.path.splitext(target)[1], target
            )
        )

    parsed_version = Version(version)
    version_fields = parsed_version.release
    if parsed_version.dev is not None:
        version_fields += (f"dev{parsed_version.dev}",)
    if parsed_version.local is not None:
        version_fields += (parsed_version.local,)

    with open(target, "w") as fp:
        fp.write(template.format(version=version, version_tuple=tuple(version_fields)))


def _do_parse(config):

    trace("dist name:", config.dist_name)
    if config.dist_name is not None:
        pretended = os.environ.get(
            PRETEND_KEY_NAMED.format(name=config.dist_name.upper())
        )
    else:
        pretended = None

    if pretended is None:
        pretended = os.environ.get(PRETEND_KEY)

    if pretended:
        # we use meta here since the pretended version
        # must adhere to the pep to begin with
        return meta(tag=pretended, preformatted=True, config=config)

    if config.parse:
        parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse)
        if isinstance(parse_result, str):
            raise TypeError(
                "version parse result was a string\nplease return a parsed version"
            )
        version = parse_result or _version_from_entrypoints(config, fallback=True)
    else:
        # include fallbacks after dropping them from the main entrypoint
        version = _version_from_entrypoints(config) or _version_from_entrypoints(
            config, fallback=True
        )

    if version:
        return version

    raise LookupError(
        "setuptools-scm was unable to detect version for %r.\n\n"
        "Make sure you're either building from a fully intact git repository "
        "or PyPI tarballs. Most other sources (such as GitHub's tarballs, a "
        "git checkout without the .git folder) don't contain the necessary "
        "metadata and will not work.\n\n"
        "For example, if you're using pip, instead of "
        "https://github.com/user/proj/archive/master.zip "
        "use git+https://github.com/user/proj.git#egg=proj" % config.absolute_root
    )


def get_version(
    root=".",
    version_scheme=DEFAULT_VERSION_SCHEME,
    local_scheme=DEFAULT_LOCAL_SCHEME,
    write_to=None,
    write_to_template=None,
    relative_to=None,
    tag_regex=DEFAULT_TAG_REGEX,
    parentdir_prefix_version=None,
    fallback_version=None,
    fallback_root=".",
    parse=None,
    git_describe_command=None,
    dist_name=None,
    version_cls=None,
    normalize=True,
    search_parent_directories=False,
):
    """
    If supplied, relative_to should be a file from which root may
    be resolved. Typically called by a script or module that is not
    in the root of the repository to direct setuptools_scm to the
    root of the repository by supplying ``__file__``.
    """

    config = Configuration(**locals())
    return _get_version(config)


def _get_version(config):
    parsed_version = _do_parse(config)

    if parsed_version:
        version_string = format_version(
            parsed_version,
            version_scheme=config.version_scheme,
            local_scheme=config.local_scheme,
        )
        dump_version(
            root=config.root,
            version=version_string,
            write_to=config.write_to,
            template=config.write_to_template,
        )

        return version_string


# Public API
__all__ = [
    "get_version",
    "dump_version",
    "version_from_scm",
    "Configuration",
    "DEFAULT_VERSION_SCHEME",
    "DEFAULT_LOCAL_SCHEME",
    "DEFAULT_TAG_REGEX",
    "Version",
    "NonNormalizedVersion",
    # TODO: are the symbols below part of public API ?
    "function_has_arg",
    "trace",
    "format_version",
    "meta",
    "iter_matching_entrypoints",
]
