import _ = require('lodash')
import * as path from 'path'
import * as rp from 'request-promise'
import * as fastify from 'fastify'
import { requiredir } from '../common'
import { Log } from '../log'
import { config } from '../service';

let logger: Log
const api = {}
const fast = fastify({ trustProxy: true })
let targetPort = 0

export function init(cfg: any, callback?: Function) {
    logger = new Log(`msbase_fastify`)

    if (cfg.targetPort) targetPort = cfg.targetPort

    const start = () => {
        if (!cfg.port) return

        try {
            _.forEach(cfg.apipath ? cfg.apipath : ['./build/services/'], requiredir)
        } catch (error) { logger.log(error.message); }
        logger.log('api initialized');

        // 路由 及 熔断   
        let circuitBreakerOpt = { threshold: 3, timeout: 3000, resetTimeout: 3000 }
        if (cfg.circuitBreaker) circuitBreakerOpt = { ...circuitBreakerOpt, ...cfg.circuitBreaker }
        fast.register(require('fastify-circuit-breaker'), circuitBreakerOpt)
        // 压缩
        fast.register(require('fastify-compress'), { global: false });

        try {
            const route = cfg.routefile ? require(path.resolve(cfg.routefile)) : null;
            if (route) route(fast)
        } catch (error) { logger.log(error.message); }
        _.keys(api).length > 0 && fast.register(function (instance: any, opts, next) {
            instance.route({
                method: 'POST',
                url: '/:cmd/',
                // beforeHandler: instance.circuitBreaker(),
                handler: async (request, reply) => {
                    let payload: any = {}

                    const start = Date.now()
                    const name = request.params.cmd.toLowerCase()
                    const bodyJson = JSON.stringify(request.body)

                    logger.debug(`1-api-${name} args-${bodyJson}`, { method_name: name })
                    try {
                        const action = api[name]
                        if (!action) throw new Error(`${name} does not exist in ${cfg.name}`)
                        payload.result = await action(request.body);
                    } catch (error) {
                        payload.error = error.message
                    }

                    const hs = Date.now() - start
                    let spend_time = Math.floor(hs / 50)
                    if (spend_time > 99) spend_time = 99
                    logger.debug(`2-api-${name} args-${bodyJson} result-${JSON.stringify(payload)} 耗时-${hs}`, { method_name: name, spend_time: spend_time })

                    // reply.send(payload)
                    reply.compress(payload)
                }
            })
            console.log('start ms api route')
            next()
        });

        fast.listen(cfg.port, '0.0.0.0', (err, address) => {
            if (err) throw err
            logger.info(`server listening on ${address}`)
        })
    }

    callback ? callback(start) : start()
}

interface Pattern {
    pubsub$?: boolean
    timeout$?: number
    maxMessages$?: number
    expectedMessages$?: number
    [key: string]: any
}

export function add(args: Pattern, callback: Function) {
    api[args.cmd] = callback
    console.log(`api '${args.cmd}' success`)
}

export async function act(args: Pattern, isThrowError = false) {
    if (!args.topic || !args.cmd) throw new Error('topic or cmd is null')
    if (args.topic) args.topic = args.topic.toLowerCase()
    if (args.cmd) args.cmd = args.cmd.toLowerCase()

    const args_obj = _.pick(args, ['data', 'context']);
    const body = JSON.stringify(args_obj)

    if (args.topic === config.name) return api[args.cmd]({ ...args_obj });
    const url = `http://service-${args.topic}${targetPort ? (':' + targetPort) : ''}/${args.cmd}/`

    const start = Date.now()
    let ret: any = await rp.post(url, { body, gzip: true, forever: true, headers: { 'Content-Type': 'application/json' } })
        .then(body => JSON.parse(body))
        .catch(e => { return { error: e.message } })

    const hs = Date.now() - start
    let spend_time = Math.floor(hs / 50)
    if (spend_time > 99) spend_time = 99

    const msg = `act--${url}|${body}|${JSON.stringify(ret)}|${hs}`
    const opts = { method_name: args.cmd, spend_time: spend_time }
    logger.debug(msg, opts)

    if (ret) {
        if ('error' in ret) {
            logger.error(msg, opts)
            if (isThrowError) throw new Error(ret.error)
        }

        if ('result' in ret) {
            ret = ret.result
        }
    }

    return ret;
}