#! /usr/bin/python
# -*- coding: utf-8 -*-

import os
import sys
import argparse

# Either we are in a development environment or not
IN_DEVEL = False

# Default server configuration
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 6787

# Level for which loggers will log
LOGGERS_LEVEL = 'DEBUG'
API_LOGGER_LEVEL = 'INFO'

# Handlers that will be used by loggers
#  - file: log to the file LOG_DIR/LOG_FILE
#  - api: serve logs through the api
#  - console: log to stderr
LOGGERS_HANDLERS = ['file', 'api']

# Directory and file to be used by logging
LOG_DIR = '/var/log/yunohost'
LOG_FILE = 'yunohost-api.log'

# Check and load - as needed - development environment
if not __file__.startswith('/usr/'):
    IN_DEVEL = True
if IN_DEVEL:
    basedir = os.path.abspath('%s/../' % os.path.dirname(__file__))
    if os.path.isdir(os.path.join(basedir, 'moulinette')):
        sys.path.insert(0, basedir)
    LOG_DIR = os.path.join(basedir, 'log')


import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize


# Initialization & helpers functions -----------------------------------

def _die(message, title='Error:'):
    """Print error message and exit"""
    print('%s %s' % (colorize(title, 'red'), message))
    sys.exit(1)

def _parse_api_args():
    """Parse main arguments for the api"""
    parser = argparse.ArgumentParser(add_help=False,
        description="Run the YunoHost API to manage your server.",
    )
    srv_group = parser.add_argument_group('server configuration')
    srv_group.add_argument('-h', '--host',
        action='store', default=DEFAULT_HOST,
        help="Host to listen on (default: %s)" % DEFAULT_HOST,
    )
    srv_group.add_argument('-p', '--port',
        action='store', default=DEFAULT_PORT, type=int,
        help="Port to listen on (default: %d)" % DEFAULT_PORT,
    )
    srv_group.add_argument('--no-websocket',
        action='store_true', default=True, dest='use_websocket',
        help="Serve without WebSocket support, used to handle "
        "asynchronous responses such as the messages",
    )
    glob_group = parser.add_argument_group('global arguments')
    glob_group.add_argument('--no-cache',
        action='store_false', default=True, dest='use_cache',
        help="Don't use actions map cache",
    )
    glob_group.add_argument('--debug',
        action='store_true', default=False,
        help="Set log level to DEBUG",
    )
    glob_group.add_argument('--verbose',
        action='store_true', default=False,
        help="Be verbose in the output",
    )
    glob_group.add_argument('--help',
        action='help', help="Show this help message and exit",
    )

    return parser.parse_args()

def _init_moulinette(use_websocket=True, debug=False, verbose=False):
    """Configure logging and initialize the moulinette"""
    # Define loggers handlers
    handlers = set(LOGGERS_HANDLERS)
    if not use_websocket and 'api' in handlers:
        handlers.remove('api')
    if verbose and 'console' not in handlers:
        handlers.add('console')
    root_handlers = handlers - set(['api'])

    # Define loggers level
    level = LOGGERS_LEVEL
    api_level = API_LOGGER_LEVEL
    if debug:
        level = 'DEBUG'
        api_level = 'DEBUG'

    # Custom logging configuration
    logging = {
        'version': 1,
        'disable_existing_loggers': True,
        'formatters': {
            'console': {
                'format': '%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
            },
            'precise': {
                'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
            },
        },
        'filters': {
            'action': {
                '()': 'moulinette.utils.log.ActionFilter',
            },
        },
        'handlers': {
            'api': {
                'level': api_level,
                'class': 'moulinette.interfaces.api.APIQueueHandler',
            },
            'file': {
                'class': 'logging.handlers.WatchedFileHandler',
                'formatter': 'precise',
                'filename': '%s/%s' % (LOG_DIR, LOG_FILE),
                'filters': ['action'],
            },
            'console': {
                'class': 'logging.StreamHandler',
                'formatter': 'console',
                'stream': 'ext://sys.stdout',
                'filters': ['action'],
            },
        },
        'loggers': {
            'yunohost': {
                'level': level,
                'handlers': handlers,
                'propagate': False,
            },
            'moulinette': {
                'level': level,
                'handlers': [],
                'propagate': True,
            },
            'gnupg': {
                'level': 'INFO',
                'handlers': [],
                'propagate': False,
            },
        },
        'root': {
            'level': level,
            'handlers': root_handlers,
        },
    }

    # Create log directory
    if not os.path.isdir(LOG_DIR):
        try:
            os.makedirs(LOG_DIR, 0750)
        except os.error as e:
            _die(str(e))

    # Initialize moulinette
    moulinette.init(logging_config=logging, _from_source=IN_DEVEL)

def _retrieve_namespaces():
    """Return the list of namespaces to load"""
    ret = ['yunohost']
    for n in ActionsMap.get_namespaces():
        # Append YunoHost modules
        if n.startswith('ynh_'):
            ret.append(n)
    return ret


# Callbacks for additional routes --------------------------------------

def is_installed():
    """
    Check whether YunoHost is installed or not

    """
    installed = False
    if os.path.isfile('/etc/yunohost/installed'):
        installed = True
    return { 'installed': installed }


# Main action ----------------------------------------------------------

if __name__ == '__main__':
    opts = _parse_api_args()
    _init_moulinette(opts.use_websocket, opts.debug, opts.verbose)

    # Run the server
    ret = moulinette.api(
        _retrieve_namespaces(),
        host=opts.host, port=opts.port, routes={
            ('GET', '/installed'): is_installed,
        }, use_cache=opts.use_cache, use_websocket=opts.use_websocket
    )
    sys.exit(ret)