/* global sapprc */
import axios from 'axios';
import components from 'bili-small-app/components/index.web';
import NavigationBar from 'bili-small-app/dev/components/NavigationBar';
import parseQuery from 'bili-small-app/runtime/lib/parse-query';
import 'bili-small-app/runtime/lib/selectComponent';

// 先引入 app.web，再引入 app.js
import 'bili-small-app/build/app.web';
import '%/app.js';
import '%/app.less';
import appJson from '%/app.json';

const defaultRemRatio = 20;

if (appJson.setRem) {
    const remRatio = normalizeRemRatio(appJson.setRem);

    setRem(remRatio);
    window.onresize = function () {
        setRem(remRatio);
    };
}

// 设置 rem 函数
function setRem (remRatio = defaultRemRatio) {
    let htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;

    if (htmlWidth === 0) {
        setTimeout(() => {
            setRem(remRatio);
        }, 50);
        return;
    }

    let htmlDom = document.getElementsByTagName('html')[0];
    htmlDom.style.fontSize = htmlWidth / remRatio + 'px';
}

function normalizeRemRatio (remRatio) {
    if (typeof remRatio === 'boolean') {
        return defaultRemRatio;
    }

    if (Number.isNaN(+remRatio) || remRatio <= 0) {
        return defaultRemRatio;
    }

    return remRatio;
}

if (appJson.darkmode) {
    const darkConfig = appJson.darkConfig || {};
    mergeAppJson(appJson, darkConfig);
}

function mergeAppJson(main, ext) {
    if (typeof ext !== 'object') {
        return main;
    }

    Object.keys(main).forEach(k => {
        switch (k) {
        case 'pages':
            main[k] = mergePages(main[k], ext[k]);
            break;
        case 'window':
            main[k] = mergeWindow(main[k], ext[k]);
            break;
        case 'tabBar':
            main[k] = mergeTabBar(main[k], ext[k]);
            break;
        }
    });
}

function mergePages(main, ext) {
    const keyToMerge = ['navigationBarBackgroundColor', 'navigationBarTextStyle', 'backgroundColor'];

    if (!Array.isArray(ext)) {
        return main;
    }

    ext.forEach(p => {
        const thePage = main.find(t => t.path === p.path);

        if (thePage) {
            keyToMerge.forEach(colorKey => {
                thePage[colorKey] = p[colorKey] || thePage[colorKey];
            });
        }
    });

    return main;
}

function mergeWindow(main, ext) {
    const keyToMerge = ['navigationBarBackgroundColor', 'navigationBarTextStyle', 'backgroundColor'];

    if (typeof ext !== 'object') {
        return main;
    }

    keyToMerge.forEach(colorKey => {
        ext[colorKey] && (main[colorKey] = ext[colorKey]);
    });

    return main;
}

function mergeTabBar(main, ext) {
    const keyToMerge = ['color', 'selectedColor', 'backgroundColor', 'borderStyle'];

    if (typeof ext !== 'object') {
        return main;
    }

    keyToMerge.forEach(colorKey => {
        (ext[colorKey] !== undefined) && (main[colorKey] = ext[colorKey]);
    });

    return main;
}

if (appJson.tabBar) {
    appJson.tabBarMap = {};
    appJson.tabBar.list.forEach(ele => appJson.tabBarMap[ele.pagePath.split('?')[0]] = ele);
    appJson.tabBar.redDotMap = {};
    appJson.tabBar.badgeMap = {};
    appJson.tabBar.visible = true;
    appJson.tabBar.instance = null;
}

if (appJson.pages) {
    appJson.pagesMap = {};

    appJson.pages.forEach(ele => {
        appJson.pagesMap[ele.path.split('?')[0]] = ele;
    });
}

if (appJson.navigateToMiniProgramAppIdList) {
    appJson.navigateToMap = {};
    appJson.navigateToMiniProgramAppIdList.forEach(ele => appJson.navigateToMap[ele.appId] = ele);
}

const pathArr = location.pathname.split('/');
const currentPath = '/pages/' + (pathArr[pathArr.length - 1].replace(/\.html/, '') || 'index');

window.__blConfig = appJson || {};
window.__blConfig.data = {};
window.__blConfig.env = process.env;
window.__devtoolsConfig = {
    appId: sapprc.appId,
    vAppId: sapprc.vAppId || '',
    envVersion: sapprc.envVersion || '',
    type: sapprc.appId.indexOf('bilinternal') === 0 ? 2 : 0 //0，小程序，1，小游戏，2，内部小程序
};
window.__blConfig = Object.assign(window.__blConfig, window.__devtoolsConfig, { currentPath });

const darkmode = !!appJson.darkmode;
const pageBackgroundColor = darkmode ? '#282828' : '#FFFFFF';

