#!/usr/bin/env python

###############################################################################
# (c) Copyright 2016 CERN                                                     #
#                                                                             #
# This software is distributed under the terms of the GNU General Public      #
# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
#                                                                             #
# In applying this licence, CERN does not waive the privileges and immunities #
# granted to it by virtue of its status as an Intergovernmental Organization  #
# or submit itself to any jurisdiction.                                       #
###############################################################################
'''
Command line client that interfaces to the YUMChecker class

:author: Stefan-Gabriel Chitic
'''
from __future__ import print_function
import logging
import optparse
import os
import sys
import traceback
import tempfile
import json
from lbinstall.YumChecker import YumChecker


# Class for known install exceptions
###############################################################################


class LHCbRPMReleaseConsistencyException(Exception):
    """ Custom exception for lb-install

    :param msg: the exception message
    """

    def __init__(self, msg):
        """ Constructor for the exception """
        # super( LHCbRPMReleaseConsistencyException, self).__init__(msg)
        Exception.__init__(self, msg)


# Classes and method for command line parsing
###############################################################################


class LHCbRPMReleaseConsistencyOptionParser(optparse.OptionParser):
    """ Custom OptionParser to intercept the errors and rethrow
    them as LHCbRPMReleaseConsistencyExceptions """

    def error(self, msg):
        """
        Arguments parsing error message exception handler

        :param msg: the message of the exception
        :return: Raises LHCbRPMReleaseConsistencyException with the exception message
        """
        raise LHCbRPMReleaseConsistencyException("Error parsing arguments: " + str(msg))

    def exit(self, status=0, msg=None):
        """
        Arguments parsing error message exception handler

        :param status: the status of the application
        :param msg: the message of the exception
        :return: Raises LHCbRPMReleaseConsistencyException with the exception message
        """
        raise LHCbRPMReleaseConsistencyException("Error parsing arguments: " + str(msg))


