#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4
#
# Copyright (C) 2016 Samsung
#
#    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; version 2
#    of the License.
#
#    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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
"""
This code is called by repa webpage
"""

import os
import re
import sys
import shutil

from xml.dom import minidom
import xml.etree.ElementTree as ElementTree

from gbp.git.repository import GitRepositoryError

from osc import core
from time import sleep
import datetime

from common.gerrit import Gerrit, get_gerrit_event, GerritError, is_ref_deleted
from common.git import Git, clone_gitproject
from common.buildservice import BuildService
from common.prerelease import get_prerelease_project_name
from common.buildtrigger import trigger_info, trigger_next
from common import runner

# set default char-set endcoding to utf-8
reload(sys)
sys.setdefaultencoding('utf-8') # pylint: disable-msg=E1101

class LocalError(Exception):
    """Local error exception."""
    pass

def is_aggregate_package(build, proj, pack):
    if "_aggregate" in build.get_src_file_list(proj, pack):
        print '%s package is a aggregate package' %(pack)
        return True

    return False

def is_link_package(build, proj, pack):
    if "_link" in build.get_src_file_list_no_expand(proj, pack):
        print '%s package is a link package' %(pack)
        return True

    return False

def check_permission(build, project, userid):
    meta_xml = build.get_meta(project)
    print meta_xml
    xml_root = ElementTree.fromstringlist(meta_xml)
    for person in xml_root.findall('person'):
        if person.attrib['role'] == 'maintainer' and userid == person.attrib['userid']:
            return True

    return False

def accept_or_reject(build, prerelease_proj, git_tag, state, comment=''):
    info = build.get_info(prerelease_proj)

    print "repa %s %s (%s)" % (state, git_tag, prerelease_proj)

    submitter = info.get('submitter', '')
    projects = '[' + ', '.join(info['projects']) + ']'
    message = ''
    if submitter:
        message = "Submitter: %s\n" % submitter

    message += "Comments: %s \nGit project: %s\nTag: %s" \
        % (comment or "submission %s" % str(git_tag),
           projects, git_tag)

    target_prj = str(info['obs_target_prj'])

    # Create SR
    org_source_packages=build.get_package_list(prerelease_proj)
    source_packages=[]
    for p in org_source_packages:
        if not is_aggregate_package(build, prerelease_proj, p) \
               and not is_link_package(build, prerelease_proj, p):
            source_packages.append(p)

    # If source_packages is not exists in target_prj
    # create a package in target_prj
    if state == 'accepted':
        org_target_packages=build.get_package_list(target_prj)
        for package in source_packages:
            if not package in org_target_packages:
                build.create_package(target_prj, package)

    reqid = build.create_sr(prerelease_proj, source_packages,
                          target_prj, message=message)

    print 'created SR %s' % reqid

    # and immediately set its state
    message = "SR %s is set to %s" % (reqid, state)
    if comment:
        message += comment

    retry_count = 3
    ret = ""
    while retry_count >= 0:
        try:
            r = build.set_sr_state(reqid, state=state,
                            message=str(message), force=True)
            print 'set SR state to', state, r
            ret = ""
            break
        except Exception as err:
            print 'set SR error: %s Retry...' %(err)
            retry_count -= 1
            ret = str(err)
            sleep(1)
    return ret

def repa_reject(build, proj, git_tag, comment=''):
    try:
        return accept_or_reject(build, proj, git_tag, 'declined', comment)
    except Exception as err:
        return str(err)

def repa_accept(build, proj, git_tag, comment=''):
    try:
        return accept_or_reject(build, proj, git_tag, 'accepted', comment)
    except Exception as err:
        return str(err)

