import json
import os
import sys
from context import EasydbException, EasydbError, UserError, get_json_value
sys.path.append(os.path.abspath(os.path.dirname(__file__)) + '/../../easydb-library/src/python')
import noderunner
import hashlib
import hmac


def easydb_server_start(easydb_context):
    logger = easydb_context.get_logger('webhook-plugin')
    logger.debug('Starting Webhook plugin.')

    easydb_context.register_callback('api', {
        'name': 'webhook',
        'callback': 'api_webhook',
    })


def dump(obj):
    return json.dumps(obj,
        sort_keys=True,
        indent=4
    )


def error_response(error, detail, status_code=500):
    return {
        'headers': {
            'Content-Type': 'application/json; charset: utf-8'
        },
        'status_code': status_code,
        'body': '%s' % dump({
            "code": error,
            "parameters": {
                "detail": detail
            }
        })
    }


def api_webhook(easydb_context, parameters):

    logger = easydb_context.get_logger('webhook-plugin')
    config = easydb_context.get_config()

    path = parameters['path'].split('/')

    if len(path) != 3:
        raise UserError('webhook.user.error.path_not_found')

    plugin = easydb_context.get_plugin(path[1])
    if plugin is None:
        raise UserError('webhook.user.error.plugin_not_found')

    if 'webhooks' not in plugin:
        raise UserError('webhook.user.error.webhook_not_found')

    webhook = None
    for _webhook in plugin['webhooks']:
        if not 'name' in _webhook:
            raise UserError('webhook.user.error.webhook_name_missing')

        if _webhook['name'] == path[2]:
            webhook = _webhook
            break

    if webhook is None:
        raise UserError('webhook.user.error.webhook_not_found')

    # check if the called webhook has a secret configured
    # if there is a body, check the signature of the request and validate the body
    secret = None

    body = get_json_value(parameters, 'body')
    if body is not None and len(body) > 0:

        secret_base_config = 'base.system.%s' % get_json_value(webhook, 'secret_base_config')
        logger.debug('request has body, check secret from base_config at \'%s\'' % secret_base_config)
        if secret_base_config is not None and len(secret_base_config) > 0:
            secret = get_json_value(config, secret_base_config)
            logger.debug('read secret from base config')

        if secret is not None and len(secret) > 0:
            SIGN_KEY = 'X-Hub-Signature'

            # if the header does not provide a signature, dont allow access
            if not 'headers' in parameters or not SIGN_KEY in parameters['headers']:
                logger.debug('%s not found in parameters.headers' % SIGN_KEY)
                return error_response('webhook.user.error.invalid_signature', {}, 403)

            signature = parameters['headers'][SIGN_KEY]
            h = hmac.new(secret.encode('utf-8'), body.encode('utf-8'), hashlib.sha1).hexdigest()
            if ('sha1=' + h) != signature:
                logger.debug('signature \'%s\' does not match \"sha1=%s\"' % (signature, h))
                return error_response('webhook.user.error.invalid_signature', {}, 403)

            logger.debug('signature \'%s\' verified' % (signature))

        else:
            logger.debug('no secret set in base config, skip check')

    script = '%s/build/webhooks/%s.js' % (plugin['base_path'], webhook['name'])

    info = {
        'request': parameters,
        'config': config,
        'plugin': plugin
    }

    out, err, exit_code = noderunner.call(
        config,
        script,
        json.dumps(info),
        [plugin['base_path'] + '/node_modules'] if plugin is not None and 'base_path' in plugin else [],
        logger
    )

    if exit_code != 0:
        error_name = 'webhook.user.error.exec_error'
        return error_response(error_name, err)

    if out == u'':
        error_name = 'webhook.user.error.empty_response'
        logger.error(error_name)
        return error_response(error_name, 'empty reponse from noderunner')

    try:
        result = json.loads(out)
        if isinstance(result, dict):
            return result
        else:
            error_name = 'webhook.user.error.parsing_error'
            error_detail = 'could not parse a json object from reponse'
            logger.error('%s: %s' % (error_name, error_detail))
            return error_response(error_name, error_detail)

    except Exception as e:
        error_name = 'webhook.user.error.parsing_error'
        logger.error('%s: %s' % (error_name, e.message))
        return error_response(error_name, e.message)
