import os
import flask
import logging
from flask_login import LoginManager

from datacloud.logging.middleware import LogReqIdMiddleware
from datacloud.logging.logging_config import configure_logging, LogLevel, ConsoleStream, get_qloud_formatter

from datacloud.dev_utils.ydb.lib.core.utils import YdbPathConfig
from datacloud.dev_utils.ydb.lib.core.ydb_manager import YdbManager, YdbConnectionParams
from datacloud.score_api.storage.score_path.ydb.storage import YdbScorePathStorage
from datacloud.score_api.storage.stability.ydb.storage import YdbStabilityStorage
from datacloud.score_api.storage.geo.ydb.storage import YdbGeoStorage
from datacloud.score_api.storage.rejected.ydb.storage import YdbRejectedRequestsStorage

from datacloud.score_api.storage.cookie_sync.optimistic import OptimisticCookieSyncSet
from datacloud.score_api.storage.cookie_sync.cookie_sync_api import CookieSyncApi


logger = logging.getLogger(__name__)

# # # ydb logging
try:
    logger = logging.getLogger('ydb.pool.Discovery')
    logger.setLevel(logging.WARN)
    logger.addHandler(logging.StreamHandler())
except Exception as ex:
    logger.warn('Failed to setup ydb logging %s', ex)
# # #


def register_ydb_score_api(app, ydb_manager, connection_params, path_config, url_prefix):
    from .blueprints import get_ydb_score_api, get_ydb_stability_api, get_ydb_reject_api, get_geo_score_api

    score_path_storage = YdbScorePathStorage(
        ydb_manager,
        connection_params.database,
        path_config.partner_scores_table_path,
        path_config.score_path_table_path
    )

    stability_storage = YdbStabilityStorage(
        ydb_manager,
        connection_params.database,
        os.path.join(path_config.root_dir, 'stability/stability')
    )

    reject_storage = YdbRejectedRequestsStorage(
        ydb_manager,
        connection_params.database,
        os.path.join(path_config.root_dir, 'rejected/rejected_requests')
    )

    geo_linear_path_storage = YdbScorePathStorage(
        ydb_manager,
        connection_params.database,
        path_config.geo_linear_parts_table_path,
        path_config.score_path_table_path
    )
    geo_storage = YdbGeoStorage(
        ydb_manager,
        connection_params.database,
        path_config.geo_path_table_path,
        path_config.geo_root_path
    )

    cookie_sync_api = CookieSyncApi(cookie_sync_set=OptimisticCookieSyncSet())

    score_api = get_ydb_score_api(ydb_manager, connection_params, path_config, score_path_storage, cookie_sync_api)
    stability = get_ydb_stability_api(ydb_manager, connection_params, path_config, stability_storage)
    reject_api = get_ydb_reject_api(ydb_manager, connection_params, path_config, reject_storage)
    geo_api = get_geo_score_api(ydb_manager, connection_params, path_config, geo_linear_path_storage, geo_storage, cookie_sync_api)

    app.register_blueprint(score_api, url_prefix=url_prefix)
    app.register_blueprint(stability, url_prefix=url_prefix)
    app.register_blueprint(reject_api, url_prefix=url_prefix)
    app.register_blueprint(geo_api, url_prefix=url_prefix)
    return app


def get_ydb_app(ydb_manager, connection_params, path_config):
    from datacloud.score_api.storage.users.ydb.storage import YdbUserStorage
    from .auth import UserLoader, unauthorized_handler

    app = flask.Flask(
        'datacloud.score_api',
        static_folder=None,
        template_folder=None,
    )
    app.config['JSON_AS_ASCII'] = False

    user_storage = YdbUserStorage(ydb_manager, connection_params.database, path_config.partner_tokens_table_path)
    user_loader = UserLoader(user_storage=user_storage)

    login_manager = LoginManager()
    login_manager.session_protection = 'strong'
    login_manager.init_app(app)
    login_manager.request_loader(user_loader)
    login_manager.unauthorized_handler(unauthorized_handler)

    app.wsgi_app = LogReqIdMiddleware(app.wsgi_app)
    return app


def is_runned_under_uwsgi():
    """ Checks is application runned under uwsgi """
    try:
        import uwsgi  # noqa
    except ImportError:
        return False
    return True


class Option(object):
    def __init__(self, name, default=None, type=None, env=None, help=None):
        self.name = name
        self.default = default
        self.env = env
        self.help = help
        self.type = type or str


