import CodeMgr from "../../scriptMain/CodeMgr"
import { StringMap } from "../Collections/StringMap"
import Gamecfg from "../common/gameCfg"
import { gameMethod } from "../common/gameMethod"
import { SevBack } from "../common/Xys"
import Config from "../Config"
import { GameEvent, NetworkEvent } from "../data/const/EventConst"
import GameDataCenter from "../data/GameDataCenter"
import GameController from "../GameController"
import UIHelp, { DialogParams } from "../logic/ui/UIHelp"
import EventMng from "../manager/EventMng"
import { I18n } from "../utils/I18nUtil"
import NetConfig from "./NetConfig"

export interface POST_DATA {
    cmdId: number
    url: string,
    body: Object,
    cb1?: (sev: SevBack) => void,
    cb2?: (sev: SevBack) => void,
    deal: number
    postTime?: number
}

export default class HttpRequest {
    // 网络请求所需时间
    static netMs: number = 0
    // 网络请求最长时间(超时时间)
    static timeoutMs: number = 15000
    // 请求失败计数
    static errCount: number = 0
    //请求失败的接口
    static errPostMap: StringMap<POST_DATA> = new StringMap<POST_DATA>();
    //当前请求的接口
    static curPostMap: StringMap<POST_DATA> = new StringMap<POST_DATA>();
    static GET(path: string, callback: Function = () => { }) {
        let isJiami = this.isJiamiPlatform();
        let r = new XMLHttpRequest()
        r.open("GET", path + `?x=${isJiami ? 1 : 0}`, true)
        r.timeout = this.timeoutMs
        let isTimeOut = false;
        let isError = false;
        r.onerror = () => {
            isError = true;
            console.log("发生错误,url=", path)
            callback(null)
        }
        r.ontimeout = () => {
            isTimeOut = true;
            console.log("超时了,url=", path)
            callback(null)
        }
        r.onloadend = function () {
            if (isTimeOut || isError) return;
            let temp = {}
            if (r.status >= 200 && r.status <= 400 && r.readyState == 4) {
                let txt = gameMethod.xorEncrypt(r.response, isJiami);
                temp = JSON.parse(txt)
            }
            callback(temp)
        }
        r.send()
    }
    // 只发送,无需处理回调
    static POST_SIMPLE(url: string, body: Object) {
        let r = new XMLHttpRequest()
        r.open("POST", url, true)
        r.setRequestHeader("Content-Type", "application/json")
        r.send(JSON.stringify(body))
    }
    // 发送业务外,需处理回调
    static POST_SIMPLE_2(url: string, body: Object, callback: Function = () => { }) {
        let r = new XMLHttpRequest()
        r.open("POST", url, true)
        r.setRequestHeader("Content-Type", "application/json")
        r.onreadystatechange = () => {
            if (r.status >= 200 && r.status <= 400 && r.readyState == 4) {
                callback(r.response)
            } else {
                console.error("POST Faile:", r.status)
            }
        }
        r.send(JSON.stringify(body))
    }
    // 业务逻辑协议调用接口
    static POST(path: string, data: { [key: string]: string }, body: Object, callback: Function = () => { }, isLogin: boolean = false) {
        body = body || {}
        if (CC_JSB) {
            if (!gameMethod.isEmpty(GameDataCenter.login.playerInfo?.switch) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo?.switch.clientLog)) {
                console.warn("C->S:", path, JSON.stringify(body))
            }
        } else {
            if (!gameMethod.isEmpty(GameDataCenter.login.playerInfo?.switch) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo?.switch.clientLog)) {
                console.warn("C->S:", path, body)
            }
        }
        let delay = new Date().getTime()
        if (NetConfig.needWait(data.url, body)) {
            EventMng.emit(NetworkEvent.WAIT, 1)
        }
        //淘宝需要通过云服务调用服务器
        if (cc.sys.platform == cc.sys.TAOBAO_MINIGAME) {
            this.taoBaoCloudRequest(data, body, callback, isLogin)
            return
        }
        let r = new XMLHttpRequest()
        r.open("POST", path, true)
        r.timeout = this.timeoutMs
        r.setRequestHeader("Content-Type", "application/json")
        let xor: number = this.isJiamiPlatform() ? 1 : 0;//原生端接收到加密数据会不全，所以就不加密了
        // r.onerror = () => {
        //     // console.warn("=======https onerror", r.status, r.readyState)
        //     callback({ type: 0 })
        //     if (isLogin) {
        //         EventMng.emit(NetworkEvent.LOGIN_FAILED)
        //         return
        //     }
        //     this.onError(data.url)
        // }
        r.onerror = r.ontimeout = () => {
            // console.warn("=======https ontimeout", r.status, r.readyState)
            this.netMs = r.timeout
            // 超时的时候,先把waiting关掉
            if (NetConfig.needWait(data.url, body)) {
                EventMng.emit(NetworkEvent.WAIT_CLOSE)
            }
            //请求失败时关闭强请求遮罩
            EventMng.emit(NetworkEvent.NETWAIT_MASK, false)
            // 如果是登录过程中出错,特殊处理
            if (isLogin) {
                EventMng.emit(NetworkEvent.LOGIN_FAILED)
                return
            }
            this.onTimeOut(data.url)
        }
        r.onreadystatechange = () => {
            // console.log("=======https onreadystatechange", r.status, r.readyState)
            if (r.readyState == 4) {
                this.netMs = new Date().getTime() - delay
                if (this.netMs > 1000) {
                    console.warn("服务器回调时间超过1秒:", path, JSON.stringify(body))
                }
            }
            let temp = {}
            if (r.status >= 200 && r.status <= 400 && r.readyState == 4) {
                this.errCount = 0
                if (r.response == null || r.response == "") {
                    EventMng.emit(NetworkEvent.ON_EXCEPTION)
                    this.showErrDialog(data.url, data.uuid, data.version, JSON.stringify(body), r.response == null ? "null" : r.response)
                    return
                }
                try {
                    // temp = JSON.parse(r.response) // gameMethod.jiemi(r.response)
                    // let jiemiTime = new Date().getTime()
                    let txt = gameMethod.xorEncrypt(r.response, xor == 1);
                    temp = JSON.parse(txt)
                    // console.log("消息解密耗时：", new Date().getTime() - jiemiTime)
                } catch (error) {
                    EventMng.emit(NetworkEvent.ON_EXCEPTION)
                    console.error("解析回调数据失败:", r.response)
                    this.showErrDialog(data.url, data.uuid, data.version, JSON.stringify(body), r.response == null ? "null" : r.response)
                }

                // try {
                if (temp && Object.keys(temp).length > 0) {
                    if (CC_JSB) {
                        if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch.clientLog)) {
                            console.warn("S->C:", JSON.stringify(temp))
                        }
                    } else {
                        if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch.clientLog)) {
                            console.warn("S->C:", temp)
                        }
                    }
                    //请求成功移除缓存的消息请求
                    this.errPostMap.Remove(data.url)
                    this.curPostMap.Remove(data.url)
                    callback(temp)
                }
                // } catch (error) {
                //     console.error("服务端回调数据处理时出现异常:", path, error)
                // }
            }
            if (r.readyState == 4 && NetConfig.needWait(data.url, body)) {
                EventMng.emit(NetworkEvent.WAIT, -1)
            }
            if (r.status < 200 || r.status > 400) {
                // 异常情况
                if (isLogin) {
                    EventMng.emit(NetworkEvent.LOGIN_FAILED)
                } else {
                    this.onTimeOut(data.url)
                }
            }
        };

        // r.send(JSON.stringify({ cs: CodeMgr.jiami(body) }))
        r.send(JSON.stringify({ xor: gameMethod.xorEncrypt(JSON.stringify(body)), x: xor }))
        // r.send(JSON.stringify(body))
    }

    //淘宝云服请求
    static taoBaoCloudRequest(data: { [key: string]: string }, body: Object, callback: Function = () => { }, isLogin: boolean = false) {
        let path = data.url + "?uuid=" + GameDataCenter.user.uuid +
            "&token=" + GameDataCenter.user.token +
            "&version=" + Config.appVersion +
            "&time=" + GameDataCenter.time.sevTime
        let self = this;
        async function taoBaoCloud() {
            let result
            try {
                result = await window['cloud'].application.httpRequest({
                    //不需要完整域名，只需要接口访问路径即可
                    'path': path,
                    'method': 'POST',
                    'headers': { "Content-Type": "application/json" },
                    'params': {},
                    'body': { xor: gameMethod.xorEncrypt(JSON.stringify(body)) },
                    //cloudAppId 云应用的Id
                    'exts': {
                        "cloudAppId": "55088",
                        "timeout": 4000,
                        //空应用调用需要填写该字段，包括协议头以及端口号（可省略），支持http、https
                        "domain": Gamecfg.packageInfo.getItem(Config.pid).wayhttp
                    }
                });
            } catch (error) {
                console.log(error)
            }
            // console.log("result:", JSON.stringify(gameMethod.xorEncrypt(result)));
            let temp = {}
            try {
                // temp = JSON.parse(result) // gameMethod.jiemi(r.response)
                temp = JSON.parse(gameMethod.xorEncrypt(result))
            } catch (error) {
                EventMng.emit(NetworkEvent.ON_EXCEPTION)
                console.error("解析回调数据失败:", result)
                // self.showErrDialog(data.url, data.uuid, data.version, JSON.stringify(body), result == null ? "null" : result)
            }
            try {
                if (temp && Object.keys(temp).length > 0) {
                    if (CC_JSB) {
                        if (!gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch.clientLog)) {
                            console.warn("S->C:", JSON.stringify(temp))
                        }
                    } else {
                        if (!gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch) && !gameMethod.isEmpty(GameDataCenter.login.playerInfo.switch.clientLog)) {
                            console.warn("S->C:", temp)
                        }
                    }
                    //请求成功移除缓存的消息请求
                    self.errPostMap.Remove(data.url);
                    self.curPostMap.Remove(data.url);
                    callback(temp)
                }
            } catch (error) {
                console.error("服务端回调数据处理时出现异常:", error)
            }
            if (NetConfig.needWait(data.url, body)) {
                EventMng.emit(NetworkEvent.WAIT, -1)
            }
        }
        this.errCount = 0
        let delayTime = new Date().getTime()
        taoBaoCloud().then(res => {
            if (!gameMethod.isEmpty(GameDataCenter.login.playerInfo?.switch?.clientLog)) {
                console.log("net delay time == " + (new Date().getTime() - delayTime))
            }
        });
    }

    // 发生服务器错误时,提示重试
    private static onError(url: string) {
        let dialogParam: DialogParams = {
            content: I18n.getI18nText("http_error", url),
            cbConfirm: () => {
                this.errCount = 0
                GameController.clear()
                cc.game.restart()
            },
            txtConfirm: I18n.getI18nText("http_overtime_confirm_1"),
            onlyConfirm: true
        }
        UIHelp.ShowSystemDialog(dialogParam)
    }

    // 发生服务器错误时,提示重试
    private static onTimeOut(url: string) {
        this.errCount++
        if (this.errCount > 3) {
            // 超过三次重连失败,提示退出游戏
            GameController.network.stopRequest = true
            let dialogParam: DialogParams = {
                content: I18n.getI18nText("http_error", url),
                cbConfirm: () => {
                    this.errCount = 0
                    GameController.clear()
                    cc.game.restart()
                },
                txtConfirm: I18n.getI18nText("http_overtime_confirm_1"),
                onlyConfirm: true
            }
            UIHelp.ShowSystemDialog(dialogParam)
        } else {
            // 提示网络错误并引导重连
            let dialogParam: DialogParams = {
                content: I18n.getI18nText("http_overtime"),
                cbConfirm: () => {
                    GameDataCenter.user.sendPlayerReconnect(() => {
                        GameController.network.stopRequest = false
                        //重连成功，重新发送失败请求
                        this.errPostMap.Foreach((key: string, value: POST_DATA) => {
                            console.log(`重新发送:${key}`)
                            GameController.network.send(value.url, value.body, value.cb1, value.cb2, value.deal)
                        })
                        this.errPostMap.Clear()
                        this.curPostMap.Clear()
                        GameController.network.RemoveTimers()
                    })
                },
                txtConfirm: I18n.getI18nText("http_overtime_confirm_2"),
                onlyConfirm: true
            }
            UIHelp.ShowSystemDialog(dialogParam)
        }
    }

    private static showErrDialog(url: string, uuid: string, version: string, body: string, response: string) {
        UIHelp.ShowDialog({
            content: "回调数据丢失,请将此界面截图给开发者\n"
                + `url:${url} uuid:${uuid} version:${version}\n`
                + `body:` + JSON.stringify(body) + "\n"
                + `response:${response.slice(0, 200)}`
        })
    }

    static isJiamiPlatform(): boolean {//以下平台不加密，因为以下加密数据会接受不全
        return !cc.sys.isNative && cc.sys.platform != cc.sys.VIVO_GAME && cc.sys.platform != cc.sys.XIAOMI_GAME && cc.sys.platform != cc.sys.OPPO_GAME;
    }
}