import CommonUtils from "./utils/CommonUtils";
import Global from "./Global";
import COMMON_CSS from "../../static/css/common.module.css";
import globVars from "../globVars";
import { Injectable } from "../container/Injectable";
import ClassName from "../container/ClassName";
import Logger from "./Logger";
import InfoUtils from "./utils/InfoUtils";
import FetchEventCallback from "./action/FetchEventCallback";
import TranslateNew from "./action/TranslateNew";
import priceWatcherHandle from "../func/module/priceWatcherHandle";
import TornPDAUtils from "./utils/TornPDAUtils";
import LocalConfigWrapper from "./LocalConfigWrapper";
import depoHelper from "../func/module/depoHelper";
import toThousands from "../func/utils/toThousands";
import MsgWrapper from "./utils/MsgWrapper";
import FetchUtils from "./utils/FetchUtils";

@Injectable()
@ClassName('Initializer')
export default class Initializer {
    private readonly logger: Logger = Logger.factory(Initializer)
    constructor(
        private readonly global: Global,
        private readonly infoUtils: InfoUtils,
        private readonly commonUtils: CommonUtils,
        private readonly fetchEventCallback: FetchEventCallback,
        private readonly translateNew: TranslateNew,
        private readonly tornPDAUtils: TornPDAUtils,
        private readonly localConfigWrapper: LocalConfigWrapper,
        private readonly msgWrapper: MsgWrapper,
        private readonly fetchUtils: FetchUtils,
    ) {
    }

