import { Channel, pinus } from 'pinus';
import { getRandValueByMinMax, getRandEelm, decodeIdCntArrayStr } from '../pubUtils/util';
import { DEFAULT_HEROES, LINEUP_NUM, ROLE_SELECT, TALENT_RELATION_TYPE, TERAPH_RANDOM, SYSTEM_OPEN_ID, GuideUnloadNum, CHECK_HERO_CONSUME, ABI_STAGE } from "../consts";
import { DicTeraph } from '../pubUtils/dictionary/DicTeraph';
import { Teraph, RoleModel, RoleType, RoleUpdate } from '../db/Role';
import { SCHOOL } from '../pubUtils/dicParam';
import { gameData, getHeroInitTalent, getHeroStarByQuality } from '../pubUtils/data';
import { SchoolModel } from '../db/School';
import { SclResultInter, SclPosInter, RewardInter, ItemInter } from '../pubUtils/interface';
import { Connect, HeroModel, HeroSkin, HeroType, HeroUpdate, Talent } from '../db/Hero';
import { SkinUpdate } from '../db/Skin';
import { Figure } from '../domain/dbGeneral';
import { pick } from 'underscore';
import { Reward } from '../domain/battleField/pvp';
import { CheckMeterial } from './role/checkMaterial';
import IP2Region from "ip2region";
import { getServerCreateTime } from './redisService';
import { checkWhiteList } from '../pubUtils/sysUtil';
import { nowSeconds } from '../pubUtils/timeUtil';
const query = new IP2Region({ disableIpv6: true });


export async function getTeraphStrengthenResult(role: RoleType, count: number, dicTeraph: DicTeraph, teraph: Teraph) {
    let criAttr: number[] = [], times = 0;
    let check = new CheckMeterial(role.roleId, role);
    for(let i = 0; i < count; i++) {
        let attrs: number[] = []; // 可以强化的属性
        dicTeraph.mainAttrMax.forEach((max, id) => {
            if (teraph.attr.get(id) < max) {
                attrs.push(id);
            }
        });
        if(attrs.length <= 0) break; // 如果所有属性都到最高
        let isEnough = await check.decrease(dicTeraph.upMaterial);

        if(!isEnough) break; // 消耗不足

        // let num = getRandValueByMinMax(TERAPH_RANDOM.MIN, TERAPH_RANDOM.MAX); // 强化时随机增加 2-4 属性
        let arr = getRandEelm(attrs, 1); // 随机出的属性id
        let critical = getRandValueByMinMax(0, 100);//属性暴击率
        if(teraph.criCount == undefined || teraph.count == undefined) {
            teraph.criCount = 0;
            teraph.count = 0;
        }
        let isCritical = critical <= dicTeraph.criRate; // 每10次需要2次保底暴击
        if(!isCritical && Math.floor((teraph.count + 2)/10) * 2 > teraph.criCount) {
            isCritical = true;
        } else if (isCritical && Math.ceil(teraph.count/10) * 2 <= teraph.criCount) {
            isCritical = false;
        }
        teraph.count ++;
        if(isCritical) teraph.criCount ++;        

        let criEffect = isCritical?dicTeraph.criEffect: 1; // 暴击效果
        for (let attrId of arr) {
            let val = teraph.attr.get(attrId); // 已有的强化值
            val += dicTeraph.mainAttrUp.get(attrId) * criEffect;
            let max = dicTeraph.mainAttrMax.get(attrId);
            if(val > max) {
                val = max;
                let attrIndex = attrs.indexOf(attrId);
                attrs.splice(attrIndex, 1);
            }
            teraph.attr.set(attrId, val);
            if(isCritical) criAttr.push(attrId);
        }
        times++;

    }
    return { times, consumes: check.getConsume(), criAttr }
}

export async function getSimpleRoleInfo(roleId: string) {
    if (!roleId) return null;
    let role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.SHOW_SIMPLE, true);
    return pick(role, ['roleId', 'roleName', 'head', 'frame', 'spine', 'lv', 'title', 'job', 'quitTime', 'loginTime', 'vLv', 'guildName', 'serverId', 'userInfo']);
}

export async function getSimpleRoleInfos(roleIds: string[]) {
    if (!roleIds || !roleIds.length) return null;
    let roles = await RoleModel.findByRoleIds(roleIds, ROLE_SELECT.SHOW_SIMPLE, true);
    return roles.map(role => pick(role, ['roleId', 'roleName', 'head', 'frame', 'spine', 'lv', 'title', 'job', 'quitTime', 'loginTime', 'vLv', 'guildName', 'serverId', 'userInfo']));
}

/**
 * 百家学宫
 * @param roleId 
 */
