import 'tsrpc';
import { ApiCall, BaseConnection, ConnectionStatus, WsServer } from 'tsrpc';
import { Wjjl } from './module/collection_wjjl';
import { GH } from './public/gonghui/gh';
import { GHManage } from './public/gonghui/manage';
import { HeroFun } from './public/hero';
import { Rank } from './public/rank/rank';
import { UserFun } from './public/user';
import { joinFightData, roleDataType } from './shared/fightControl/fightType';
import { ResGetList } from './shared/protocols/item/PtlGetList';
import { ServiceType } from './shared/protocols/serviceProto';
import { ResLogin } from './shared/protocols/user/PtlLogin';
import { HeroShared, otherBuff } from './shared/public/hero';
import { PlayerShared } from './shared/public/player';
import { PublicShared } from './shared/public/public';
import {RankKfjs} from "./public/rank/rank_kfjs";
import { setGud } from './public/gud';

export function extendType() {

}

type msg = ServiceType['msg'];
type s2cMsg = Omit<msg, 'msg_c2s/BindUid'>;
type omitMsg = Pick<s2cMsg, 'msg_s2c/EquipChange' | 'msg_s2c/HeroChange' | 'msg_s2c/ItemChange' | 'msg_s2c/ShiwuChange' | 'msg_s2c/PeijianChange'>;

declare module 'tsrpc' {
    export interface BaseConnection {
        /**玩家上阵英雄 */
        get heroPos(): k_v<string>;
        /**有些功能会对英雄有属性加成 */
        get otherBuff(): otherBuff;
        /**获取当前公会 */
        get gonghui(): GH;
        /**玩家唯一uid 玩家登录时赋值*/
        uid: string;
        /**玩家登录账号 玩家登录时赋值*/
        bindUid: string;
        /**玩家数据 玩家登录时赋值*/
        gud: ResLogin['gud'];
        /**玩家道具 玩家拉取道具列表时赋值*/
        //item: ResGetList['list'];
        /**历史获得 */
        lshd: Partial<{
            hero: k_v<number>;
            equip: k_v<number>;
            peijian: k_v<number>;
        }>;
        /**玩家调用api的时间 */
        requstApiTime: k_v<number>;
        /**API 锁 */
        apiLock: k_v<number>;
        /**获取默认上阵战斗数据 */
        getDefaultFightData(def?: k_v<string>): Promise<joinFightData>;
        /**刷新上阵英雄战力 */
        refreshPower(): Promise<any>;
        /**玩家计算在线时长时间戳 */
        onlineTime: number;
    }

    export interface WsServer {
        /**玩家uid对应的socket连接 */
        uid_connections: k_v<BaseConnection<ServiceType>>;
        /**向单个玩家推送消息 */
        sendMsgByUid<msgType extends `msg_s2c${string}` & keyof msg>(uid: string, type: msgType, val: msg[msgType], pid?: string): void;

        broadcastClusterMsg<T extends string & keyof ServiceType['msg']>(msgName: T, msg: ServiceType['msg'][T], filter?: any): Promise<{
            isSucc: true;
        } | {
            isSucc: false;
            errMsg: string;
        }>;
    }

    export interface ApiCall {
        /**玩家唯一uid 玩家登录时赋值 */
        get uid(): string;
        /**有些功能会对英雄有属性加成 */
        get otherBuff(): otherBuff;
        /**接口回调之前自动推送的消息 */
        eventMsg: k_v<k_v<any>>;
        /**消息推送 */
        sendMsg<T extends string & keyof ServiceType['msg']>(msgName: T, msg: ServiceType['msg'][T]): ReturnType<BaseConnection['sendData']>;
        /**添加推送消息 */
        addEventMsg<T extends string & keyof omitMsg>(msgName: T, msgKey: string, msg: omitMsg[T]['']): void;
        addEventMsg<T extends 'msg_s2c/PlayerChange'>(msgName: T, msg: Partial<ResLogin['gud']>): void;
        addEventMsg<T extends 'msg_s2c/LshdChange'>(msgName: T, msgKey: 'equip' | 'hero' | 'peijian', msg: k_v<number>): void;
        /**返回错误码 */
        errorCode(code: number): Promise<void>;
    }

