import { UserModel } from '@db/User';
import { LadderMatchRecModel } from '@db/LadderMatchRec';
import { PvpRecordModel } from '@db/PvpRecord';
import { BattleRecordModel } from '@db/BattleRecord';
import { STATUS, WAR_TYPE } from '@consts';
import { Controller } from 'egg';
import * as fs from 'fs';
import { RoleModel } from '@db/Role';
import { NoticeModel } from '@db/Notice';
import { ServerParamWithRole, GroupParam } from '../domain/gameField/serverlist';
import { reloadResources } from 'app/pubUtils/data';
import { ServerlistModel } from '@db/Serverlist';
import { dispatch } from 'app/pubUtils/dispatcher';
import { RedisClient } from 'redis';
import { REDIS_KEY } from '@consts';
import { RegionModel } from '@db/Region';
import { getRandEelmWithWeight } from 'app/pubUtils/util';
import { getLocalRplUrl, getRemoteRplUrl } from 'app/pubUtils/battleUtils'
import { ChannelInfoModel } from '@db/ChannelInfo';
import { GVGVestigeRecModel } from '@db/GVGVestigeRec';
import { GVGBattleRecModel } from '@db/GVGBattleRec';
const sendToWormhole = require('stream-wormhole');
const pump = require('mz-modules/pump');

export default class GameController extends Controller {

    public async checkVersion() {
        const { ctx } = this;
        const { version } = ctx.request.body;

        let curRegion = await RegionModel.findRegionByEnv(this.app.config.realEnv);
        if(!curRegion) {
            return ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR);
        }