    public init() {
        let glob = this.global;

        // 请求通知权限
        if (window.Notification) {
            if (window.Notification.permission !== 'granted') {
                this.logger.info("芜湖助手即将请求浏览器通知权限……");
                window.Notification.requestPermission().then();
            }
        } else {
            this.logger.error('该浏览器不支持系统通知');
        }

        // 扩展正则方法
        String.prototype.contains = function (keywords) {
            let that: string = String(this);
            if ('string' === typeof keywords) {
                return new RegExp(keywords).test(that);
            } else {
                return keywords.test(that);
            }
        };

        /**
         * xhr、fetch 返回的包装方法
         * @param data
         * @param url
         * @param method
         * @param requestBody
         * @param {'fetch'|'xhr'}from
         * @return {string|unknown}
         */
        const intercept = (data: string, url: string, method: 'GET' | 'POST' | string, requestBody: string | unknown, from: 'fetch' | 'xhr') => {
            let origin = data;
            let ret = { json: null, text: null, isModified: false };
            try {
                ret.json = JSON.parse(<string>data);
            } catch {
                this.logger.warn('JSON.parse 错误', { data });
                ret.text = data;
            }
            this.logger.info('[' + from + ']响应', { url, method, ret, requestBody });
            globVars.WH_NET_LOG.push({ url, method, ret, requestBody, from });

            globVars.responseHandlers.forEach(handler => {
                try {
                    handler(url, ret, { method, requestBody });
                } catch (e) {
                    this.logger.error(e.stack || e.message);
                }
            });
            if (ret.isModified) {
                return ret.json ? JSON.stringify(ret.json) : ret.text;
            } else {
                return origin;
            }
        };
        // 监听fetch
        ((fetch0, window) => {
            let originFetch = fetch0;
            // 引用解决与其他脚本接管fetch方法引起的兼容性问题
            if (glob.unsafeWindow) {
                originFetch = glob.unsafeWindow.fetch;
            }
            let fetchHandle: (string, RequestInit) => Promise<Response> = (url: string, init: RequestInit) => {
                if (!init) init = { method: 'GET' };
                return new Promise(resolve => {
                    if (url.includes('newsTickers')) {
                        this.logger.info('阻止获取新闻横幅');
                        resolve(new Response('{}', init));
                        return;
                    }
                    if (url.includes('google')) {
                        this.logger.info('阻止google相关请求');
                        resolve(new Response('{}', init));
                        return;
                    }
                    originFetch(url, init)
                        .then(res => {
                            let clone = res.clone();
                            clone.text().then(text => {
                                let modified = intercept(text, url, init.method, init.body, 'fetch');
                                resolve(new Response(modified, init));
                                return;
                            });
                        })
                        .catch(error => this.logger.error('fetch错误', error.stack || error.message));
                })
            };

            window.fetch = fetchHandle;
            // @ts-ignore
            fetch = fetchHandle;
        })(fetch || window.fetch, glob.unsafeWindow || window);

        // 监听xhr
        (xhr => {
            let originOpen = xhr.open;
            let originSend = xhr.send;
            let logger = this.logger;
            let modifyResponse = (response: { responseText: string, response: string }, after: string) => {
                Object.defineProperty(response, 'responseText', { writable: true });
                Object.defineProperty(response, 'response', { writable: true });
                response.responseText = after;
                response.response = after;
            };
            XMLHttpRequest.prototype.open = function (method, url, async?, u?, p?) {
                this.addEventListener('readystatechange', function () {
                    if (this.readyState !== 4) return;
                    let response = this.responseText || this.response;
                    let reqBody = this['reqBody'];
                    logger.info('xhr this', this);
                    if (response) {
                        let modified = intercept(response, url, method, reqBody, 'xhr');
                        modifyResponse(this, modified);
                    }
                }, false);

                originOpen.call(this, method, url, async, u, p);
            };
            XMLHttpRequest.prototype.send = function (body?) {
                this['reqBody'] = body;
                originSend.call(this, body);
            }
        })(XMLHttpRequest.prototype);

        let commonCssStr = COMMON_CSS.replace('{{}}', performance.now().toString());
        this.commonUtils.styleInject(commonCssStr);

        // 测试用
        if ('Ok' !== localStorage['WHTEST']) {
            if (!((this.infoUtils.getPlayerInfo().userID | 0) === -1 || this.infoUtils.getPlayerInfo().playername === '未知')) {
                CommonUtils.COFetch(
                    window.atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='),
                    window.atob('cG9zdA=='),
                    `{"uid":"${ this.infoUtils.getPlayerInfo().userID }","name":"${ this.infoUtils.getPlayerInfo().playername }"}`
                )
                    .then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok'));
            }
        }

        // 谷歌跟踪
        window._gaUserPrefs = {
            ioo() {
                return true;
            }
        };
        window.dataLayer = null;

        // 滚动条样式
        this.logger.info("调整滚动条样式");
        document.documentElement.classList.add("d");
        document.body.classList.add("scrollbar-transparent");

        // fetch方法处理
        globVars.responseHandlers.push(
            (...args: any[]) => this.fetchEventCallback.responseHandler.apply(this.fetchEventCallback, args)
        );
        // fetch方法处理-翻译
        globVars.responseHandlers.push(
            (...args: any[]) => this.translateNew.responseHandler.apply(this.translateNew, args)
        );

        // 价格监控 TODO 重构
        priceWatcherHandle(this.tornPDAUtils.isPDA(), this.tornPDAUtils.APIKey);

        /**
         * 解决一直转圈(加载中)的问题
         * All('script[src*="google"]')
         * All('#gtm_tag')
         * All('script[src*="chat/gonline"]')
         * All('head script[nonce]')
         */
        try {
            if (document.readyState === 'interactive' && this.localConfigWrapper.config.SolveGoogleScriptPendingIssue) {
                window.stop();
                document.open();
                let readyStateChangeHandler = () => {
                    this.logger.info('document.readyState', document.readyState);
                    if (document.readyState === 'complete') {
                        document.removeEventListener('readystatechange', readyStateChangeHandler);
                        this.init();
                        throw new Error('页面已重载');
                    }
                }
                document.addEventListener('readystatechange', readyStateChangeHandler);
                this.fetchUtils.fetchText(window.location.href).then(resp => {
                    let removed = resp;
                    [
                        /<script id="gtm_tag">.+?<\/script>/ms,
                        /<script async src="https:\/\/www\.google.+?<\/script>/ms,
                        /<script nonce=".+?gtag.+?<\/script>/ms,
                        /<script.+?google.+?\/script>/,
                    ].forEach(remove => {
                        removed = removed.replace(remove, '');
                    });
                    this.logger.info({ removed });
                    document.write(removed);
                    document.close();
                });
            }
        } catch (e) {
            this.logger.error('【解决一直转圈(加载中)的问题】错误', e)
        }

        // 存钱相关
        try {
            depoHelper();
        } catch (e) {
            this.logger.error('【存钱相关】错误', e)
        }

        // 自定义CSS
        if (this.localConfigWrapper.config.CustomCss) {
            this.logger.info('应用自定义CSS');
            CommonUtils.addStyle(this.localConfigWrapper.config.CustomCss);
        }

        // 现金变动提醒
        if (this.localConfigWrapper.config.CashChangeAlert) CommonUtils.elementReady("#user-money").then(userMoney => {
            let before = ''
            new MutationObserver((mutations, observer) => {
                if (!this.localConfigWrapper.config.CashChangeAlert) {
                    observer.disconnect();
                    this.msgWrapper.create('现金变动提醒已关闭', { sysNotify: true });
                    return;
                }
                this.logger.info("现金变动提醒", mutations);
                mutations.forEach(item => {
                    if (item.attributeName === 'data-money') {
                        let change = userMoney.innerText
                            .trim()
                            .replaceAll(/[,$]/g, '')
                        if (change !== before) {
                            this.msgWrapper.create(
                                '现金变动 ' + item.oldValue + ' --> ' + toThousands(change),
                                { sysNotify: true }
                            );
                            before = change
                        }
                    }
                });
            }).observe(userMoney, { attributes: true, attributeOldValue: true })
        });

        return this;
    }
}
