#!/usr/bin/env python

# Copyright (C) 2010, 2011, 2012, 2013, 2014 Intel, Inc.
#
#    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.
#

"""A wrapped mail handler used in request jobs"""

import os
from urllib2 import HTTPError

# def := { event_name1: ( trigger, trigger ),
#          event_name2: ( trigger, ),
#        }
# trigger := { 'temp': 'template name',
#              'cond':  lambda incline function
#              'sub':   string template for subject
#              'to':    list of To:
#              'cc':    list of Cc:
#              'from':  From:
#              'fields': list of variables to be expand in template
#              'detail?': True/False, whether need detailed reqinfo
#              'tgtprj': the field name in obsEvent as the target prj
#            }
# NOTE: multiple triggers need be IN STRICT ORDER

IGNORE_HOME = True

AUTO_REJECT_COMMENT = 'You are not a valid submitrequest sender'

MAIL_TRIGGERS = {
        'OBS_SRCSRV_REQUEST_CREATE':
            (
             {
                'temp':  'obs_request_delpkg',
                'cond':  lambda bs, req:
                             req.get('deleteproject') and
                             req.get('deletepackage'),
                'sub':   '${reqid}: Remove package '
                         '${deleteproject}/${deletepackage}',
                'to':    ['mailinglist'],
                'cc':    [],
                'from':  'who',
                'fields':['description', 'deleteproject', 'deletepackage'],
                'detail?':False,
                'tgtprj': 'deleteproject',
             },
             {
                'temp':  'obs_request_changedevel',
                'cond':  lambda bs, req:
                             req.get('origintype') == 'change_devel',
                'sub':   '${reqid}: Change devel of '
                         '${targetproject}/${targetpackage} to '
                         '${sourceproject}/${sourcepackage}',
                'to':    ['mailinglist'],
                'cc':    [],
                'from':  'who',
                'fields':['description', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
             {
                'temp':  'obs_request_newpkg',
                'cond':  lambda bs, req:
                             not bs.exists(req['targetproject'],
                                           req['targetpackage']),
                'sub':   '${reqid}: New package '
                         '${targetproject}/${targetpackage}',
                'to':    ['mailinglist'],
                'cc':    [],
                'from':  'who',
                'fields':['description', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':True,
                'tgtprj': 'targetproject',
             },
             {
                'temp':  'obs_request_create',
                'cond':  None,
                'sub':   '${reqid}: Changes to '
                         '${targetproject}/${targetpackage}',
                'to':    ['mailinglist'],
                'cc':    [],
                'from':  'who',
                'fields':['description', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':True,
                'tgtprj': 'targetproject',
             },
            ),
        'OBS_SRCSRV_REQUEST_ACCEPTED':
            (
             {
                'temp':  'obs_request_changedevel_accept',
                'cond':  lambda bs, req:
                             req['state'] == 'accepted' and
                             req.get('origintype') == 'change_devel',
                'sub':   '${reqid} accepted: '
                         '${sourceproject}/${sourcepackage} is devel of '
                         '${targetproject}/${targetpackage}',
                'to':    ['mailinglist'],
                'cc':    [],
                'from':  'sender',
                'fields':['comment', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
             {
                'temp':  'obs_request_accept',
                'cond':  lambda bs, req: req['state'] == 'accepted',
                'sub':   '${reqid} accepted: '
                         '${targetproject}/${targetpackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'sender',
                'fields':['comment', 'targetproject', 'targetpackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
            ),
        'OBS_SRCSRV_REQUEST_STATECHANGE':
            (
             {
                'cond':  lambda bs, req:
                             req['state'] == 'declined' and
                             req.get('deleteproject') and
                             req.get('deletepackage'),
                'temp':  'obs_request_delpkg_decline',
                'sub':   '${reqid} declined: Request to delete '
                         '${deleteproject}/${deletepackge}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'sender',
                'fields':['comment', 'deleteproject', 'deletepackage'],
                'detail?':False,
                'tgtprj': 'deleteproject',
             },
             {
                'cond':  lambda bs, req:
                             req['state'] == 'declined' and
                             req.get('origintype') == 'change_devel',
                'temp':  'obs_request_changedevel_decline',
                'sub':   '${reqid} declined: '
                         '${sourceproject}/${sourcepackage} as devel of '
                         '${targetproject}/${targetpackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'sender',
                'fields':['comment', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
             {
                'cond':  lambda bs, req:
                             req['state'] == 'declined' and
                             req['comment'] == AUTO_REJECT_COMMENT,
                'temp':  'obs_request_auto_reject',
                'sub':   '${reqid} auto rejected: '
                         '${sourceproject}/${sourcepackage} to '
                         '${targetproject}/${targetpackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'sender',
                'fields':['comment', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
             {
                'cond':  lambda bs, req: req['state'] == 'declined',
                'temp':  'obs_request_decline',
                'sub':   '${reqid} declined: '
                         '${sourceproject}/${sourcepackage} to '
                         '${targetproject}/${targetpackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'sender',
                'fields':['comment', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
             {
                'cond':  lambda bs, req: req['state'] == 'accepted',
                'temp':  'obs_request_accept',
                'sub':   '${reqid} accepted: '
                         '${sourceproject}/${sourcepackage} to '
                         '${targetproject}/${targetpackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'sender',
                'fields':['comment', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
             {
                'cond':  lambda bs, req:
                             req['state'] == 'revoked' and
                             req.get('deleteproject') and
                             req.get('deletepackage'),
                'temp':  'obs_request_delpkg_revoke',
                'sub':   '${reqid} revoked: Request to delete '
                         '${deleteproject}/${deletepackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'who',
                'fields':['comment', 'deleteproject', 'deletepackage'],
                'detail?':False,
                'tgtprj': 'deleteproject',
             },
             {
                'cond':  lambda bs, req:
                             req['state'] == 'revoked' and
                             req.get('origintype') == 'change_devel',
                'temp':  'obs_request_changedevel_revoke',
                'sub':   '${reqid} revoked: Change devel of '
                         '${targetproject}/${targetpackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'who',
                'fields':['comment', 'targetproject', 'targetpackage',
                          'sourceproject', 'sourcepackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
             {
                'cond':  lambda bs, req: req['state'] == 'revoked',
                'temp':  'obs_request_revoke',
                'sub':   '${reqid} revoked: Changes to '
                         '${targetproject}/${targetpackage}',
                'to':    ['GIT_AUTHOR_EMAIL'],
                'cc':    ['mailinglist'],
                'from':  'who',
                'fields':['comment', 'targetproject', 'targetpackage'],
                'detail?':False,
                'tgtprj': 'targetproject',
             },
            ),
        }

USERDATA_CACHE = {}

def logger(level, msg):
    """Print log message"""
    print level, msg

def _get_reqinfo(_bs, rid, details):
    """Get request info"""
    try:
        return _bs.gen_request_info(str(rid), show_detail=details)
    except HTTPError, err:
        logger('error', 'failed to get info for req: %s, abort!' % rid)
        logger('error', str(err))
        return ''

def _is_valid_submitter(temp, author, enabled_users):
    """Judge whether the author is valid submitter or not"""
    if temp in ('obs_request_newpkg', 'obs_request_create'):
        if enabled_users:
            if author not in enabled_users:
                return False

    return True

def mailer(request, _bs, request_data, sr_enabled_users=None,
           templates_dir='.'):
    """ Sending mail to mailing list for different kinds of reqs
    """

    return_dict = {}

    if request['event_type'] not in MAIL_TRIGGERS:
        return {}
    rid = request['id']

    for event, triggers in MAIL_TRIGGERS.iteritems():
        if request['event_type'] != event:
            continue

        for tri in triggers:
            if not tri['cond'] or tri['cond'](_bs, request):
                tgtprj = request[tri['tgtprj']]
                if tgtprj is None:
                    logger('info', 'ignore requests for empty targetproject:%s'
                            % event)
                    return return_dict

                if IGNORE_HOME and tgtprj.startswith('home'):
                    logger('info', 'ignore requests for home project: %s' % \
                            event)
                    return return_dict

                if not _is_valid_submitter(tri['temp'], request['author'], \
                        sr_enabled_users):
                    _bs.req_decline(rid, msg=AUTO_REJECT_COMMENT)
                    return return_dict

                logger('info', 'sending mail for %s %s' %(event, tri['temp']))

                reqinfo = _get_reqinfo(_bs, rid, tri['detail?']).splitlines()
                # get first 100 lines if reqinfo exceed 100 lines
                if len(reqinfo) > 100:
                    reqinfo = reqinfo[0:100]
                    reqinfo.append('\nPlease refer to OBS webUI for more ' \
                            'details about this SR.')
                return_dict['reqinfo'] = '\n'.join(reqinfo)

                # prepare email 'to' list
                return_dict['To'] = []
                for _to in tri['to']:
                    if isinstance(request_data[_to], list):
                        return_dict['To'] += request_data[_to]
                    elif isinstance(request_data[_to], str) or \
                            isinstance(request_data[_to], unicode):
                        return_dict['To'] += request_data[_to].split(',')

                # prepare email 'cc' list
                return_dict['Cc'] = []
                for _cc in tri['cc']:
                    if isinstance(request_data[_cc], list):
                        return_dict['Cc'] += request_data[_cc]
                    elif isinstance(request_data[_cc], str) or \
                            isinstance(request_data[_cc], unicode):
                        return_dict['Cc'] += request_data[_cc].split(',')

                return_dict['realname'] = request_data['author']
                if request_data['bccs']:
                    return_dict['Bcc'] = request_data['bccs']

                return_dict['From'] = request_data['noreply_sender']

                return_dict['template'] = tri['temp']
                from string import Template
                return_dict['subject'] = Template(tri['sub']).safe_substitute(\
                        request, reqid=str(rid))

                # other items from obs_event
                for var in tri['fields']:
                    return_dict[var] = request[var]

                break

    if 'template' in return_dict:

        template_str = file(os.path.join(templates_dir, \
                return_dict['template'])).read()
        msg = []
        try:
            from Cheetah.Template import Template
            template = Template(template_str, searchList=return_dict)
            template.msg = "\n".join(msg)
            body = str(template)
        except ImportError:
            from string import Template
            template = Template(template_str)
            body = template.safe_substitute(return_dict, msg="\n".join(msg))
        return_dict['body'] = body

    return return_dict
