/* globals GLOBAL_OBJECT, APP_JSON, APPID */

// 是否是内部小程序
export let isInternalSmallApp = true;

export let launchOptions = {};

export let appJSON = {};

// callbackID 自增
let uid = 0;

export const getUid = () => ++uid;


// 是否打印内部log
let isDebug = false;
if (typeof Service !== 'undefined') {
    Service.getUid = getUid;
    try {
        isDebug = APP_JSON.debug || false;
    } catch (error) {
        console.error(error);
    }
}

export let showSystemLog = isDebug;

export const setShowSystemLog = (show) => {
    showSystemLog = !!show;
};

export const emptyFn = () => {};

/**
 * 检查option是否为targetType类型
 * @param {Any} option 被检查的属性
 * @param {String} targetType 目标类型
 *
 * @returns {String | Null} 若符合返回null，否则返回option的类型
 */
export const typeCheck = (option, targetType) => {
    const dataType = Object.prototype.toString.call(option).match(/\[Object (.+?)\]/i);

    if (dataType[1] !== targetType) {
        return dataType[1];
    }

    return null;
};

export const throwError = (error) => {
    if (typeof Service !== 'undefined') {
        Service.onError(error, true);
    }

    console.error(error);
};

/**
 * 异步接口通用方法
 * @param {String} api callNative的api
 * @param {Object} options 入参
 * @param {Object} types 类型检查 key是属性名，value是目标类型
 * @param {String} apiMsg 回调的errMsg中的api名
 * @param {Object} pretreatment 对一些参数的预处理
 */
export const callNative = (api, options, types = {}, apiMsg = api, pretreatment = {}) => {
    showSystemLog && console.log('------ callNative ------', api, options);
    const callbackId = ++uid;
    const successFn = typeof options.success === 'function' ? options.success : emptyFn;
    const failFn = typeof options.fail === 'function' ? options.fail : emptyFn;
    const completeFn = typeof options.complete === 'function' ? options.complete : emptyFn;

    // 调用了internal api但不是内部小程序，则不让调用
    if ((api.indexOf('internal') === 0 || api.indexOf('_inner') === 0) && !isInternalSmallApp) {
        failFn({ errMsg: 'api not support' });
        return;
    }

    const callbackFn = (result) => {
        let _output = {}
        if (result.code === 0) {
            _output = Object.assign({ errMsg: `${apiMsg}:ok` }, Object.prototype.toString.call(result.data) === '[object Object]' ? result.data : { data: result.data })
            if (pretreatment.code) {
                _output.code = result.code;
            }

            successFn(_output);
            completeFn(_output);
        } else {
            let errInfo = result.msg;
            let output = {};

            if (pretreatment.resultFn && typeof pretreatment.resultFn === 'function') {
                let _tempValue = pretreatment.resultFn(result);
                if (_tempValue) errInfo = _tempValue;
            }

            output.errMsg = `${api}:fail ${errInfo}`;

            // 判断是否需要code透传。
            if (pretreatment.code) {
                output.code = result.code;
            }

            failFn(output);
            completeFn(output);
        }
    };

    for (let i = 0; i < Object.keys(types).length; i++) {
        const key = Object.keys(types)[i];
        const dataType = Object.prototype.toString.call(options[key]).match(/\[Object (.+?)\]/i);

        if (dataType[1] !== types[key]) {
            failFn({ errMsg: `${apiMsg}:fail parameter error: parameter.${key} should be ${types[key]} instead of ${dataType[1]};` });
            completeFn({ errMsg: `${apiMsg}:fail parameter error: parameter.${key} should be ${types[key]} instead of ${dataType[1]};` });
            return;
        }
    }

    if (pretreatment.requestFn && typeof pretreatment.requestFn === 'function') {
        pretreatment.requestFn(callbackId, options);
    }

    GLOBAL_OBJECT.callbacks[callbackId] = callbackFn;
    GLOBAL_OBJECT.callNative(api, JSON.stringify(options), callbackId);
};

/**
 * 同步接口通用方法
 * @param {String} api callNative的API
 * @param {Object} options 入参
 * @param {Object} types 类型检查 key是属性名，value是目标类型
 * @param {String} apiMsg 回调的errMsg中的api名
 */
