#!/usr/bin/python3

import gi
import git
import os
import sys

from gi.repository import GLib

gi.require_version("Modulemd", "2.0")
from gi.repository import Modulemd


def print_failure(failure):
    print("Error: %s" % failure.get_gerror())
    print("Offending YAML: \n%s" % failure.get_yaml())


def unusable_baseline():
    print("Baseline was unusable. Skipping test.")
    return os.EX_OK


def get_index_and_defaults(repo, filename, commit):
    try:
        yaml = repo.git.show("%s:%s" % (commit, filename))
    except git.exc.GitCommandError as e:
        # This file didn't exist in the commit, so return
        # FileNotFoundError to skip checking it.
        raise FileNotFoundError(
            "{} does not exist at commit {}".format(filename, commit)
        )

    index = Modulemd.ModuleIndex.new()
    try:
        ret, failures = index.update_from_string(yaml, True)
        if ret != True:
            for failure in failures:
                print_failure(failure)
            raise IOError("Invalid modulemd-defaults document")

    except gi.repository.GLib.Error as e:
        print("Error: %s" % e)
        raise IOError("Invalid YAML document")

    module_names = index.get_module_names()
    if len(module_names) > 1:
        raise IOError("YAML contained multiple modules")
    elif len(module_names) < 1:
        raise IOError("YAML contained no modules")

    defaults = index.get_module(index.get_module_names()[0]).get_defaults()
    if not defaults:
        raise IOError("YAML contained no defaults")

    return index, defaults


def main():
    filename = sys.argv[1]
    baseline_commit = sys.argv[2]
    updated_commit = sys.argv[3]

    script_dir = os.path.dirname(os.path.realpath(__file__))

    repo = git.Repo(script_dir, search_parent_directories=True)

    # First get the defaults from the baseline commit
    try:
        baseline_index, baseline_defaults = get_index_and_defaults(
            repo, filename, baseline_commit
        )
    except FileNotFoundError as e:
        # The baseline file didn't exist, so there's no valid original
        # to compare to. Check it only with validate.py.
        print(e, file=sys.stderr)
        return unusable_baseline()
    except IOError as e:
        # The baseline file was invalid (which should never happen...).
        # Check it only with validate.py (in case this PR is trying to
        # correct the situation
        print(e, file=sys.stderr)
        return os.EX_DATAERR

    try:
        updated_commit = updated_commit
        updated_index, updated_defaults = get_index_and_defaults(
            repo, filename, updated_commit
        )
    except FileNotFoundError as e:
        # The PR is removing this file. Assume that this is acceptable
        print(
            "{} is being removed. Not performing any comparison tests.".format(
                filename
            ),
            file=sys.stderr,
        )
        return os.EX_OK
    except IOError as e:
        # If we hit this, the patch is broken.
        print(e, file=sys.stderr)
        return os.EX_DATAERR

    if updated_defaults.get_modified() <= baseline_defaults.get_modified():
        print(
            "%s has changed but the 'modified' field has not increased."
            % filename
        )
        print("Baseline modified: {}".format(baseline_defaults.get_modified()))
        print("Updated modified: {}".format(updated_defaults.get_modified()))
        return os.EX_DATAERR

    # Confirm that these two sets of defaults merge cleanly
    merger = Modulemd.ModuleIndexMerger.new()
    merger.associate_index(baseline_index, 0)
    merger.associate_index(baseline_index, 0)

    try:
        merger.resolve()
    except gi.repository.GLib.Error as e:
        print("Merge Error: %s" % e, file=sys.stderr)
        return os.EX_DATAERR

    return os.EX_OK


if __name__ == "__main__":
    sys.exit(main())