#!/usr/bin/env python

# Global Modules
from subprocess import *
import sys
import re
import os
import portage
from portage.dep import dep_getcpv
import base64
import requests

from tatt.gentooPackage import gentooPackage as gP
import tatt.packageFinder as packageFinder
from tatt.scriptwriter import writeusecombiscript as writeUSE
from tatt.scriptwriter import writerdepscript as writeRdeps
from tatt.scriptwriter import writesucessreportscript as writeSuccess
from tatt.scriptwriter import writecommitscript as writeCommit
from tatt.scriptwriter import writeCleanUpScript as writeCleanup
from tatt.tattConfig import tattConfig as tattConfig
from tatt.job import job as job
from tatt.tool import get_repo_dir

##### Generate a global config obj, reading from ~/.tatt #####
config = tattConfig()
session = requests.Session()
session.params.update({'Bugzilla_api_key': config['bugzilla-key']})

######### Main program starts here ###############

### USAGE and OPTIONS ###
from optparse import OptionParser

parser=OptionParser()
parser.add_option("-d", "--depend",
                  help="Determine stable rdeps",
                  dest="depend",
                  action="store_true",
                  default = False)
parser.add_option("-u", "--use", "--usecombis",
                  help="Determine use flag combinations",
                  dest="usecombi",
                  action="store_true",
                  default = False)
parser.add_option("-f", "--file", 
                  help="Input File containing packages",
                  dest="infile",
                  action="store"
                  )
parser.add_option("-j", "--jobname",
                  help="name for the job, prefix of output files",
                  dest="jobname",
                  action="store")
parser.add_option("-b", "--bug",
                  help="do the full program for a given stable request bug",
                  dest="bugnum",
                  # type="int",
                  # We could test this here, but in most cases the bugnumber
                  # is actually used as a string, so we validate further down
                  # and leave it as a string
                  action="store")
parser.add_option("-s", "--success",
                  help="Comment that the program was successfully tested",
                  dest="succbugnum",
                  action="store")
parser.add_option("-r", "--resolve",
                  help="Resolve the given bugnumber, needs a message",
                  dest="resolvenum",
                  action="store")
parser.add_option("-c", "--close",
                  help="Resolve the given bugnumber with closing it, needs to be combined with -r",
                  dest="close",
                  action="store_true")
parser.add_option("-k", "--keywording",
                  help="search for keywording packages, needs to be combined with -f",
                  dest="keywording",
                  action="store_true",
                  default = False)
parser.add_option("-m", "--message",
                  help="Message for bug resolution.",
                  dest="resolvemessage",
                  action="store")
parser.add_option("-v", "--verbose",
                  help="Print informative output.",
                  dest="verbose",
                  action="store_true",
                  default = False)

(options,args) = parser.parse_args()

## Messing with the configuration:
# Save verbosity level
config['verbose']=options.verbose
# Normalize the template dir:
config['template-dir']=os.path.abspath(config['template-dir'])+os.sep
# If given, test if the bugnumber represents an int
try:
    int(options.bugnum)
except ValueError:
    print ("The bugnumber you gave with -b should be an integer.")
    sys.exit(1)
except TypeError:
    # This occurs if bugnum is None, that is, -b was not given
    pass

## If safedir is set, check for the current directory
if config['safedir'] != "":
    if os.getcwd().find(config['safedir']) == -1:
        # Safedir not found
        print ("Your safedir variable is set to '" + config['safedir'] + "',")
        print ("but you are in " + os.getcwd())
        print ("Exiting.")
        sys.exit (1)

## -s and a bugnumber was given ?
if options.succbugnum:
    print("Reporting success for bug number " + options.succbugnum)
    retcode = call(['bugz', 'modify', options.succbugnum, '-c', config['successmessage']])
    if retcode == 0:
        print("Success!");
        sys.exit (0)
    else:
        print("Failure commenting on Bugzilla")
        sys.exit(1)

# get a job object to save things to
myJob = job()

