# -*- coding: utf-8 -*-

from __future__ import print_function
import os
import sys
import json
import argparse
import time
from datetime import datetime, date as _date
from dateutil.relativedelta import relativedelta
try:
    from email.Utils import formatdate
except ImportError:  # py3
    from email.utils import formatdate
import requests
import os.path


def logs(domain, api_key, begin=None, end=None, max_retries=None, verbose=False):
    url = 'https://api.mailgun.net/v2/%s/events' % domain
    params = {
        'begin': begin or formatdate(),
    }
    if end:
        params['end'] = end
    init = True
    while True:
        count_get_page_retry = 0
        if params:
            response = requests.get(url=url, auth=('api', api_key), params=params)
            if not response.ok:
                while count_get_page_retry < max_retries:
                    time.sleep(2)
                    response = requests.get(url=url, auth=('api', api_key), params=params)
                    count_get_page_retry += 1
                    if response.ok:
                        break

        else:
            response = requests.get(url=url, auth=('api', api_key))
            if not response.ok:
                while count_get_page_retry < max_retries:
                    time.sleep(2)
                    response = requests.get(url=url, auth=('api', api_key), params=params)
                    count_get_page_retry += 1
                    if response.ok:
                        break

        if not response.ok:
            raise ValueError('Invalid status_code: {0}'.format(response.status_code))

        if verbose:
            print('# {0} {1}'.format(response.request.method, response.request.url), file=sys.stderr)
            print('# STATUS CODE: {0}'.format(response.status_code), file=sys.stderr)

        data = response.json()

        items = data['items']
        for record in items:
            yield record

        if not len(items):
            if init:
                # first iteraction, fetch first page
                url = data['paging']['first']
                params=None
            else:
                # EOF
                return
        else:
            url = data['paging']['next']
        init = False


def strdate_to_rfc2822(value=None, midnight=False, now=False):
    """Convert date in format YYYY/MM/DD to RFC2822 format.
        If value is None, return current utc time.
    """
    if midnight and now:
        raise ValueError('Choose one of "midnight" or "now"')

    strdate = value if value else datetime.utcnow().strftime('%Y/%m/%d')
    datetimetuple = list(map(int, strdate.split('/')))
    if midnight:
        datetimetuple += (0, 0, 0)
    elif now:
        now = datetime.utcnow()
        datetimetuple += (now.hour, now.minute, now.second)
    else:
        datetimetuple += (23, 59, 59)

    date = datetime(*datetimetuple)
    timestamp = time.mktime(date.utctimetuple())
    return date.strftime('%a, %d %b %Y %H:%M:%S -0000')