    export interface ApiServiceDef {
        /**
         * <br /> 
         * 
         * timeIntervalLimit?: number; ---> api调用间隔 默认500毫秒 <br />
         * 
         * needGudKey?: string[]; ---> 需要玩家gud里面存在的字段 并且conn.gud[key]为true
         */
        conf?: {
            [k: string]: any;
        };
    }
}

//BaseConnection
Object.defineProperties(BaseConnection.prototype, {
    'heroPos': {
        get(this: BaseConnection) {
            return this.gud.heroPos;
        },
    },
    'otherBuff': {
        get(this: BaseConnection): otherBuff {
            return UserFun.getOtherBuff(this.gud);
        },
    },
    'gonghui': {
        // 涉及call.conn.gonghui，对应引用变量处需增加await
        async get(this: BaseConnection): Promise<GH> {
            // return GHManage.list[this.gud.ghId];
            return await GHManage.getGH(this.gud.ghId);
        }
    },
});

BaseConnection.prototype.getDefaultFightData = async function (this: BaseConnection, def?: k_v<string>) {
    let posObj = def || this.heroPos;
    let roles: k_v<roleDataType> = {};

    if (!def && this.gud.selectMatrix && this.gud.matrixPos) {
        posObj = this.gud.matrixPos[this.gud.selectMatrix];
    }

    let heros = await HeroFun.getHeros(this, Object.values(posObj).filter(_id => _id != '') as string[]);

    Object.entries(posObj).forEach(obj => {
        let pos = obj[0];
        let _id = obj[1];
        let hero = heros.filter(h => h._id == _id)[0];

        if (hero) {
            roles[pos] = {
                ...hero,
                attr: {
                    ...HeroShared.getHeroBasicAttr(hero, { ...this.otherBuff, allBuff: HeroShared.getAllBuff(heros) }, Number(pos))
                }
            };
        }
    });

    return {
        player: { ...this.gud, buff: PlayerShared.getBuff(this.gud) },
        roles: roles
    };

};

// 战力刷新
BaseConnection.prototype.refreshPower = async function (this: BaseConnection<ServiceType>) {
    let power: number;

    const heroIds = Object.values(this.heroPos).filter(id => id != '');

    if (heroIds.length < 1) return power = 0;

    const heros = (await HeroFun.getHeros(this, heroIds)).filter(hero => hero != null);

    if (heros.length < 1) return power = 0;

    let jiban = 0;
    let powerObj = {
        hero: 0,
        zj: 0
    }
    // 英雄战力
    powerObj.hero = heros.map((h, index) => {
        let buff = HeroShared.getHeroBasicAttr(h, { ...this.otherBuff, allBuff: HeroShared.getAllBuff(heros) }, index + 1);
        jiban += buff.jiban || 0;
        return HeroShared.computeHeroZhanLi(buff);
    }).reduce((a, b) => a + b);
    Wjjl.setVal(this.uid, 'hero_jiban', jiban);
    // 主角属性变量设置
    let zjBuff: k_v<number> = {
        zj_atk: 0,
        zj_def: 0,
        zj_liliang: 0,
        zj_zhishi: 0
    }
    // 主角等级属性获取
    
    zjBuff.zj_atk += PublicShared.eval(G.gc.com.zjcz_atk.value as any as string, { lv: this.gud.lv })
    zjBuff.zj_def += PublicShared.eval(G.gc.com.zjcz_def.value as any as string, { lv: this.gud.lv })
    zjBuff.zj_liliang += PublicShared.eval(G.gc.com.zjcz_liliang.value as any as string, { lv: this.gud.lv })
    zjBuff.zj_zhishi += PublicShared.eval(G.gc.com.zjcz_zhishi.value as any as string, { lv: this.gud.lv })
    // 图鉴属性获取
    for(let i = 1; i <= this.gud.tujianLv; i++) {
        let item = G.gc.tujian_jc[i]
        if(item) {
            // 各等级累加
            for(let k in item.buff) {
                zjBuff[k] += item.buff[k]
            }
        }
    }
    // 名望属性加成
    if(this.gud.renown > 0) {
        let mwConf = G.gc.mw_dj
        // 各等级累加
        for(let i = 1; i <= this.gud.renown; i++) {
            let mwData = mwConf[i]
            for(let k in mwData.buff) {
                zjBuff[k] += mwData.buff[k]
            }
        }
    }
    // 训练计划加成
    let skillConf = G.gc.xunlianjihua
    if(this.gud.skills) {
        for(let i in this.gud.skills) {
            let item = skillConf[i]
            for(let j in zjBuff) {
                if((j + 'pro') == item.skill) {
                    zjBuff[j] += Math.floor(zjBuff[j] * PublicShared.eval(item.v, { slv: this.gud.skills[i]}))
                }
            }
        }
    }
    // 属性战力计算
    powerObj.zj = Object.values(zjBuff).reduce((a, b) => a + b) * 5
    // 总战力计算
    power = Math.floor(Object.values(powerObj).reduce((a, b) => a + b));
    // console.log(__filename)
    // console.log(await Wjjl.setVal(this.uid, 'hero_jiban', jiban))
    // console.log(power)
    // console.log(powerObj)
    // console.log(tujianBuff)
    // console.log(zjBuff)
    if (power != this.gud.power) {
        let dbUpdate = { power: power };
        if(power > this.gud?.maxpower){
            //记录历史最大战力
            //this.gud.maxpower = power;
            dbUpdate['maxpower'] = power;
        }

        //this.gud.power = power;
        setGud(this.uid,dbUpdate);
        
        G.mongodb.collection('user').updateOne({ uid: this.uid }, { $set: dbUpdate });
        this.sendMsg('msg_s2c/PlayerChange', dbUpdate);

        let rankKfjs = RankKfjs(7)
        rankKfjs?.setDataAndCheck({
            player: this.gud,
            valArr: [power]
        });

        Rank.list['zhanli'].addNew({
            valArr: [power],
            ... await this.getDefaultFightData()
        });

        Wjjl.setVal(this.uid, 'power', power);
    }
};