document.body.style.backgroundColor = pageBackgroundColor;

const PREFIX = window.__blConfig.env === 'production' ? '//miniapp.bilibili.com' : '';

// for bl.request
axios.get(`${PREFIX}/miniapp-b/miniapp/shop/url/info?appId=${window.__blConfig.appId}`).then(response => {
    if (response.data.code == 0) {
        const data = response.data;
        window.__blConfig = Object.assign(window.__blConfig, data);
    }
});
// data
axios.get(`${PREFIX}/api/miniapp/info/main?appId=${window.__blConfig.appId}`).then(response => {
    if (response.data.code == 0) {
        const data = response.data.data;
        window.__blConfig.data = data;
        window.__devtoolsConfig.type = data.type;
    } else {
        window.__blConfig.data = {};
    }
}).catch(function (error) {
    console.log(error);
});

// 页面有 NavigationBar 则展示
const showNavigationBar = () => {
    const { tabBar, tabBarMap = {}, pagesMap = {}, window:globalconfig } = window.__blConfig || {};
    const selected = pagesMap[window.__blConfig.currentPath];
    let instance = null;
    if (
        (tabBar && tabBarMap[window.__blConfig.currentPath]) || // 是tabBar
        (selected && selected.hasNavigationBar) || // 页面设置
        (globalconfig && globalconfig.hasNavigationBar && selected.hasNavigationBar === undefined) // window设置
    ) {
        instance = NavigationBar({
            config: window.__blConfig,
            selected
        });
    }
    return instance;
};
window.__blConfig.navigationInstance = showNavigationBar();

const query = parseQuery(window.location.href);
let topPath = '';

// 监听页面后退前进刷新等事件，在执行操作前更新sessionStorage里的globalData
window.onunload = () => {
    sessionStorage.bili_sapp_globalData = JSON.stringify(window.__appInstance.globalData);
};
Vue.use(components);

Vue.prototype.triggerEvent = function(eventName, eventPayload, { bubbles = false } = {}) {
    if (bubbles) {
        let parent = this;
        while (parent) {
            parent.$emit(eventName, {
                detail: eventPayload,
            });
            parent = parent.$parent;
        }
    } else {
        this.$emit(eventName, {
            detail: eventPayload,
        });
    }
};
Vue.prototype._mkarray = function(node, section, value) {
    const keys = section.split(/\[(\d+)\]/).filter(i => i !== '');
    const lastIndex = keys.pop();
    keys.forEach(key => {
        if (!Array.isArray(node[key])) {
            Vue.set(node, key, []);
        }
        node = node[key];
    });
    if (value === undefined) {
        Object.prototype.toString.call(node[lastIndex]) === '[object Object]' ? (value = node[lastIndex]) : (value = {});
    }
    Vue.set(node, lastIndex, value);
    return node[lastIndex];
};
Vue.prototype.setData = function(data) {
    if (typeof data === 'object') {
        for (const key in data) {
            if (data.hasOwnProperty(key)) {
                const value = data[key];

                const pathArray = key.split('.');
                const lastKey = pathArray.pop();

                let node = this.$data;
                for (let i = 0; i < pathArray.length; i++) {
                    const section = pathArray[i];
                    if (/\[\d+\]/.test(section)) {
                        node = this._mkarray(node, section);
                    } else {
                        if (typeof node[section] !== 'object' || node[section] == null) {
                            Vue.set(node, section, {});
                        }
                        node = node[section];
                    }
                }
                if (/\[\d+\]/.test(lastKey)) {
                    this._mkarray(node, lastKey, value);
                } else {
                    Vue.set(node, lastKey, value);
                }
            }
        }
    }
};
Vue.prototype.$methodInService = function(methodName, args) {
    if (typeof this[methodName] === 'function') {
        this[methodName].apply(this, args);
    }
};

// 最后再引入 bl api 和 App
require('bili-small-app/dev/bl');

const App = require('./index.vue').default;
App.__isPage = true;