        const versionFlag = ctx.service.utils.compareVersion(version, curRegion.minVersion);
        if (versionFlag >= 0) {
            ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS);
            return;
        }
        //版本号太低
        ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR, { version: curRegion.minVersion });
        return;
    }

    public async checkReview() {
        const { ctx } = this;
        const { version, platformAppid, platformAppId, addressType } = ctx.request.body;

        let curRegion = await RegionModel.findRegionByEnv(this.app.config.realEnv);
        if(!curRegion) {
            return ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR);
        }

        if(curRegion.addressType != addressType) {
            return ctx.body = ctx.service.utils.resResult(STATUS.ADDRESS_ERR);
        }

        let hasPolicy = false, userPolicyLink = '', privacyPolicyLink = '';       // 是否需要替换协议，有就是用下面两个字段替换
        let platform = platformAppid ?? platformAppId;
        let channelInfo = await ChannelInfoModel.findByPlatform(platform);
        if(channelInfo && !channelInfo.isDefaultPolicy) {
            hasPolicy = true;
            userPolicyLink = channelInfo.userPolicyLink;
            privacyPolicyLink = channelInfo.privacyPolicyLink;
        }

        let isReview = await ctx.service.update.checkReview(curRegion, version);
        let hasNewWebServer = false, webServerUrl = '';
        if(isReview && curRegion.reviewEnv) {
            let reviewRegion = await RegionModel.findRegionByEnv(curRegion.reviewEnv);
            if(reviewRegion) {
                hasNewWebServer = true, webServerUrl = reviewRegion.webHost;
            }
        }

        ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isReview, hasNewWebServer, webServerUrl, hasPolicy, userPolicyLink, privacyPolicyLink });
        return;
    }

    public async getServerList() {
        try {
            const { ctx } = this;
            let { uid } = ctx;
            const { version, addressType } = ctx.request.body;
            let serverList = new Array<GroupParam>();
            let loginServerList = new Array<ServerParamWithRole>();

            let curRegion = await RegionModel.findRegionByEnv(this.app.config.realEnv);
            if(!curRegion) {
                return ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR);
            }

            if(curRegion.addressType != addressType) {
                return ctx.body = ctx.service.utils.resResult(STATUS.ADDRESS_ERR);
            }

            let isReview = await ctx.service.update.checkReview(curRegion, version);
            let env = isReview? curRegion.reviewEnv: ctx.app.config.realEnv;

            let allServers = await ServerlistModel.findByEnv(env||ctx.app.config.realEnv, isReview);
            let roles = await RoleModel.findAllByUid(uid, true, true);
            for (let server of allServers) {
                let curGroup = serverList.find(cur => cur.groupId == server.groupId);
                if (!curGroup) {
                    curGroup = new GroupParam(server);
                    serverList.push(curGroup);
                }
                curGroup.pushServer(server);

                let role = roles.find(role => role.serverId == server.id);
                if (!!role) {
                    let curLoginInfo = new ServerParamWithRole(role, server);
                    loginServerList.push(curLoginInfo);
                }
            }

            loginServerList.sort((a, b) => { return b.updatedAt.getTime() - a.updatedAt.getTime() });

            if (serverList) {
                ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { serverList, loginServerList });
            } else {
                ctx.body = ctx.service.utils.resResult(STATUS.SERVER_NOT_FOUND);
            }
            return

        }catch(e) {
            console.error(e);
        }
    }

    public async getnotice() {
        const { ctx } = this;

        let notice = await NoticeModel.getAllNotice();
        let result = notice.map(cur => {
            let { id, title, tag, type, content, time } = cur;
            return {
                id, title, tag, type, content, time
            }
        })

        ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { notice: result });
        return
    }


    public async reloadResource() {
        const { ctx } = this;

        try {

            reloadResources();
            ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true });
            return;
        } catch (e) {
            ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: (<Error>e).stack });
            return;
        }
    }

    public async queryEnter() {
        const { ctx } = this;
        const { app, userCode } = ctx;

        let redisClient: RedisClient = app.context.redisClient;
        let hash = await redisClient.hvalsAsync(REDIS_KEY.SYS_SERVER);
        let connectors = hash.map(cur => JSON.parse(cur));

        if (!connectors || connectors.length === 0) {
            ctx.body = ctx.service.utils.resResult(STATUS.CONNECTOR_ERR);
            return
        }
        // select connector
        let sum = connectors.reduce((pre, cur) => pre + (cur['num']||0), 0);
        let res;
        if(sum > 0) {
            let serversWithWeight = connectors.map(cur => ({...cur, weight: sum - (cur['num']||0)}));
            let randResult = getRandEelmWithWeight(serversWithWeight);
            res = randResult.dic;
        }
        if(!res) {
            res = dispatch(userCode, connectors, 'connector');
        }
        let { id, serverType, clientHost, clientPort, num = 0 } = res;

        await redisClient.hsetAsync(REDIS_KEY.SYS_SERVER, id, JSON.stringify({ serverType, clientHost, clientPort, id, num: num + 1 }));
        ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { host: res.clientHost, port: res.clientPort });
        return
    }

    public async upload() {
        const { ctx } = this;
        const parts = ctx.multipart();
        let part;
        let [writePath, token, battleCode, fullPath, remoteUrl, warType] = ['', '', '', '', '', 0];
        while ((part = await parts()) != null) {
            if (part.length) {
                console.log('kv: ', `${part[0]}: ${part[1]}`);
                if (part[0] === 'token') {
                    token = part[1];
                } else if (part[0] === 'battleCode') {
                    battleCode = part[1];
                } else if (part[0] === 'warType') {
                    // 检测 part[1] 类型
                    if (typeof part[1] === 'number') {
                        warType = part[1];
                    } else if (typeof part[1] === 'string' && !isNaN(parseInt(part[1]))) {
                        warType = parseInt(part[1]);
                    }
                }
            } else {
                if (!part.filename) {
                    continue;
                }
                console.log(part);
                if (part.fieldname === 'rpl' && battleCode !== '') {
                    console.log('field: ', part.fieldname);
                    console.log('filename: ', part.filename);
                    if (token === '') {
                        ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS);
                        return;
                    }
                    const user = await UserModel.findUserByToken(token);
                    if (!user) {
                      console.error('token invalid');
                      ctx.body = ctx.service.utils.resResult(STATUS.TOKEN_ERR);
                      return;
                    }

                    let roleId = '';
                    if (!!warType && warType === WAR_TYPE.GVG_BATTLE) { // gvg激战期录像，没有通用 BattleRecord，单独处理
                        let gvgBattleRec = await GVGBattleRecModel.findByBattleCode(battleCode);
                        if (!gvgBattleRec) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_NOT_FOUND);
                        roleId = gvgBattleRec.attackTeam?.roleId;
                        if (!roleId) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_NOT_FOUND);
                    } else {
                        let battleRec = await BattleRecordModel.getBattleRecordByCode(battleCode, true);
                        if (!battleRec) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_NOT_FOUND);
                        roleId = battleRec.roleId;
                        warType = battleRec.warType;
                        if (warType !== WAR_TYPE.PVP && warType !== WAR_TYPE.LADDER && warType !== WAR_TYPE.GVG_VESTIGE) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_RPL_NOT_SUPPORT);
                    }

                    writePath = getLocalRplUrl(roleId, warType, battleCode);
                    try {
                        fs.accessSync(writePath);
                    } catch (err) {
                        if (err) {
                            fs.mkdirSync(writePath, { recursive: true });
                        }
                    }
                    fullPath = `${writePath}/${battleCode}.bin`
                    console.log(fullPath);
                    if (!fs.existsSync(fullPath)) {
                        fs.writeFileSync(fullPath, '');
                    }
                    const writeStream = fs.createWriteStream(fullPath);
                    await pump(part, writeStream);

                    let updateDBRes;
                    if (warType === WAR_TYPE.PVP) {
                        updateDBRes = await PvpRecordModel.updateRplStatus(battleCode, true);
                    } else if (warType === WAR_TYPE.LADDER) {
                        updateDBRes = await LadderMatchRecModel.updateRplStatus(battleCode, true);
                    } else if (warType == WAR_TYPE.GVG_VESTIGE) {
                        updateDBRes = await GVGVestigeRecModel.updateRplStatus(battleCode, true);
                    } else if (warType == WAR_TYPE.GVG_BATTLE) {
                        updateDBRes = await GVGBattleRecModel.updateRplStatus(battleCode, true);
                    }
                    if (!updateDBRes) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_RPL_UPDATE_ERR);
                    remoteUrl = `${getRemoteRplUrl(ctx.app.config.realEnv, roleId, warType, battleCode)}/${battleCode}.bin`;
                } else {
                    await sendToWormhole(part);
                }
            }
        }
        ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { rplUrl: remoteUrl });
        return;
    }
}