export const callNativeSync = (api, options = {}, types = {}, apiMsg = api) => {
    showSystemLog && console.log('------ callNativeSync ------', api, options);
    let result;

    // 调用了internal api但不是内部小程序，则不让调用
    if ((api.indexOf('internal') === 0 || api.indexOf('_inner') === 0) && !isInternalSmallApp) {
        throwError('api not support');
        throw new Error('api not support')
    }

    for (let i = 0; i < Object.keys(types).length; i++) {
        const key = Object.keys(types)[i];
        const dataType = Object.prototype.toString.call(options[key]).match(/\[Object (.+?)\]/i);

        if (dataType[1] !== types[key]) {
            throwError(`${apiMsg}:fail parameter error: parameter.${key} should be ${types[key]} instead of ${dataType[1]};`);
        }
    }

    try {
        result = GLOBAL_OBJECT.callNative(api, JSON.stringify(options));
        result = JSON.parse(result);

        if (result.code !== 0) {
            // 不存在的api
            if (result.code === 101) {
                throwError(`unsupported method: ${api}`);
            } else {
                throwError(result.msg)
            }
        }
    } catch (e) {
        return {};
    }

    if (!result) {
        return {};
    }

    return result;
};

try {
    if (typeof Service !== 'undefined') {
        isInternalSmallApp = APPID.indexOf('bilinternal') === 0 || APPID.indexOf('biliwidget') === 0;
    }
} catch (error) {
    console.log(error);
}


export const serialize = (function() {
    var isMixed = function(obj) {
        return /\[object (Object)|(Array)\]/.test(
            Object.prototype.toString.call(obj)
        );
    };
    try {
        if (typeof Proxy !== 'undefined') {
            var handler = function(rootPath) {
                rootPath = rootPath || '@';
                var self = this;

                return {
                    get: function(target, key) {
                        var result = Reflect.get(target, key);
                        var descriptor = Object.getOwnPropertyDescriptor(
                            target,
                            key
                        );

                        if (isMixed(result)) {
                            var cacheKey = Array.isArray(target)
                                ? ''.concat(rootPath, '[').concat(key, ']')
                                : ''.concat(rootPath, '.').concat(key);
                            var filtered = self.filter(function(a) {
                                return a.value === result;
                            });

                            if (filtered.length) {
                                return '<Circular '.concat(
                                    filtered[0].key,
                                    '>'
                                );
                            }

                            self.push({ key: cacheKey, value: result });
                            return new Proxy(
                                result,
                                handler.call(self, cacheKey)
                            );
                        }

                        if (
                            typeof result === 'function' && descriptor &&
                            descriptor.enumerable
                        ) {
                            return '<Function>';
                        }

                        return result;
                    },
                };
            };

            return function(obj, space) {
                if (isMixed(obj)) {
                    var caches = [{ key: '@', value: obj }];
                    var proxy = new Proxy(obj, handler.call(caches));
                    return JSON.stringify(proxy, null, space);
                }

                return JSON.stringify(obj, null, space);
            };
        } else {
            // 兼容方案
            return function(object) {
                var trap = [];
                return (
                    JSON.stringify(object, function(key, value) {
                        if (isMixed(value)) {
                            if (
                                trap.reduce(function(prev, curr) {
                                    return curr.value === value || prev;
                                }, false)
                            ) {
                                return '<Circular>';
                            } else {
                                trap.push({ key: key, value: value });
                                return value;
                            }
                        } else if (typeof value === 'function') {
                            return '<Function>';
                        }
                        return value;
                    })
                );
            };
        }
    } catch (e) {

    }
})();

const MAPPING_BILI_THEME = {
    '-1': 'none',
    '0': 'pink',
    '1': 'white',
    '2': 'night',
    '3': 'red',
    '4': 'yellow',
    '5': 'green',
    '6': 'blue',
    '7': 'purple'
};
export const mapBiliTheme = function mapBiliTheme (biliThemeId) {
    return MAPPING_BILI_THEME[biliThemeId] || '';
};
