"""Version and diagnostic information for the mudpy engine."""

# Copyright (c) 2018-2020 mudpy authors. Permission to use, copy,
# modify, and distribute this software is granted under terms
# provided in the LICENSE file distributed with this software.

import json
import sys


# TODO(fungi) Clean up once Python 3.8 is the oldest interpreter supported
try:
    import importlib.metadata
    use_importlib = True
except ModuleNotFoundError:
    import pkg_resources
    use_importlib = False


class VersionDetail:

    """Version detail for a Python package."""

    def __init__(self, package):
        if use_importlib:
            project_name = package.metadata.get('Name')
        else:
            project_name = package.project_name
        self.project_name = _normalize_project(project_name)
        version = package.version
        self.version_info = tuple(version.split('.'))

        # Build up a human-friendly version string for display purposes
        self.text = "%s %s" % (self.project_name, version)

        # Obtain Git commit ID from PBR metadata if present
        if use_importlib:
            dist = importlib.metadata.distribution(self.project_name)
        else:
            dist = pkg_resources.get_distribution(self.project_name)
        try:
            if use_importlib:
                pbr_metadata = dist.read_text("pbr.json")
            else:
                pbr_metadata = dist.get_metadata("pbr.json")
        except (IOError, KeyError):
            pbr_metadata = None
        if pbr_metadata:
            self.git_version = json.loads(pbr_metadata)["git_version"]
        else:
            self.git_version = None
        if self.git_version:
            self.text = "%s (%s)" % (self.text, self.git_version)

    def __repr__(self):
        return self.text


class Versions:

    """Tracks info on known Python package versions."""

    def __init__(self, project_name):
        # Normalize the supplied name
        project_name = _normalize_project(project_name)

        # Python info for convenience
        self.python_version = "%s Python %s" % (
            sys.platform, sys.version.split(" ")[0])

        # List of package names for this package's declared dependencies
        requirements = []
        if use_importlib:
            for req in importlib.metadata.distribution(project_name).requires:
                requirements.append(_normalize_project(req))
        else:
            for req in pkg_resources.get_distribution(project_name).requires():
                requirements.append(_normalize_project(req.project_name))

        # Accumulators for Python package versions
        self.dependencies = {}
        self.environment = {}

        # Loop over all installed packages
        if use_importlib:
            distributions = importlib.metadata.distributions()
        else:
            distributions = pkg_resources.working_set
        for package in distributions:
            version = VersionDetail(package)
            # Sort packages into the corresponding buckets
            if version.project_name in requirements:
                # This is a dependency
                self.dependencies[version.project_name] = version
            elif version.project_name == project_name:
                # This is our main package
                self.version = version
            else:
                # This may be a transitive dep, or merely installed
                self.environment[version.project_name] = version

        self.dependencies_text = ", ".join(
            sorted([x.text for x in self.dependencies.values()]))
        self.environment_text = ", ".join(
            sorted([x.text for x in self.environment.values()]))

    def __repr__(self):
        return "Running %s on %s with dependencies %s." % (
            self.version.text,
            self.python_version,
            self.dependencies_text,
            )


def _normalize_project(project_name):
    """Convenience function to normalize Python project names."""
    if use_importlib:
        return project_name.lower()
    else:
        return pkg_resources.safe_name(project_name).lower()