def main():
    parser = argparse.ArgumentParser(description='Retrieve Mailgun event logs.')

    parser.add_argument('-d', '--days', help='Days ago (N)',
                        dest='days',
                        type=int,
                        default=None)

    parser.add_argument('-b', '--begin', help='Begin date (YYYY/MM/DD)',
                        dest='begin',
                        default=None)

    parser.add_argument('-e', '--end', help='End date (YYYY/MM/DD)',
                        dest='end',
                        default=None)

    parser.add_argument('-j', '--json', help='Print json (original) log records',
                        dest='json',
                        action='store_true',
                        default=False)

    parser.add_argument('-V', '--version', help='Print version to stdout and exit',
                        dest='version',
                        action='store_true',
                        default=False)

    parser.add_argument('domain', help='Domain registered on Mailgun (or set env var MAILGUN_DOMAIN)',
                        metavar='domain',
                        type=str,
                        nargs='?',
                        default=None)

    parser.add_argument('api_key', help='Mailgun API KEY (or set env var MAILGUN_API_KEY)',
                        metavar='api_key',
                        type=str,
                        nargs='?',
                        default=None)

    parser.add_argument('-r', '--retries', help="Number of retries while",
                        dest='max_retries',
                        type=int,
                        default=100)

    parser.add_argument('--file', help="Output file",
                        dest='filename',
                        type=str,
                        default=None)

    parser.add_argument('--logstash', help="Output logstash url",
                        dest='logstashurl',
                        type=str,
                        default=None)

    parser.add_argument('-v', '--verbose', help='Print debug messages on stderr',
                        dest='verbose',
                        action='store_true',
                        default=False)

    args = parser.parse_args()

    if args.version:
        from . import __title__, __version__
        print('{0}-{1}'.format(__title__, __version__))
        sys.exit(0)

    jsondata = {'logs': []}

    # parse date interval
    if args.days:
        begin_day = _date.today() - relativedelta(days=args.days)
        begin = strdate_to_rfc2822(begin_day.strftime('%Y/%m/%d'), midnight=True)
        end_day = _date.today() - relativedelta(days=1)
        end = strdate_to_rfc2822(end_day.strftime('%Y/%m/%d'))
    else:
        begin = strdate_to_rfc2822(args.begin, midnight=True)
        end = strdate_to_rfc2822(args.end, now=False) if args.end else None

    if begin:
        jsondata['begin'] = begin
    if end:
        jsondata['end'] = end

    # parse api domain and credentials
    try:
        if args.domain:
          domain = args.domain
        else:
          domain = os.environ['MAILGUN_DOMAIN']
        jsondata['domain'] = domain
    except KeyError:
        print('Missing mailgun API key')
        sys.exit(1)

    try:
        if args.api_key:
            api_key = args.api_key
        else:
            api_key = os.environ['MAILGUN_API_KEY']
    except KeyError:
        print('Missing mailgun API key')
        sys.exit(1)

    if args.verbose:
        print('# BEGIN DATE: {}'.format(begin), file=sys.stderr)
        print('# END DATE: {}'.format(end), file=sys.stderr)
        sys.stderr.flush()

    if args.filename:
        # Check for existing file
        if os.path.exists(args.filename):
            while True:
                answer = raw_input("File %s exists! Proceed? You will lost data in the file!: " % file)
                if answer.lower() == 'yes':
                    break
                elif answer.lower() == 'no':
                    sys.exit('Interrupted by user')
        # Open file for write
        try:
            logfile = open(args.filename, 'w')
        except IOError:
            print('Can not open file to write')

    # Main loop
    time_start = datetime.now().replace(microsecond=0)
    for log in logs(domain=domain, api_key=api_key, begin=begin, end=end, max_retries=args.max_retries, verbose=args.verbose):
        if args.json:
            line_json = json.dumps(log, indent=3)

            if args.filename:
                try:
                    logfile.write(line_json + '\n')
                except IOError:
                    print('Can not write log to file ' + args.filename)
                    logfile.close()

            if args.logstashurl:
                try:
                    requests.put(args.logstashurl, data=line_json, headers={'Content-Type': 'application/json'})
                except IOError:
                    print('Can put log to ' + args.logstashurl)

        else:
            status = log.get('event', '').upper()
            ok = status in ['ACCEPTED', 'DELIVERED']
            line = '[%s] %s <%s>' % (datetime.utcfromtimestamp(log.get('timestamp', '')), status , log.get('recipient', ''))
            if not ok:
                line += ' (%s)' % (log.get('delivery-status', {}).get('description', '') or log.get('delivery-status', {}).get('message', ''))
            line += ': %s' % log.get('message', {}).get('headers', {}).get('subject', '')
            logfile.write(line + '\n')

    if args.filename:
        logfile.close()

    if args.verbose:
        # Getting stop time and execution time
        time_stop = datetime.now().replace(microsecond=0)
        time_exec = time_stop - time_start

        # Summary output
        summary = """
        Start time: %s
        End time: %s
        Total execution time: %s
        """ % (time_start, time_stop, time_exec)
        print('\n' + '!' * 79 + '\n' + summary + '!' * 79 + '\n')

if __name__ == '__main__':
    main()