class LHCbRPMReleaseConsistencyClient(object):
    """ Main class for the tool """

    def __init__(self, configType, arguments=None,
                 dry_run=False, prog=" LHCbRPMReleaseConsistency"):
        """ Common setup for both clients """
        self.configType = configType
        self.log = logging.getLogger(__name__)
        self.arguments = arguments
        self.checker = None
        self.prog = prog

        parser = LHCbRPMReleaseConsistencyOptionParser(usage=usage(self.prog))
        parser.add_option('-d', '--debug',
                          dest="debug",
                          default=False,
                          action="store_true",
                          help="Show debug information")
        parser.add_option('--info',
                          dest="info",
                          default=False,
                          action="store_true",
                          help="Show logging messages with level INFO")
        parser.add_option('--build-folder',
                          dest="buildfolder",
                          default='/data/archive/artifacts/release/'
                                  'lhcb-release/',
                          action="store",
                          help="Add custom folder for builds")
        parser.add_option('--repo-url',
                          dest="repourl",
                          default='https://cern.ch/lhcb-nightlies-artifacts/'
                                  'release/lhcb-release/',
                          action="store",
                          help="Add custom repo url")
        parser.add_option('--no-details',
                          dest="nodetails",
                          default=False,
                          action="store_true",
                          help="Displays only the name of"
                               " the missing packages.")
        self.parser = parser

    def main(self):
        """ Main method for the ancestor:
        call parse and run in sequence

        :returns: the return code of the call
        """
        rc = 0
        try:
            opts, args = self.parser.parse_args(self.arguments)
            # Checkint the siteroot and URL
            # to choose the siteroot
            self.siteroot = tempfile.gettempdir()

            # Now setting the logging depending on debug mode...
            if opts.debug or opts.info:
                logging.basicConfig(format="%(levelname)-8s: "
                                    "%(funcName)-25s - %(message)s")
                if opts.info:
                    logging.getLogger().setLevel(logging.INFO)
                else:
                    logging.getLogger().setLevel(logging.DEBUG)

            self.buildfolder = opts.buildfolder
            self.repourl = opts.repourl

            # Getting the function to be invoked
            self.run(opts, args)

        except LHCbRPMReleaseConsistencyException as lie:
            print("ERROR: " + str(lie), file=sys.stderr)
            self.parser.print_help()
            rc = 1
        except:
            print("Exception in lb-install:", file=sys.stderr)
            print('-'*60, file=sys.stderr)
            traceback.print_exc(file=sys.stderr)
            print('-'*60, file=sys.stderr)
            rc = 1
        return rc

    def run(self, opts, args):
        """ Main method for the command

        :param opts: The option list
        :param args: The arguments list
        """
        # Parsing first argument to check the mode

        # Setting up repo url customization
        # By default repourl is none, in which case the hardcoded default
        # is used skipConfig allows returning a config with the LHCb
        # repositories

        from lbinstall.LHCbConfig import Config
        conf = Config(self.siteroot)
        local_url = "%s" % (self.repourl)
        local_folder = "%s" % (self.buildfolder)
        conf.repos["local_repo"] = {"url": local_url}
        rpm_list = [f for f in os.listdir(local_folder)
                    if f.endswith('.rpm')]

        self.checker = YumChecker(siteroot=self.siteroot,
                                  config=conf,
                                  strict=True,
                                  simple_output=opts.nodetails)
        platform = args[0]
        packages = []
        tmp_platform = platform.replace('-', '_')
        for rpm in rpm_list:
            if tmp_platform not in rpm:
                continue
            tmp = rpm.split('-')
            if len(tmp) > 0:
                name = tmp[0]
                name = name.replace('+', '\+')
            else:
                raise Exception("No packages found")
            if len(tmp) > 1:
                version = tmp[1]
                vaersion = version.split('.')[0]
            else:
                version = None
            if len(tmp) > 2:
                release = tmp[2]
                release =release.split('.')[0]
            else:
                release = None

            packages.extend(self.checker.queryPackages(name,
                                                       None,
                                                       None))
        for rpmname, version, release in packages:
            self.log.info("Checking consistency for: %s %s %s"
                          % (rpmname, version, release))
        json_file = os.path.join(local_folder,
                                 'build', platform, 'RPMs_report.json')
        res = self.checker.getMissingPackgesFromTuples(packages,
                                                       force_local=True)
        new_data = {}
        for missing in res:
            req = missing['dependency']
            req_name = "%s.%s.%s" % (req.name,
                                     req.version,
                                     req.release)
            p = missing['package']
            p_name = "%s.%s.%s" % (p.name,
                                   p.version,
                                   p.release)
            if not new_data.get(p_name, None):
                new_data[p_name] = []
            new_data[p_name].append(req_name)
        if not os.path.isdir(os.path.dirname(json_file)):
            os.makedirs(os.path.dirname(json_file))
        with open(json_file, 'w') as outfile:
            new_data = {
                'missing_dependencies': new_data
            }
            json.dump(new_data, outfile)


###############################################################################
def usage(cmd):
    """ Prints out how to use the script...

    :param cmd: the command executed
    """
    cmd = os.path.basename(cmd)
    return """\n%(cmd)s - '

It can be used in the following way:

%(cmd)s [build_id]
Verifies the consistency of RPM(s) from the yum repository for all the releases
in the build id.

""" % {"cmd": cmd}


def LHCbRPMReleaseConsistency(configType="LHCbConfig", prog="lbyumcheck"):
    """
    Default caller for command line LHCbRPMReleaseConsistency client
    :param configType: the configuration used
    :param prog: the name of the executable
    """

    logging.basicConfig(format="%(levelname)-8s: %(message)s")
    logging.getLogger().setLevel(logging.WARNING)
    sys.exit(LHCbRPMReleaseConsistencyClient(configType, prog=prog).main())

# Main just chooses the client and starts it
if __name__ == "__main__":
    LHCbRPMReleaseConsistency()