Vue.directive('bili', {
    bind(el, binding) {
        let lastClickTime = 0;
        let clickTimer = null;
        let arg = binding.arg;
        let value = binding.value;
        let modifiers = binding.modifiers;

        if (!arg) {
            console.warn('[vue-bili] event type argument is required');
        }

        if (!value) return;

        if (!value.params) {
            value.params = [];
        }

        let startTx, startTy, endTx, endTy;

        el.addEventListener('touchstart', (e) => {
            let touch = e.touches[0];
            startTx = touch.clientX;
            startTy = touch.clientY;
        }, false);

        el.addEventListener('touchend', (e) => {
            let touch = e.changedTouches[0];
            endTx = touch.clientX;
            endTy = touch.clientY;

            if (Math.abs(startTx - endTx) < 6 && Math.abs(startTy - endTy) < 6) {
                if (arg === 'tap') {
                    if (typeof value === 'function') {
                        value();
                    } else if (typeof value == 'object') {
                        if (value.event) {
                            value.method(e, ...value.params);
                        } else {
                            value.method(...value.params);
                        }
                    }
                }

                let nowTime = new Date().getTime();
                if (nowTime - lastClickTime < 300) {
                    /* 双击 */
                    lastClickTime = 0;
                    clickTimer && clearTimeout(clickTimer);

                    if (arg === 'dbclick') {
                        if (typeof value === 'function') {
                            value();
                        } else if (typeof value == 'object') {
                            if (value.event) {
                                value.method(e, ...value.params);
                            } else {
                                value.method(...value.params);
                            }
                        }
                    }
                } else {
                    /* 单击 */
                    lastClickTime = nowTime;
                    clickTimer = setTimeout(() => {
                        if (arg === 'click') {
                            if (typeof value === 'function') {
                                value();
                            } else if (typeof value == 'object') {
                                if (value.event) {
                                    value.method(e, ...value.params);
                                } else {
                                    value.method(...value.params);
                                }
                            }
                        }
                    }, 300);
                }

                if (arg === 'tap' || arg === 'click' || arg === 'dbclick') {
                    if (modifiers && modifiers.stop) {
                        e.stopPropagation();
                    } else if (modifiers && modifiers.prevent) {
                        e.preventDefault();
                    }
                }
            }
        }, false);
    }
});
window.app = new Vue({
    el: '#app',
    render(h) {
        return h('app', {
            ref: 'bl__app'
        });
    },
    components: {
        App
    },
    beforeCreate() {
        try {
            if (!sessionStorage.bili_sapp_hasOnLaunch) {
                topPath = window.location.href;
                sessionStorage.bili_sapp_topPath = topPath;
                sessionStorage.bili_sapp_hasOnLaunch = true;
                sessionStorage.bili_sapp_globalData = window.__appInstance.globalData ? JSON.stringify(window.__appInstance.globalData) : '{}';

                window.__appInstance.onLaunch && window.__appInstance.onLaunch({
                    appId: sapprc.appId,
                    virtualId: sapprc.vAppId,
                    envVersion: sapprc.envVersion,
                    path: topPath.split('?')[0],
                    query,
                    refererInfo: query.refererInfo || {}
                });
            } else {
                window.__appInstance.globalData = (sessionStorage.bili_sapp_globalData && JSON.parse(sessionStorage.bili_sapp_globalData)) || {};
            }
        } catch (error) {
            console.error(error);
        }

        // 无法监听resize-content-height事件，挪到mounted()触发this.handler();

        // document.addEventListener('resize-content-height', this.handler, this);

        // 如果当前页面是tab页，则显示tabBar
        bl.showTabBar();
    },
    mounted() {
        try {
            window.__appInstance.onShow && window.__appInstance.onShow({
                path: window.location.href.split('?')[0],
                query,
                refererInfo: query.refererInfo || {},
                topPath: sessionStorage.bili_sapp_topPath
            });
        } catch (error) {
            console.error(error);
        }

        this.handler();
    },
    beforeDestroy() {
        // document.removeEventListener('resize-content-height', this.handler, this);
    },
    methods: {
        handler() {
            this.$nextTick(() => {
                const { navigationInstance, tabBar } = window.__blConfig;
                let container = document.body || document.documentElement,
                    tabHeight = 0,
                    navigatorHeight = 0,
                    padding = '';

                // 需再加个nextTick，否则tabBar获取不到正确的height
                this.$nextTick(() => {
                    if (navigationInstance) { // 有navigation
                        navigatorHeight = navigationInstance.$el.clientHeight;
                        container.style.padding = `${navigatorHeight}px 0 0`;
                    }

                    if (tabBar && tabBar.instance) { // 当前页面是tab页
                        tabHeight = tabBar.instance.$el.clientHeight;
                        if (tabBar.position === 'top') {
                            tabBar.instance.$el.style.top = `${navigatorHeight}px`;
                            padding = `${navigatorHeight + tabHeight}px 0 0`;
                        } else {
                            padding = `${navigatorHeight}px 0 ${tabHeight}px`;
                        }

                    } else { // 只有navigator高度
                        padding = `${navigatorHeight}px 0 0`;
                    }
                    container.style.padding = padding;
                });
            });
        }
    }
});


// 读取页面配置
const pageConfig = appJson.pagesMap[currentPath];
window.__blConfig.pageConfig = pageConfig;

const enablePullDownRefresh = typeof pageConfig.enablePullDownRefresh === 'undefined' ?
    (appJson.window.enablePullDownRefresh || false) :
    pageConfig.enablePullDownRefresh;

