#!/usr/bin/python3

# Copyright SUSE LLC
# SPDX-License-Identifier: MIT
"""Script to label openQA jobs."""

import argparse
import json
import logging
import subprocess  # noqa: S404
import sys
from urllib.parse import urljoin

import requests

logging.basicConfig()
log = logging.getLogger(sys.argv[0] if __name__ == "__main__" else __name__)
openqa_cmd = ""
args = ""


class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
    """Preserve multi-line __doc__ and provide default arguments in help strings."""


def _setup_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="openqa-label-all",
        description="""openqa-label-all allows comments to be applied to a set jobs, which match the
specified options, on the specified server.""",
        formatter_class=CustomFormatter,
    )
    parser.add_argument(
        "-v",
        "--verbose",
        help="Increase verbosity level, specify multiple times to increase verbosity",
        action="count",
        default=1,
    )
    parser.add_argument("--build", help="The build for which to look for a failing module")
    parser.add_argument("--module", required=True, help="The failing module to look for")
    parser.add_argument("--groupid", default=None, help="Limits labeling to this group only")
    parser.add_argument(
        "--openqa-host",
        default="https://openqa.suse.de",
        help="The openQA host to act on",
    )
    parser.add_argument("--result", help="The result of the jobs to look for", default="failed")
    parser.add_argument("--label", default="", help="The label to write as comment for each found job")
    parser.add_argument("--dry-run", action="store_true", help="Do not do any action on openQA")
    parser.add_argument(
        "--no-restart",
        action="store_true",
        help="Do not restart the jobs after labelling",
    )
    parser.add_argument("--timeout", type=float, default=60, help="Timeout for HTTP requests")
    return parser


def _parse_args() -> argparse.Namespace:
    parser = _setup_parser()
    args = parser.parse_args()
    verbose_to_log = {
        0: logging.CRITICAL,
        1: logging.ERROR,
        2: logging.WARNING,
        3: logging.INFO,
        4: logging.DEBUG,
    }
    logging_level = logging.DEBUG if args.verbose > len(verbose_to_log) else verbose_to_log[args.verbose]
    log.setLevel(logging_level)
    return args


def _call(cmds: list, args: argparse.Namespace) -> int:
    log.debug("call: %s", cmds)
    return subprocess.run((["echo", "Simulating: "] if args.dry_run else []) + cmds, check=True).returncode  # noqa: S603


def _openqa_call(cmds: list) -> int:
    return _call(openqa_cmd + cmds, args)


def _comments(job: dict) -> list:
    log.debug("Retrieving comments on job %s", job["id"])
    completed_process = subprocess.run([*openqa_cmd, f"jobs/{job['id']}/comments"], capture_output=True, check=True)  # noqa: S603
    job_comments = completed_process.stdout
    comments = " ".join([i["text"] for i in json.loads(job_comments)])
    log.debug("Comments: %s", comments)
    return comments


def _needs_label(label: str, job: dict) -> bool:
    has_label = label in _comments(job)
    log.debug("%s has label: %s", job["id"], has_label)
    return not has_label


def _main() -> int:
    global args, openqa_cmd  # noqa: PLW0603
    args = _parse_args()
    request_params = {"result": args.result, "latest": 1, "scope": "relevant"}

    openqa_cmd = ["openqa-cli", "api", "--host", args.openqa_host]

    if args.groupid:
        request_params.update({"groupid": args.groupid})

    if args.build:
        request_params.update({"build": args.build})

    log.debug("args: %s", args)
    host = args.openqa_host if args.openqa_host.startswith("https://") else "https://" + args.openqa_host
    url = "api/v1/jobs"

    r = requests.get(urljoin(host, url), params=request_params, timeout=args.timeout)
    r.raise_for_status()
    jobs = r.json()["jobs"]

    if len(jobs) <= 0:
        log.error("No jobs found for: %s", r.url)
        return 61

    jobs_with_failed_module = [
        j for j in jobs if [m for m in j["modules"] if m["name"] == args.module and m["result"] == args.result]
    ]

    unlabeled_jobs = (job["id"] for job in jobs_with_failed_module if _needs_label(args.label, job))

    for i in unlabeled_jobs:
        _openqa_call(["-X", "post", f"jobs/{i}/comments", f"text={args.label}"])

    if args.no_restart:
        log.debug("Not restarting jobs")
    else:
        log.debug("Restarting jobs")
        for i in (j["id"] for j in jobs_with_failed_module):
            _openqa_call(["-X", "post", f"jobs/{i}/restart"])
    return 0


if __name__ == "__main__":
    _main()