## If -f and a filename have been given:
if options.infile:
    try:
        packfile=open(options.infile, 'r')
    except IOError:
        print("Given filename not found !")
        sys.exit(1)
    packraw = packfile.read()
    packfile.close()
    targetarch = config['arch']
    if options.keywording:
        targetarch = '~' + targetarch
        myJob.type="keyword"
    else:
        myJob.type="stable"

    myJob.packageList = packageFinder.findPackages(packraw, targetarch, get_repo_dir(config['repodir']))
## -b and a bugnumber was given ?
if options.bugnum:
    print("Bugnumber:  " + options.bugnum)
    myJob.bugnumber=options.bugnum
    params = {"id": options.bugnum}
    response = session.get(config["bugzilla-url"] + "/rest/bug", params=params).json()
    if "message" in response:
        print(response["message"])
        sys.exit(1)
    if len(response["bugs"]) == 0:
        print("bug " + options.bugnum + " not found in bugzilla")
        sys.exit(1)

    response = response["bugs"][0]
    if "KEYWORDREQ" in response["keywords"] or response["component"] == "Keywording":
        # This is a keywording bug:
        print ("Keywording bug detected.")
        myJob.type="keyword"
    elif "STABLEREQ" in response["keywords"] or response["component"] == "Stabilization" or response["component"] == "Vulnerabilities":
        # Stablebug
        print ("Stabilization bug detected.")
        myJob.type="stable"
    else:
        print ("Could not detect bug's type, is the 'Keywords' field set?")
        sys.exit(1)
    if myJob.packageList==None:
        if response["cf_stabilisation_atoms"]:
            myJob.packageList = packageFinder.findPackages(response["cf_stabilisation_atoms"], config['arch'], get_repo_dir(config['repodir']), options.bugnum)
            if len(myJob.packageList) == 0 and ("KEYWORDREQ" in response["keywords"] or response["component"] == "Keywording"):
                myJob.packageList = packageFinder.findPackages(response["cf_stabilisation_atoms"], config['arch'], get_repo_dir(config['repodir']), options.bugnum)
        else:
            response = session.get(config["bugzilla-url"] + "/rest/bug/{}/attachment".format(options.bugnum), params=params).json()["bugs"][str(options.bugnum)]
            for attachment in response:
                if attachment["is_obsolete"] == 1:
                    continue
                for flag in attachment['flags']:
                    if flag["name"] == "stabilization-list" and flag["status"] == '+':
                        myJob.packageList = packageFinder.findPackages(base64.b64decode(attachment["data"]).decode("utf8"), config['arch'], get_repo_dir(config['repodir']), options.bugnum)


# joint code for -f and -b
##########################