var touchStartY, touchEndY;
document.ontouchstart = (e) => {
    e = e || window.event;
    let touch = e.targetTouches[0];
    touchStartY = touch.pageY;
};
// 下拉刷新
if (enablePullDownRefresh) {
    if (window.scrollY == 0) { // 页面位于顶部才触发下拉刷新
        document.ontouchend = (e) => {
            e = e || window.event;
            let touch = e.changedTouches[0];
            touchEndY = touch.pageY;

            const existInstance = document.querySelector('.bl__refresh-container');
            if ((touchEndY - touchStartY) > 50) {
                if (existInstance) { // 避免多次下滑
                    return;
                }
                // 触发下拉刷新及回调
                bl.startPullDownRefresh();
                App.onPullDownRefresh && App.onPullDownRefresh.call(window.app.$children[0]);
            }

            if (existInstance) {
                // 上滑终止下拉刷新
                if ((touchStartY - touchEndY) > 30) {
                    bl.stopPullDownRefresh();
                }
            }
        };
    }
}


// 页面滚动 && 上拉触底
const enableScrollEvent = typeof pageConfig.enableScrollEvent === 'undefined' ?
    (appJson.window.enableScrollEvent || false) :
    pageConfig.enableScrollEvent;
const onReachBottomDistance = typeof pageConfig.onReachBottomDistance === 'undefined' ?
    (typeof appJson.window.onReachBottomDistance === 'undefined' ? 50 : appJson.window.onReachBottomDistance) :
    pageConfig.onReachBottomDistance;
// 是否在触发onReachBottom的区间里
let isInDistance = false;

if (enableScrollEvent) {
    document.addEventListener('scroll', function() { // 有滚动时，滚动至底部触发
        var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
        var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
        var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;

        App.onPageScroll && App.onPageScroll.call(window.app.$children[0], {
            scrollTop: window.scrollY
        });

        if (scrollTop + windowHeight + onReachBottomDistance >= scrollHeight) {
            if (!isInDistance) {
                isInDistance = true;
                App.onReachBottom && App.onReachBottom.call(window.app.$children[0]);
            }
        } else {
            isInDistance = false;
        }
    }, this);
}

// 点击tab
window.onbeforeunload = function() {
    if (!window.__blConfig.tabBarMap) {
        return;
    }

    const cur = window.__blConfig.pagesMap[window.__blConfig.currentPath];
    let tabIndex;

    Object.keys(window.__blConfig.tabBarMap).forEach((key, index) => {
        if (key == cur.path){
            tabIndex = index;
        }
    });
    App.onTabItemTap && App.onTabItemTap.call(window.app.$children[0], {
        index: tabIndex,
        pagePath: cur.path,
        text: window.__blConfig.tabBarMap[cur.path].text
    });
};

// 窗口尺寸大小变化
window.addEventListener('resize', function() {
    App.onWindowResize && App.onWindowResize.call(window.app.$children[0], {
        windowWidth: window.screen.width,
        windowHeight: window.screen.height
    });
});

// bl.internal.isEnableBounce
let isBounce;
isBounce = true; // 默认返回true
if(bl.internal.enableBounce({enable: 0})) {
    isBounce = false;
}
if(bl.internal.enableBounce({enable: 1})) {
    isBounce = true;
}
window.__blConfig.pageConfig = Object.assign(window.__blConfig.pageConfig, { isBounce });

// disableScroll 配置项处理
let isScroll;
isScroll = true; // bl.interl.isEnableScroll
let container = document.body || document.documentElement;
if(appJson.window && appJson.window.disableScroll) {
    if (typeof window.__blConfig.pageConfig.disableScroll === 'undefined'
        || window.__blConfig.pageConfig.disableScroll) { // disableScroll 页面级配置项优先级较高
        container.style.cssText += 'overflow: hidden;';
        isScroll = false;
    }
}
if(appJson.window && appJson.window.disableScroll === false || typeof appJson.window.disableScroll === 'undefined') {
    if (window.__blConfig.pageConfig && window.__blConfig.pageConfig.disableScroll) { // disableScroll 页面级配置项优先级较高
        container.style.cssText += 'overflow: hidden;';
        isScroll = false;
    }
    else {
        isScroll = true;
    }
}
window.__blConfig.pageConfig = Object.assign(window.__blConfig.pageConfig, { isScroll });

Vue.prototype.createSelectorQuery = function() {
    return bl.createSelectorQuery(this).in(this);
};
Vue.prototype.createIntersectionObserver = function(options) {
    return bl.createIntersectionObserver(this, options);
};

if (typeof SmallApp !== 'undefined' && typeof SmallApp.postMessage === 'function') {
    SmallApp.postMessage({type: 'render-loaded'});
}