// ApiCall --------------
Object.defineProperties(ApiCall.prototype, {
    'uid': {
        get(this: ApiCall) {
            return this.conn.uid;
        },
    },
    'otherBuff': {
        get(this: ApiCall): otherBuff {
            return this.conn.otherBuff;
        },
    }
});

const succ = ApiCall.prototype.succ;
ApiCall.prototype.succ = async function (this: ApiCall, res: any) {
    this.eventMsg && await this.sendMsg('msg_s2c/Collection', this.eventMsg);
    return succ.call(this, res);
};

ApiCall.prototype.sendMsg = function (this: ApiCall, msgName: string, msg: any) {
    return this.conn.sendMsg(msgName, {
        fromApi: this.service.name,
        msg: msg
    });
};

const doubleApi = [
    'peijiancangku/Deal',
    'xstask/Get'
]

ApiCall.prototype.addEventMsg = function (this: ApiCall) {
    if (!this.conn.id) return;
    let msgName: keyof s2cMsg = arguments[0];
    let msgKey = arguments[1];
    let msg = arguments.length == 3 ? arguments[2] : arguments[1];

    if (!this.eventMsg) this.eventMsg = {};
    if (!this.eventMsg[msgName]) this.eventMsg[msgName] = {};
    if (arguments.length == 3) {
        if (!this.eventMsg[msgName][msgKey]) this.eventMsg[msgName][msgKey] = {};
        PublicShared.mergeProperty(this.eventMsg[msgName][msgKey], msg);
    } else {
        if(doubleApi.includes(this.service?.name) && this.service?.type=='api'&& Object.keys(this.eventMsg[msgName])[0]==Object.keys(msgKey)[0]){
            this.eventMsg[msgName] = msgKey
        }else {
            PublicShared.mergeProperty(this.eventMsg[msgName], msgKey);
        }
    }

    switch (msgName) {
        case 'msg_s2c/ItemChange':
            // if (this.conn.item[msgKey]) {
            //     if (msg.num == 0) {
            //         this.conn.item[msgKey] = null;
            //         delete this.conn.item[msgKey];
            //     } else Object.assign(this.conn.item[msgKey], msg);
            // } else {
            //     this.conn.item[msgKey] = msg;
            // }
            break;
        case 'msg_s2c/PlayerChange':
            Object.assign(this.conn.gud, msg);
            if (Object.keys(this.otherBuff).intersection(Object.keys(msg)).length > 0) this.conn.refreshPower();
            break;
        case 'msg_s2c/LshdChange':
            this.conn?.lshd?.[msgKey] && PublicShared.mergeProperty(this.conn.lshd[msgKey], msg);
            break;
    }
};

ApiCall.prototype.errorCode = function (this: ApiCall, code: number) {
    return this.error('', { code: code });
};