export async function getSchoolList(roleId: string) {

    const dicPosition = decodeIdCntArrayStr(SCHOOL.SCHOOL_POSITION, 1); // id=>isOpen

    const userSchoolList = await SchoolModel.findByRoleId(roleId);

    let school = new Array<SclResultInter>();
    gameData.school.forEach((dicSchool) => {
        let position = new Array<SclPosInter>();
        dicPosition.forEach((isOpen, dicId) => {
            let id = parseInt(dicId);

            let userSchool = userSchoolList.find(cur => cur.schoolId == dicSchool.id && cur.positionId == id);
            if (userSchool) {
                position.push({
                    id,
                    hid: userSchool.hid,
                    isOpen: userSchool.isOpen
                });
            } else {
                position.push({
                    id,
                    hid: 0,
                    isOpen: !!isOpen
                });
            }
        });

        school.push({
            id: dicSchool.id,
            position
        });
    });
    return school;
}

function getDefaultHeroInfos(heroes: Map<number, HeroUpdate>, skins: Map<number, SkinUpdate>) {
    const initInfos: { heroInfo: HeroUpdate, skinInfo: SkinUpdate}[] = [];
    for(let hid of DEFAULT_HEROES) {
        initInfos.push({
            heroInfo: heroes.get(hid),
            skinInfo: skins.get(hid)
        });
    }
    return initInfos;
}

export function getDefaultRoleInfo(heroes: Map<number, HeroUpdate>, skins: Map<number, SkinUpdate>, role:RoleUpdate, figureInfo: { heads: Figure[], frames: Figure[], spines: Figure[] }) {
    const initInfos = getDefaultHeroInfos(heroes, skins);
    return { initInfos, role, figureInfo };
}

export function getInitHeroById(hid: number) {
    return {
        heroInfo: pinus.app.get('initHeroes').get(hid),
        skinInfo: pinus.app.get('initSkins').get(hid)
    }
}

export function addConsumeToHero(oldConsume: Reward[] = [], consumes: ItemInter[] = []) {
    if(!oldConsume) oldConsume = [];
    let consumeMap = new Map<number, number>();
    for(let { id, count = 1 } of consumes) {
        if(!consumeMap.has(id)) {
            consumeMap.set(id, count);
        } else {
            consumeMap.set(id, consumeMap.get(id) + count);
        }
        
    }
    let newConsume: Reward[] = [];
    for(let {id, count} of oldConsume) {
        if(consumeMap.has(id)) {
            newConsume.push({ id, count: count + consumeMap.get(id)});
            consumeMap.delete(id);
        } else {
            newConsume.push({ id, count });
        }
    }
    for(let [id, count] of consumeMap) {
        newConsume.push({ id, count });
    }
    return newConsume;
}

export function addConnect(connections: Connect[], curConnect: Connect) {
    let newConnections: Connect[] = [];
    let index = -1;
    for(let i = 0; i < connections.length; i++) {
        if(connections[i].shipId == curConnect.shipId) {
            index = i;
            newConnections.push(curConnect);
        } else {
            newConnections.push(connections[i]);
        }
    }
    if(index == -1) {
        newConnections.push(curConnect);
    }
    return newConnections;
}

export function checkUnlockTalentCondition(hid: number, id: number, talents: Talent[], usedTalentPoint: number) {
    let dicHero = gameData.hero.get(hid);
    let dicHeroTalent = gameData.heroTalent.get(id);
    if(!dicHeroTalent) return false;

    if(dicHero.talentId != dicHeroTalent.talentId) return false;
    
    // 累计消耗n点天赋点
    if(dicHeroTalent.preTotalPoint > usedTalentPoint) return false;
    // 互斥的天赋没有被解锁
    for(let { type, ids } of dicHeroTalent.relation) {
        if(type == TALENT_RELATION_TYPE.CONFLICT) {
            let hasTalent = talents.find(talent => ids.indexOf(talent.id) != -1);
            if(hasTalent) return false;
        }
    }
    // 前置节点
    let orFlag = false; // 只要其中一个满足就可以
    let maxPrecost = 0; // 前置节点最多的消耗
    for(let arr of dicHeroTalent.prepositionId) {
        let andFlag = true; // arr内所有节点都需要满足
        for(let id of arr) {
            let curTalent = talents.find(cur => cur.id == id);
            if(curTalent) {
                let dic = gameData.heroTalent.get(id);
                if(dic) {
                    let allCost = 0;
                    for(let { lv, cost } of dic.level) {
                        if(curTalent.level >= lv) allCost += cost;
                    }
                    if(allCost > maxPrecost) maxPrecost = allCost;
                } 
            } else {
                andFlag = false;
            }
        }
        if(andFlag) orFlag = true;
    }
    if(!orFlag) return false;
    // 在前置节点中消耗至少n点天赋点（在有多个前置节点时，取最高的1个）
    if(dicHeroTalent.preSinglePoint > maxPrecost) return false;
    return true;
}