def process_single_item(no, repa_decision, obs_project, sr_tag, repa_comment, repa_user):
    """
    """

    error_string = ""

    print "\n=========="
    print "OBS_PROJECT = ", obs_project
    print "SR_TAG = ", sr_tag
    print "REPA_COMMENT = ", repa_comment
    print "REPA_DECISION = ", repa_decision
    print "REPA_USER = ", repa_user

    obs_api = os.getenv("OBS_API_URL")
    obs_user = os.getenv("OBS_API_USERNAME")
    obs_passwd = os.getenv("OBS_API_PASSWD")

    build = BuildService(obs_api, obs_user, obs_passwd)
    ret = 0

    # home:prerelease:Tizen:Mobile:submit:tizen:20161025.103628
    prerelease_proj=get_prerelease_project_name(obs_project, sr_tag)

    if os.getenv("REPA_CHECK_PERMISSION", 0):
        if not check_permission(build, obs_project, repa_user):
            error_string = "User %s does not have permission for OBS project %s" % (repa_user, obs_project)

    if error_string is None or error_string == "":
        if repa_decision == "accept":
            error_string = repa_accept(build, prerelease_proj, sr_tag, repa_comment)
        else:
            error_string = repa_reject(build, prerelease_proj, sr_tag, repa_comment)
            try:
                if error_string and ':basechecker:' in prerelease_proj:
                    print "%s project is remove." % prerelease_proj
                    sys.stdout.flush()
                    build.cleanup(prerelease_proj, "This project has expired")
                    bm_data = {"bm_stage": "REPA",
                               "sr_tag": sr_tag,
                               "profile":obs_project,
                               "status": "Succeeded",
                               "status_reason": "basechecker Project"}
                    trigger_next("BUILD-MONITOR_%s" % (no), bm_data)
                    bm_stage = "SR_Accept"
                    bm_start_datetime = datetime.datetime.now()
                    bm_end_datetime = datetime.datetime.now()
                    event_fields = {"id":0,"description":"","targetproject":"","state":"DECLINED", "sourceproject":prerelease_proj}
                    bm_data = {"bm_stage": bm_stage,
                               "bm_git_tag": sr_tag,
                               "event_fields" : event_fields,
                               "bm_start_datetime":str(bm_start_datetime),
                               "bm_end_datetime":str(bm_end_datetime) }
                    trigger_next("BUILD-MONITOR_%s" % bm_stage , bm_data)
                    return ""
                elif error_string:
                    print "Declined SR(%s) is in remove..." % prerelease_proj
                    sys.stdout.flush()
                    build.cleanup(prerelease_proj, "This project has expired")
                    error_string = ""
            except Exception as err:
                    print "%s project is remove error = %s" % (prerelease_proj, err)

    # BUILD-MONITOR
    bm_stage = "REPA"
    if error_string is None or error_string == "":
        status_str = "Succeeded"
    else:
        status_str = "Failed"
    bm_data = {"bm_stage": bm_stage,
               "sr_tag": sr_tag,
               "profile":obs_project,
               "status": status_str,
               "status_reason": error_string}
    trigger_next("BUILD-MONITOR_%s" % (no), bm_data)

    return error_string

if __name__ == '__main__':

    repa_decision = os.getenv("REPA_DECISION")
    obs_project   = os.getenv("OBS_PROJECT")
    sr_tag        = os.getenv("SR_TAG")
    repa_comment  = os.getenv("REPA_COMMENT")
    repa_user     = os.getenv("REPA_USER")

    errors = []

    if repa_decision and obs_project and sr_tag and repa_comment and repa_user:
        ret = process_single_item("0", repa_decision, obs_project, sr_tag, repa_comment, repa_user)
        if len(ret) > 0:
            errors.append(ret)

    trigger_item = os.getenv("TRIGGER_INFO", None)
    if trigger_item:
        item_list = trigger_info(trigger_item)
        for x in item_list:
            x['no'] = '{:03d}'.format(int(x['no']))
        new_list = sorted(item_list, key=lambda k: k['no'])
        new_list = sorted(new_list, key=lambda k: k['obs_project'])
        for x in new_list:
            no            = x.get('no')
            repa_decision = x.get('repa_decision_jenkins')
            obs_project   = x.get('obs_project')
            sr_tag        = x.get('sr_tag')
            repa_comment  = x.get('comment')
            repa_user     = x.get('repa_user')
            ret = process_single_item(no, repa_decision, obs_project, sr_tag, repa_comment, repa_user)
            if len(ret) > 0:
                errors.append(ret)

    if len(errors) >= 1:
        sys.exit(len(errors))
