#!/usr/bin/python
# -*- coding: utf-8 -*-
#    Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
"""
Senpy is a modular sentiment analysis server. This script runs an instance of
the server.

"""

from flask import Flask
from senpy.extensions import Senpy
from senpy.utils import easy_test
from senpy.plugins import list_dependencies
from senpy import config

import logging
import os
import sys
import argparse
import senpy

SERVER_PORT = os.environ.get("PORT", 5000)


def main():
    parser = argparse.ArgumentParser(description='Run a Senpy server')
    parser.add_argument(
        '--level',
        '-l',
        metavar='logging_level',
        type=str,
        default="INFO",
        help='Logging level')
    parser.add_argument(
        '--no-proxy-fix',
        action='store_true',
        default=False,
        help='Do not assume senpy will be running behind a proxy (e.g., nginx)')
    parser.add_argument(
        '--log-format',
        metavar='log_format',
        type=str,
        default='%(asctime)s %(levelname)-10s %(name)-30s \t %(message)s',
        help='Logging format')
    parser.add_argument(
        '--debug',
        '-d',
        action='store_true',
        default=False,
        help='Run the application in debug mode')
    parser.add_argument(
        '--no-default-plugins',
        action='store_true',
        default=False,
        help='Do not load the default plugins')
    parser.add_argument(
        '--host',
        type=str,
        default="0.0.0.0",
        help='Use 0.0.0.0 to accept requests from any host.')
    parser.add_argument(
        '--port',
        '-p',
        type=int,
        default=SERVER_PORT,
        help='Port to listen on.')
    parser.add_argument(
        '--plugins-folder',
        '-f',
        type=str,
        action='append',
        help='Where to look for plugins.')
    parser.add_argument(
        '--install',
        '-i',
        action='store_true',
        default=False,
        help='Install plugin dependencies before running.')
    parser.add_argument(
        '--dependencies',
        action='store_true',
        default=False,
        help='List plugin dependencies')
    parser.add_argument(
        '--strict',
        action='store_true',
        default=config.strict,
        help='Fail if optional plugins cannot be loaded.')
    parser.add_argument(
        '--test',
        '-t',
        action='store_true',
        default=False,
        help='Test all plugins before launching the server')
    parser.add_argument(
        '--no-run',
        action='store_true',
        default=False,
        help='Do not launch the server.')
    parser.add_argument(
        '--data-folder',
        '--data',
        type=str,
        default=None,
        help='Where to look for data. It be set with the SENPY_DATA environment variable as well.')
    parser.add_argument(
        '--no-threaded',
        action='store_true',
        default=False,
        help='Run a single-threaded server')
    parser.add_argument(
        '--no-deps',
        '-n',
        action='store_true',
        default=False,
        help='Skip installing dependencies')
    parser.add_argument(
        '--version',
        '-v',
        action='store_true',
        default=False,
        help='Output the senpy version and exit')
    parser.add_argument(
        '--allow-fail',
        '--fail',
        action='store_true',
        default=False,
        help='Do not exit if some plugins fail to activate')
    parser.add_argument(
        '--enable-cors',
        '--cors',
        action='store_true',
        default=False,
        help='Enable CORS for all domains (requires flask-cors to be installed)')
    args = parser.parse_args()
    print('Senpy version {}'.format(senpy.__version__))
    print(sys.version)
    if args.version:
        exit(1)
    rl = logging.getLogger()
    rl.setLevel(getattr(logging, args.level))
    logger_handler = rl.handlers[0]

    # First, generic formatter:
    logger_handler.setFormatter(logging.Formatter(args.log_format))

    app = Flask(__name__)
    app.debug = args.debug

    sp = Senpy(app,
               plugin_folder=None,
               default_plugins=not args.no_default_plugins,
               install=args.install,
               strict=args.strict,
               data_folder=args.data_folder)
    folders = list(args.plugins_folder) if args.plugins_folder else []
    if not folders:
        folders.append(".")
    for p in folders:
        sp.add_folder(p)

    plugins = sp.plugins(plugin_type=None, is_activated=False)
    maxname = max(len(x.name) for x in plugins)
    maxversion = max(len(str(x.version)) for x in plugins)
    print('Found {} plugins:'.format(len(plugins)))
    for plugin in plugins:
        import inspect
        fpath = inspect.getfile(plugin.__class__)
        print('\t{: <{maxname}} @ {: <{maxversion}} -> {}'.format(plugin.name,
                                                                  plugin.version,
                                                                  fpath,
                                                                  maxname=maxname,
                                                                  maxversion=maxversion))
    if args.dependencies:
        print('Listing dependencies')
        missing = []
        installed = []
        for plug in sp.plugins(is_activated=False):
            inst, miss, nltkres = list_dependencies(plug)
            if not any([inst, miss, nltkres]):
                continue
            print(f'Plugin: {plug.id}')
            for m in miss:
                missing.append(f'{m} # {plug.id}')
            for i in inst:
                installed.append(f'{i} # {plug.id}')
        if installed:
            print('Installed packages:')
            for i in installed:
                print(f'\t{i}')
        if missing:
            print('Missing packages:')
            for m in missing:
                print(f'\t{m}')

    if args.install:
        sp.install_deps()

    if args.test:
        sp.activate_all(sync=True)
        easy_test(sp.plugins(is_activated=True), debug=args.debug)

    if args.no_run:
        return

    sp.activate_all(sync=True)
    if sp.strict:
        inactive = sp.plugins(is_activated=False)
        assert not inactive

    print('Senpy version {}'.format(senpy.__version__))
    print('Server running on port %s:%d. Ctrl+C to quit' % (args.host,
                                                            args.port))
    if args.enable_cors:
        from flask_cors import CORS
        CORS(app)

    if not args.no_proxy_fix:
        from werkzeug.middleware.proxy_fix import ProxyFix
        app.wsgi_app = ProxyFix(app.wsgi_app)

    try:
        app.run(args.host,
                args.port,
                threaded=not args.no_threaded,
                debug=app.debug)
    except KeyboardInterrupt:
        print('Bye!')
    sp.deactivate_all()


if __name__ == '__main__':
    main()
