#!/usr/bin/python3 -u
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import logging
import os
import sys
from argparse import ArgumentParser
from subprocess import PIPE, run
from typing import Optional, Tuple

from reportclient import (_, RETURN_OK, RETURN_FAILURE)
import report

FILENAME_DUPHASH = "duphash"
FILENAME_OSINFO = "os_info"
OSINFO_BUGZILLA_PRODUCT = "REDHAT_BUGZILLA_PRODUCT="
OSINFO_BUGZILLA_PRODUCT_VERSION = "REDHAT_BUGZILLA_PRODUCT_VERSION="

logging.basicConfig(format="%(name)s [%(levelname)s] %(message)s",
                    level=logging.WARNING)
logger = logging.getLogger("abrt-action-find-bodhi-update")


def load_elements(dump_dir_path: str) -> Tuple[str, Optional[str], Optional[str]]:
    dump_dir = report.dd_opendir(dump_dir_path, report.DD_OPEN_READONLY)
    if not dump_dir:
        logger.error(_("Cannot open problem directory '%s'"), dump_dir_path)
        sys.exit(RETURN_FAILURE)

    dd_load_flags = (report.DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE |
                     report.DD_FAIL_QUIETLY_ENOENT)

    duphash = dump_dir.load_text(FILENAME_DUPHASH, dd_load_flags)
    if not duphash:
        logger.error(_("Problem directory is missing file '%s'"), FILENAME_DUPHASH)
        dump_dir.close()
        sys.exit(RETURN_FAILURE)

    os_info = dump_dir.load_text(FILENAME_OSINFO, dd_load_flags)
    dump_dir.close()
    if not os_info:
        logger.error(_("Problem directory is missing file '%s'"), FILENAME_OSINFO)
        sys.exit(RETURN_FAILURE)

    # get Bugzilla Product and Version from os_info
    product: Optional[str] = os.getenv("Bugzilla_Product")
    version: Optional[str] = None
    for line in os_info.split("\n"):
        if not product and OSINFO_BUGZILLA_PRODUCT in line:
            product = parse_os_release_line(line)
        if OSINFO_BUGZILLA_PRODUCT_VERSION in line:
            version = parse_os_release_line(line)

    return duphash, product, version


def parse_os_release_line(line: str) -> str:
    """Parse key-value line and returns value"""
    return line.split("=")[1].strip().strip('"')


if __name__ == "__main__":
    arg_parser = ArgumentParser(
            description="Find Bodhi updates based on ABRT's problem directory. "
                        "This tool reads duphash file in problem directory and "
                        "searches for new updates according to the crash data.",
            epilog="environment variables: *Bugzilla_Product* -- Product bug "
                   "field value. Useful if you need a product different from "
                   "the one in PROBLEM_DIR/os_info.")
    arg_parser.add_argument("-d", "--problem-dir",
            type=str, default=".",
            help="Path to problem directory")
    arg_parser.add_argument("-b", "--without-bodhi",
            action="store_true", dest="without_bodhi", default=False,
            help="Run without abrt-bodhi. Prints only Bugzilla bug id of duplicate "
                 "if it exists.")
    arg_parser.add_argument("-v", "--verbose",
            action="count", dest="verbose", default=0,
            help="Be verbose")

    args = arg_parser.parse_args()
    try:
        dump_dir_path = os.path.abspath(args.problem_dir)
    except FileNotFoundError as ex:
        logging.error(_("Problem directory error: %s"), ex)
        sys.exit(RETURN_FAILURE)

    ABRT_VERBOSE = os.getenv("ABRT_VERBOSE")
    if ABRT_VERBOSE:
        try:
            args.verbose = int(ABRT_VERBOSE)
        except ValueError:
            pass

    if args.verbose > 0:
        logger.setLevel(logging.INFO)

    duphash, product, version = load_elements(dump_dir_path)

    if product:
        logger.info(_("Using product %s."), product)
    else:
        logger.info(_("Using product '%s' from /etc/os-release."), OSINFO_BUGZILLA_PRODUCT)
    if version:
        logger.info(_("Using product version %s."), version)

    # Find bugzilla bug with abrt_hash: == $duphash_content and product ==
    # $product, if OSINFO_BUGZILLA_PRODUCT from crash's os_info doesn't exist,
    # the OSINFO_BUGZILLA_PRODUCT from /etc/os-release is used
    cmdline = ["reporter-bugzilla", "-h", duphash]
    if product:
        cmdline.extend(["-p", product])

    proc = run(cmdline, stdout=PIPE, check=False, encoding="utf-8")
    if proc.returncode:
        logger.error(_("Search for duplicate bugs failed: %s"), proc.stderr)
        sys.exit(RETURN_FAILURE)

    bug_id = proc.stdout.strip()

    if bug_id:
        logger.warning(_("Duplicate Bugzilla bug '#%s' was found"), bug_id)
    else:
        logger.info(_("There is no Bugzilla bug with 'abrt_hash:%s'"), duphash)
        sys.exit(RETURN_OK)

    if version and "rawhide" in version.lower():
        logger.error(_("abrt-bodhi does not support product version 'Rawhide'"))
        sys.exit(RETURN_OK)

    if not args.without_bodhi:
        cmdline = ["abrt-bodhi", "-b", bug_id, "-d", dump_dir_path]
        if version:
            cmdline.extend(["-r", version])
        run(cmdline, check=False)

    sys.exit(RETURN_OK)