export function updateSkinTalent(skins: HeroSkin[], newTalent: Talent, usedTalentPoint: number) {
    let newSkins: HeroSkin[] = [];
    let newTalents: Talent[] = [];
    for(let skin of skins) {
        if(skin.enable) {
            let hasNewTalent = false;
            for(let talent of skin.talent) {
                if(talent.id == newTalent.id) {
                    newTalents.push(newTalent);
                    hasNewTalent = true;
                } else {
                    newTalents.push(talent);
                }
            }
            if(!hasNewTalent) newTalents.push(newTalent);
            newSkins.push({...skin, talent: newTalents, usedTalentPoint })
        } else {
            newSkins.push(skin);
        }
    }
    return { newSkins, newTalents };
}

export function initSkinTalent(skins: HeroSkin[], isAll = false) {
    let newSkins: HeroSkin[] = [];
    for(let skin of skins) {
        if(isAll || skin.enable) {
            newSkins.push({ ...skin, talent: getHeroInitTalent(skin.skinId), usedTalentPoint: 0 });
        } else {
            newSkins.push(skin);
        }
    }
    return newSkins
}

export function checkSystemIsOpen(role: RoleType, id: SYSTEM_OPEN_ID) {
    if(!role || !role.hasInit || !role.guide) return false;

    let dicSystemOpenTime = gameData.sysOpenTime.get(id);
    if(!dicSystemOpenTime) return false;

    if(dicSystemOpenTime.warId) {
        return role.warStar.findIndex(cur => cur.id == dicSystemOpenTime.warId) != -1;
    } else {
        return role.lv >= dicSystemOpenTime.lv
    }
}

function calSchoolPoint(quality: number, star: number, colorStar: number) {
    let _star = colorStar > 0? colorStar: star;
    let point = (quality - 1) * 6 + _star;
    return point > 0? point: 0;
}

export async function getSchoolPoint(roleId: string) {
    let schools = await SchoolModel.findByRoleId(roleId);
    return schools.reduce((pre, cur) => {
        if(cur.isOpen && cur.hid > 0) {
            return pre + calSchoolPoint(cur.quality, cur.star, cur.colorStar);
        }
        return pre;
    }, 0);
}

export async function getIpLocation(ip: string) {
    try {
        const res = query.search(ip);
        console.log('##### setIpLocation', res)
        return res.province||res.country||'未知';
    } catch(e) {
        console.error('setIpLocation', e)
    }
}

// 服务器是否开启
export async function checkServerIsOpen(serverId: number, ip: string, uid: number) {
    let isWhiteList = await checkWhiteList(pinus.app.get('env'), ip, uid);
    if(isWhiteList) return true;

    let serverTime = await getServerCreateTime(serverId);
    return serverTime <= nowSeconds();
}

export function calStarUpConsume(hero: HeroType) {
    let originConsumes = hero.consumes||[];
    if(CHECK_HERO_CONSUME) {
        let dicHero = gameData.hero.get(hero.hid);
        let dicJob = gameData.job.get(dicHero?.jobid);
        if(dicHero && dicJob) {
            let decrease = 0;
            for(let star = dicHero.initialStar; star <= hero.star; star++) {
                let curDicHeroStar = getHeroStarByQuality(dicJob.job_class, dicHero.quality, star);
                if(!curDicHeroStar) continue;
                let max = star == hero.star ? hero.starStage: ABI_STAGE.END;
                for (let i = 0; i < max; i++) {
                    decrease += curDicHeroStar.advanceUpFragmentNum;
                }
            }
            for(let quality = dicHero.quality; quality < hero.quality; quality++) {
                let curDicHeroQualityUp = gameData.heroQualityUp.get(quality);
                if (!curDicHeroQualityUp) continue;
                decrease += curDicHeroQualityUp.fragmentNum;
            }
    
            return decreaseConsume(originConsumes, dicHero.pieceId, decrease);
        }
    }
    return { consumes: originConsumes, newConsumes: [] };
}

// 从原始消耗里减去消耗
function decreaseConsume(origin: Reward[], pieceId: number, decrease: number) {
    let consumes: Reward[] = [], newConsumes: Reward[] = [];
    for(let { id, count } of origin) {
        if(id == pieceId) {
            consumes.push({ id, count: count - decrease > 0? count - decrease: 0 });
            newConsumes.push({ id, count: decrease });
        } else {
            consumes.push({ id, count });
        }
    }
    return { consumes, newConsumes };
}