#!/usr/bin/python3 -s
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015-2016 Bitergia
#
# 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; either version 3 of the License, or
# (at your option) any later version.
#
# 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.
#
# Authors:
#     Santiago Dueñas <sduenas@bitergia.com>
#

import argparse
import configparser
import json
import logging
import os.path
import sys
import time

import redis

from grimoirelab.toolkit.datetime import str_to_datetime

from arthur.arthur import Arthur
from arthur.common import CH_PUBSUB, ARCHIVES_DEFAULT_PATH


ARTHUR_USAGE_MSG = \
"""%(prog)s [-c <file>] [-g] <repositories> | --help """

ARTHUR_DESC_MSG = \
"""King Arthur commands his loyal knight Perceval on the quest
to retrieve data from software repositories.

Repositories are given to the program in a JSON stream. This stream
is read from the standard input or from a file. Then, each repository
is transformed into a Perceval job, scheduling and running using
a distributed job queue platform.

required arguments:
  repositories          list of repositories

optional arguments:
  -h, --help            show this help message and exit
  -c FILE, --config FILE
                        set configuration file
  -g, --debug           set debug mode on
  -d, --database        URL database connection (default: 'redis://localhost/8')
  -s, --sync            work in synchronous mode (without workers)
  -o, --outfile         output filename
  --archive-path        path to archive manager directory
  --no-archive          do not archive fetched raw data
  --pubsub-channel      name of redis pubsub channel on which to publish updates
"""

# Logging formats
ARTHUR_LOG_FORMAT = "[%(asctime)s] - %(message)s"
ARTHUR_DEBUG_LOG_FORMAT = "[%(asctime)s - %(name)s - %(levelname)s] - %(message)s"


def main():
    args = parse_args()

    # Read default parameters from a configuration file
    if args.config_file:
        defaults = read_config_file(args.config_file)
    else:
        defaults = {}

    # Set archive manager directory
    base_archive_path = None if args.no_archive else args.archive_path

    outfile = args.outfile

    configure_logging(args.debug)

    logging.info("King Arthur is on command.")

    conn = connect_to_redis(args.database)

    arthur = Arthur(conn,
                    async_mode=args.sync_mode,
                    base_archive_path=base_archive_path,
                    pubsub_channel=args.pubsub_channel)

    repositories = json.load(args.repositories)

    logging.info("Reading repositories...")
    for repo in repositories['repositories']:
        from_date = repo['args'].get('from_date', None)
        if from_date:
            repo['args']['from_date'] = str_to_datetime(from_date)

        arthur.add(repo['origin'], repo['backend'], repo['args'])
    logging.info("Done. Ready to work!")

    while True:
        time.sleep(1)
        items = arthur.items()

        for item in items:
            obj = json.dumps(item, indent=4, sort_keys=True)
            outfile.write(obj)
            outfile.write('\n')

    logging.info("King Arthur completed his quest.")


def parse_args():
    """Parse command line arguments"""

    parser = argparse.ArgumentParser(usage=ARTHUR_USAGE_MSG,
                                     description=ARTHUR_DESC_MSG,
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     add_help=False)

    parser.add_argument('-h', '--help', action='help',
                       help=argparse.SUPPRESS)
    parser.add_argument('-c', '--config', dest='config_file',
                        default=os.path.expanduser('~/.arthur/arthur.cfg'),
                        help=argparse.SUPPRESS)
    parser.add_argument('-g', '--debug', dest='debug',
                        action='store_true',
                        help=argparse.SUPPRESS)
    parser.add_argument('-d', '--database', dest='database',
                        default='redis://localhost/8',
                        help=argparse.SUPPRESS)
    parser.add_argument('-s', '--sync', dest='sync_mode',
                        action='store_false',
                        help=argparse.SUPPRESS)
    parser.add_argument('-o', '--output', type=argparse.FileType('w'),
                        dest='outfile', default=sys.stdout,
                        help=argparse.SUPPRESS)
    parser.add_argument('--archive-path', dest='archive_path',
                        default=os.path.expanduser(ARCHIVES_DEFAULT_PATH),
                        help=argparse.SUPPRESS)
    parser.add_argument('--no-archive', dest='no_archive',
                        action='store_true',
                        help=argparse.SUPPRESS)
    parser.add_argument('repositories', nargs='?', type=argparse.FileType('r'),
                        default=sys.stdin,
                        help=argparse.SUPPRESS)
    parser.add_argument('--pubsub-channel', dest='pubsub_channel',
                        default=CH_PUBSUB,
                        help=argparse.SUPPRESS)
    return parser.parse_args()


def read_config_file(filepath):
    """Read a Arthur configuration file.

    This function reads common configuration parameters
    from the given file.

    :param filepath: path to the configuration file

    :returns: a configuration parameters dictionary
    """
    config = configparser.SafeConfigParser()
    config.read(filepath)

    args = {}
    sections = ['arthur']

    for section in sections:
        if section in config.sections():
            d = dict(config.items(section))
            args.update(d)

    return args


def configure_logging(debug=False):
    """Configure Arthur logging

    The function configures the log messages produced by Arthur
    and Perceval backends. By default, log messages are sent
    to stderr. Set the parameter `debug` to activate the debug
    mode.

    :param debug: set the debug mode
    """
    if not debug:
        logging.basicConfig(level=logging.INFO,
                            format=ARTHUR_LOG_FORMAT)
        logging.getLogger('requests').setLevel(logging.WARNING)
        logging.getLogger('urrlib3').setLevel(logging.WARNING)
    else:
        logging.basicConfig(level=logging.DEBUG,
                            format=ARTHUR_DEBUG_LOG_FORMAT)

def connect_to_redis(db_url):
    """Create a connection with a Redis database"""

    conn = redis.StrictRedis.from_url(db_url)
    logging.debug("Redis connection stablished with %s.", db_url)

    return conn


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        s = "\n\nReceived Ctrl-C or other break signal. Exiting.\n"
        sys.stderr.write(s)
        sys.exit(0)