if myJob.packageList is not None and len(myJob.packageList) > 0:
    ## Assigning jobname
    if options.jobname:
        myJob.name = options.jobname
    elif options.infile:
        myJob.name = options.infile
    elif options.bugnum:
        myJob.name = myJob.packageList[0].packageName() + '-' + options.bugnum
    else:
        myJob.name = myJob.packageList[0].packageName()
    print ("Jobname: " + myJob.name)
    ## Determine jobtype

    port = portage.db[portage.root]["porttree"].dbapi

    filteredPackages = []
    # for keywording bugs the packages that already have the keyword still need
    # to be unmasked so they can be used by the other packages that still need work
    kwPackages = []

    if config['arch']:
        targetarch = config['arch']
        if myJob.type == "keyword":
            targetarch = '~' + targetarch

        for p in myJob.packageList:
            print("Found the following package atom : " + p.packageString())

            # check if the package already has the needed keywords
            kw = port.aux_get(dep_getcpv(p.packageString()), ["KEYWORDS"])
            if len(kw) > 0:
                kwl = kw[0].split()
                try:
                    kwl.index(config['arch'])
                    # the list of keywords in portage already contains the arch
                    # as stable, skip this package
                    print("\talready stable for " + config['arch'])
                    continue
                except ValueError:
                    pass

                try:
                    kwl.index(targetarch)
                    # the list of keywords in portage already contains the target keyword,
                    # skip this package from building, but remember it for unmasking
                    print("\talready keyworded as " + targetarch)
                    kwPackages.append(p)
                    continue
                except ValueError:
                    filteredPackages.append(p)

    if len(filteredPackages) == 0:
        print("\nno packages left")
        sys.exit(0)

    myJob.packageList = filteredPackages

    # Unmasking:
    unmaskname=config['unmaskdir']
    if os.path.exists(unmaskname) and not os.path.isdir(unmaskname):
        print ("unmaskdir '", unmaskname, "' exists and is no directory")
        sys.exit(1)
    elif not os.path.exists(unmaskname):
        os.mkdir(unmaskname, 0o755)
    unmaskname=unmaskname+"/tatt_"+myJob.name

    try:
        unmaskfile=open(unmaskname, 'r+')
    except IOError:
        try:
            unmaskfile=open(unmaskname, 'w')
            unmaskfile.write(" ")
            unmaskfile.close()
        except IOError:
            # If we can't write to the file, then it should be configured differently
            print (" ".join(["Can not write to ",unmaskname]))
            print ("Maybe you don't have permission or the location is invalid.")
            print (" ".join(["Is",config['unmaskdir'],"a writeable directory?"]))
            print ("Probably you want to configure a different unmaskfile")
            print ("in your ~/.tatt.  Exiting")
            sys.exit(1)
        unmaskfile=open(unmaskname, 'r+')

    unmaskfileContent = unmaskfile.read()
    for p in myJob.packageList:
        # Test if unmaskfile already contains the atom
        if re.search(re.escape(p.packageString()), unmaskfileContent):
            print (p.packageString() + " already in "+unmaskname)
        else:
            unmaskfile.write(p.packageString())
            if myJob.type=="stable":
                pass
            elif myJob.type=="keyword":
                unmaskfile.write(" ** ")
            else:
                print ("Uh Oh, no job.type? Tell tomka@gentoo.org to fix this!")
            unmaskfile.write("  # Job " + myJob.name + "\n")
            print ("Unmasked " + p.packageString()+ " in "+unmaskname)

    # now write the remaining packages for keywording
    for p in kwPackages:
        # Test if unmaskfile already contains the atom
        if re.search(re.escape(p.packageString()), unmaskfileContent):
            print (p.packageString() + " already in "+unmaskname)
        else:
            unmaskfile.write(p.packageString() + "  # Job " + myJob.name + "\n")
            print ("Unmasked " + p.packageString() + " in " + unmaskname)

    unmaskfile.close()
    ## Write the scripts
    writeUSE(myJob, config)
    writeRdeps(myJob, config)
    writeCleanup (myJob, config, unmaskname)
    ## Successscript can only be written if we have a bugnumber
    if myJob.bugnumber:
        writeSuccess(myJob, config)
    writeCommit(myJob, config)
    sys.exit (0)

# Code for resolving bugs (-r and -m)
#####################################
if options.resolvenum:
    if not options.resolvemessage:
        print("Please call with a message per -m")
        sys.exit (1)
    print("Resolving bug number " + options.resolvenum)
    calllist = ['bugz', 'modify', options.resolvenum, '-c', options.resolvemessage, '--remove-cc', config['arch']+"@gentoo.org"]
    if options.close:
        calllist = calllist + ['--fixed']
    retcode = call(calllist)
    if retcode == 0:
        print("Success!");
        sys.exit (0)
    else:
        print("Failure accessing bugzilla.")
        sys.exit(1)


## If we arrive here then a package atom should be given
try:
    myJob.packageList=[gP(args[0])]
    myJob.name=myJob.packageList[0].packageName()
except IndexError:
    print("Please call with complete package atom (including version) as argument.")
    sys.exit (1)

if options.depend:
    writeRdeps(myJob, config)

if options.usecombi:
    writeUSE(myJob, config)

## That's all folks ##