startup_options = [
    Option('host', env='XPROD_HOST', default='::1',
           help='address to work on'),
    Option('port', env='XPROD_HOST', default='8008', type=int,
           help='port to work on'),
    Option('yt_cluster', env='XPROD_YT_CLUSTER',
           help='YT cluster'),
    Option('yt_root', env='XPROD_YT_ROOT', default='//home/x-products/testing/master',
           help='YT root directory path'),
    Option('debug', env='XPROD_DEBUG', default=False,
           help='debug'),
    Option('log_level', env='LOG_LEVEL', default='INFO',
           help='change default log level (use DEBUG, INFO, WARNING, etc)'),
    Option('log_format', env='QLOUD_LOGGER_STDOUT_PARSER', default='line',
           help='log format (line or json)'),
    Option('user_tokens', env='DATACLOUD_USER_TOKENS',
           help='comma-separated pairs token:login'),
    Option('vendors_with_sync', env='VENDORS_WITH_SYNC', default='',
           help='comma-separated list of cookie_vendor that supports cookie-sync'),
    Option('depricated_scores', env='DEPRICATED_SCORES',
           help='comma-separated list of depriacated scores from CryptaV1, that will be disables in future.',
           default=''),
    Option('ydb_token', env='YDB_TOKEN',
           help='token to access YanexDataBase',
           default=''),
    Option('ydb_endpoint', env='YDB_ENDPOINT',
           help='endpoint to YanexDataBase',
           default='ydb-ru-prestable.yandex.net:2135'),
    Option('ydb_database', env='YDB_DATABASE',
           help='Database path for YandexDatabBase',
           default='/ru-prestable/impulse/test/xprod-scoring-api-db'),
    Option('ydb_root_dir', env='YDB_ROOT_DIR',
           help='Root dir for YandexDataBase',
           default='sandbox_score_api'),
    Option('tvm_client_id', env='TVM_CLIENT_ID', help='TVM Client Id', default=''),
    Option('tvm_ydb_id', env='TVM_YDB_ID', help='TVM YDB ID', default=''),
    Option('tvm_secret', env='TVM_SECRET', help='TVM Secret', default='')
]


class EnvOptions(object):
    """ Reads options from OS environment variables """

    def __init__(self, options):
        super(EnvOptions, self).__init__()
        environ = os.environ

        for opt in options:
            if opt.env in environ:
                value = opt.type(environ[opt.env])
            else:
                value = opt.default
            setattr(self, opt.name, value)


def ydb_main():
    opts = EnvOptions(startup_options)

    log_format = get_qloud_formatter(opts.log_format)
    configure_logging(
        log_level=LogLevel(opts.log_level),
        console_formatter=log_format,
        console_stream=ConsoleStream.STDOUT,
        reconfigure_yt=True,
    )

    path_config = YdbPathConfig(opts.ydb_root_dir)

    connection_params = YdbConnectionParams(
        endpoint=opts.ydb_endpoint,
        database=opts.ydb_database,
        auth_token=opts.ydb_token,
        tvm_client_id=opts.tvm_client_id,
        tvm_secret=opts.tvm_secret,
        tvm_ydb_id=opts.tvm_ydb_id,
    )
    ydb_manager = YdbManager(connection_params)

    app = get_ydb_app(ydb_manager, connection_params, path_config)
    register_ydb_score_api(app, ydb_manager, connection_params, path_config, url_prefix='/v1')

    if is_runned_under_uwsgi():
        # just returns configured app to uWSGI process
        return app

    # For gunicorn
    return app

    # port = int(opts.port)
    # logger.info('listening [%s]:%s', opts.host, port)

    # opts.debug = True
    # if opts.debug:
    #     logger.info('Debug?')
    #     app.run(debug=True, host=opts.host, port=port, passthrough_errors=True)
    # else:
    #     logger.info('Or not?')
    #     from gevent.wsgi import WSGIServer
    #     http_server = WSGIServer((opts.host, port), app)
    #     http_server.serve_forever()


uwsgi_app = None
if is_runned_under_uwsgi():
    uwsgi_app = ydb_main()


if __name__ == '__main__':
    application = ydb_main()

# --disable-logging
# uwsgi --plugin python --http-socket [::]:8008 --manage-script-name --module datacloud.score_api.app --callable uwsgi_app --master --workers 1

# python test_run_app.py
