# Copyright 2024 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# noqa: E402 pylint: disable=invalid-name

"""Contains all the pre-upload and pre-commit validation checks for SuiteSets"""

# Optional but recommended
PRESUBMIT_VERSION = "2.0.0"

# Mandatory: run under Python 3
USE_PYTHON3 = True


def CheckProtosAreUpToDate(input_api, output_api):
    """CheckProtosAreUpToDate validates the generated protos are up-to-date."""
    suite_set_root = input_api.PresubmitLocalPath()
    err = _generate_protos(input_api, suite_set_root)
    if err:
        return [output_api.PresubmitError(err)]

    proto_dir = input_api.os_path.join(suite_set_root, "generated")
    proto_diff, err = _git_diff(input_api, proto_dir)
    if err:
        return [output_api.PresubmitError(err)]
    if proto_diff:
        err_msg = (
            "The generated proto files are not up-to-date.\n\n"
            "Amend the commit to include the unstaged changes\n"
            "to the generated proto files."
        )
        return [output_api.PresubmitError(err_msg)]
    return []


def CheckSuiteSetsAreWellFormed(input_api, output_api):
    """CheckSuiteSetsAreWellFormed checks the wellformedness of the protos."""
    script_path = input_api.os_path.join(
        input_api.PresubmitLocalPath(), "presubmit/validate_suite_sets.py"
    )
    suite_sets_proto_file = input_api.os_path.join(
        input_api.PresubmitLocalPath(), "generated/suite_sets.jsonpb"
    )
    suites_proto_file = input_api.os_path.join(
        input_api.PresubmitLocalPath(), "generated/suites.jsonpb"
    )
    any_file_affected = (
        _is_affected_file(input_api, script_path)
        or _is_affected_file(input_api, suite_sets_proto_file)
        or _is_affected_file(input_api, suites_proto_file)
    )
    if not any_file_affected:
        return []

    cmd_name = "validate_suite_sets"
    cmd = [
        input_api.python3_executable,
        script_path,
        "--suite_set_file",
        suite_sets_proto_file,
        "--suite_file",
        suites_proto_file,
    ]
    presubmit_cmd = input_api.Command(
        name=cmd_name,
        cmd=cmd,
        kwargs={},
        message=output_api.PresubmitPromptWarning,
    )
    print("Running " + cmd_name)
    return input_api.RunTests([presubmit_cmd])


def _generate_protos(input_api, suite_set_root):
    """_generate_protos calls the given script."""
    generate_script = input_api.os_path.join(suite_set_root, "generate.sh")
    cmd = [generate_script]
    proc = input_api.subprocess.Popen(
        args=cmd,
        stdout=input_api.subprocess.PIPE,
        stderr=input_api.subprocess.PIPE,
        universal_newlines=True,
    )
    _, err = proc.communicate()
    if proc.returncode and err:
        return f"Error when calling {generate_script}: {str(err)}"
    return None


def _git_diff(input_api, path):
    """_git_diff returns the string generated by git diff for the given path."""
    cmd = ["git", "diff", path]
    proc = input_api.subprocess.Popen(
        args=cmd,
        stdout=input_api.subprocess.PIPE,
        stderr=input_api.subprocess.PIPE,
        universal_newlines=True,
    )
    out, err = proc.communicate()
    if proc.returncode and err:
        return None, f"Error when calling git diff: {str(err)}"
    return out.strip(), None


def _is_affected_file(input_api, file_to_check):
    """_is_affected_file returns True if given file in the affected files."""

    def is_file_to_check(file):
        norm_f_path = input_api.os_path.normpath(file.AbsoluteLocalPath())
        norm_f_to_check_path = input_api.os_path.normpath(file_to_check)
        return norm_f_path == norm_f_to_check_path

    affected_files = input_api.AffectedFiles(file_filter=is_file_to_check)
    return len(affected_files) > 0
