import { BackendSession } from "pinus";
import { GVG_ACTIVE_TYPE, GVG_ITEM, GVG_RESOURCE_TYPE, GVG_SEED_TYPE, GVG_TECH_TYPE, ITEM_CHANGE_REASON, LEAGUE_JOB, PUSH_ROUTE } from "../../consts";
import { GVGLeagueFarmModel, GVGLeagueFarmType } from "../../db/GVGLeagueFarm";
import { GVGLeaguePrepareModel, Tech } from "../../db/GVGLeaguePrepare";
import { GVGUserDailyDataModel, GVGUserDailyDataType } from "../../db/GVGUserDailyData";
import { GVGUserDataModel } from "../../db/GVGUserData";
import { GVGUserItemModel } from "../../db/GVGUserItem";
import { LeagueField } from "../../domain/gvgField/returnData";
import { calLeagueLv, gameData, getFieldMaxAddType } from "../../pubUtils/data";
import { GVG } from "../../pubUtils/dicParam";
import { RewardInter } from "../../pubUtils/interface";
import { nowSeconds } from "../../pubUtils/timeUtil";
import { arrToMap, getArrayOfNumber, getRandEelm, decodeIdCntArrayStr, parseGoodStr, sortArrRandom } from "../../pubUtils/util";
import { addGVGReward } from "./gvgItemService";
import { getGVGConfig } from "./gvgService";
import { getProduceCoinCnt } from "./gvgItemService";
import { GVGVestigeRankModel } from "../../db/GVGVestigeRank";
import { addLeagueLvUpMessage, addResourceRecord } from "./gvgRecService";
import { GVGLeagueType } from "../../db/GVGLeague";
import { sendMessageToGuildWithSuc } from "../pushService";

export function checkPreTech(techId: number, activeQueue: number[], techQueue: Tech[]) {
    const dicTech = gameData.gvgTech.get(techId);
    if(!dicTech) return false;

    let hasOrUnlock = dicTech.prepositionId.length == 0;
    for(let andArr of dicTech.prepositionId) { // or关系
        let hasAndUnlock = true;
        for(let id of andArr) { // and关系
            if(!checkTechIsIng(id, activeQueue, techQueue)) {
                hasAndUnlock = false; break;
            }
        }
        if(hasAndUnlock) hasOrUnlock = true;
    }
    return hasOrUnlock;
}

export function checkTechIsIng(techId: number, activeQueue: number[], techQueue: Tech[]) {
    return techQueue.findIndex(cur => cur.id == techId) != -1 || activeQueue.indexOf(techId) != -1
}

export function calProducerContribute(obj: { food: number, mineral: number, wood: number }) {
    let { food = 0, mineral = 0, wood = 0 } = obj||{};
    let ratio = decodeIdCntArrayStr(GVG.GVG_PRODUCE_GAME_RATIO, 1);
    let foodRatio = ratio.get(GVG_RESOURCE_TYPE.FOOD.toString())||0;
    let mineralRatio = ratio.get(GVG_RESOURCE_TYPE.MINERAL.toString())||0;
    let woodRatio = ratio.get(GVG_RESOURCE_TYPE.WOOD.toString())||0;
    return food * foodRatio + mineral * mineralRatio + wood * woodRatio;
}

export function calFighterContribute(obj: { score: number }) {
    return obj?.score||0
}

/**
 * 获得活跃
 * @param leagueCode 
 * @param roleId 
 * @param type GVG_ACTIVE_TYPE
 * @returns 
 */
export async function addGVGActive(leagueCode: string, roleId: string, type: GVG_ACTIVE_TYPE, count = 1) {
    let { configId } = getGVGConfig();

    let add = gameData.gvgActive.get(type)||0;
    let result = await GVGUserDataModel.addActive(configId, leagueCode, roleId, type, add * count);
    return result.active;
}

export async function addGVGTechActive(leagueCode: string, roleId: string) {
    let { configId } = getGVGConfig();
    let check = await GVGUserDailyDataModel.checkGetTechActive(configId, leagueCode, roleId);
    if(!check) {
        await GVGUserDailyDataModel.setHasGetTechActive(configId, leagueCode, roleId);
        return await addGVGActive(leagueCode, roleId, GVG_ACTIVE_TYPE.TECH_ACTIVATE);
    } else {
        const data = await GVGUserDataModel.findByRole(configId, leagueCode, roleId);
        return data?.active||0;
    }
}

export function getGVGDailyItems(job: number) {
    return job == LEAGUE_JOB.PRODUCER? parseGoodStr(GVG.GVG_PRODUCER_GET): parseGoodStr(GVG.GVG_FIGHTER_GET);
}

export async function getDailyLoginReward(roleId: string, roleName: string, sid: string, leagueCode: string, job: number) {
    let { configId } = getGVGConfig();
    if(job && !await GVGUserDailyDataModel.checkHasLogin(configId, leagueCode, roleId)) {
        // 发每日奖励
        await GVGUserDailyDataModel.setHasLogin(configId, leagueCode, roleId);
        let leagueGoods = await addGVGReward(roleId, roleName, leagueCode, sid, getGVGDailyItems(job), [], ITEM_CHANGE_REASON.GVG_LOGIN)
        let active = await addGVGActive(leagueCode, roleId, GVG_ACTIVE_TYPE.LOGIN);
        return { active, reward: leagueGoods }
    }
    return null;
}

export function getMyContribute(userDailyData: GVGUserDailyDataType, myRanks: {vestigeId: number, rank: number}[]) {
    let score = 0;
    for(let { vestigeId, rank} of myRanks) {
        let dicRankMap = gameData.gvgVestige.get(vestigeId);
        if(dicRankMap && dicRankMap.get(rank)) score += dicRankMap.get(rank).score; 
    }
    return {
        food: userDailyData?.food||0,
        mineral: userDailyData?.mineral||0,
        wood: userDailyData?.wood||0,
        score: userDailyData?.score||score,
    }
}

/**
 * 资源
 * @param leagueCode 联军id 
 * @param roleId 玩家id
 * @param resourceType 资源类型
 * @param count 数量
 * @returns 
 */
export async function addResource(myLeague: GVGLeagueType, roleId: string, roleName: string, resourceType: GVG_RESOURCE_TYPE, count: number) {
    let { leagueCode } = myLeague;
    let { configId } = getGVGConfig();
    let resources = getResourceCnt(resourceType, count);
    if(!resources) return false;
    let { food = 0, mineral = 0, wood = 0 } = resources;

    // 联军更新资源
    let league = await GVGLeaguePrepareModel.addResource(configId, leagueCode, food, mineral, wood);
    let originLv = league?.lv||1;
    // 联军升级
    let lv = calLeagueLv(league.resources);
    league = await GVGLeaguePrepareModel.setLv(configId, leagueCode, lv);
    // 个人贡献更新
    await GVGUserDataModel.addResource(configId, leagueCode, roleId, food, mineral, wood);
    // 个人今日贡献更新
    let userDailyData = await GVGUserDailyDataModel.addResource(configId, leagueCode, roleId, food, mineral, wood);
    let myRanks = await GVGVestigeRankModel.findAllByRole(roleId);

    let myContribute = getMyContribute(userDailyData, myRanks);
    addResourceRecord(roleId, roleName, leagueCode, resourceType, count);
    if(lv > originLv) addLeagueLvUpMessage(roleId, roleName, myLeague, lv);

    return { resources: league.resources, leagueLv: league.lv, myContribute }
}

function getResourceCnt(resourceType: GVG_RESOURCE_TYPE, count: number) {
    switch(resourceType) {
        case GVG_RESOURCE_TYPE.FOOD: 
            return { food: count };
        case GVG_RESOURCE_TYPE.MINERAL:
            return { mineral: count };
        case GVG_RESOURCE_TYPE.WOOD:
            return { wood: count };
    }
}

export async function lockField(leagueCode: string, roleId: string, roleName: string, farmId: number) {
    let { configId } = getGVGConfig(); 
    const produceCoinCnt = await getLockFieldCnt(leagueCode, roleId);
    if(produceCoinCnt == 0) return

    const leagueFarms = await GVGLeagueFarmModel.findByFarmId(configId, leagueCode, farmId);       
    // 查询现有的田
    let { maxAddTypeCntMap, myAddTypeCntMap, allLands, myFieldCnt, myLockField } = calCntFromFarms(farmId, leagueFarms, roleId);
    if(allLands.length <= 0 || myLockField >= produceCoinCnt) return; // 田数量不足 or 已经锁过了

    let allAdd = calAddType(myFieldCnt, myAddTypeCntMap, maxAddTypeCntMap, produceCoinCnt - myLockField);
    let randLand = sortArrRandom(getRandEelm(allLands, produceCoinCnt - myLockField));
    let lands: { fieldId: number, addType: number }[] = [];
    for(let fieldId of randLand) {
        let addType = 0;
        for(let [_addType, addCnt] of allAdd) {
            if(addCnt > 0) {
                addType = _addType; 
                allAdd.set(_addType, addCnt - 1);
                break;
            }
        }
        lands.push({ fieldId, addType });
    }
    // 更新田
    let dicFarm = gameData.gvgResource.get(farmId);
    await GVGLeagueFarmModel.lockFields(configId, leagueCode, farmId, dicFarm?.type, roleId, roleName, lands);
    return await GVGLeagueFarmModel.findByFarmIdAndRoleId(configId, leagueCode, farmId, roleId);
}

function calCntFromFarms(farmId: number, leagueFarms: GVGLeagueFarmType[], roleId: string) {
    let maxAddTypeCntMap = getFieldMaxAddType(farmId); // 加成田最大数量
    let myAddTypeCntMap = new Map<number, number>();
    let myFieldCnt = 0; // 玩家已有的田有
    let myLockField = 0; // 玩家没有种只是锁住了的田
    let allLands = getArrayOfNumber(gameData.gvgResource.get(farmId)?.sum??0); // 全部可使用的田
    for(let leagueFarm of leagueFarms) {
        // 加成
        for(let {addType, roleId: addTypeRoleId} of leagueFarm.addTypes) {
            maxAddTypeCntMap.set(addType, maxAddTypeCntMap.get(addType) - 1);
            if(addTypeRoleId == roleId) {
                if(!myAddTypeCntMap.has(addType)) myAddTypeCntMap.set(addType, 0);
                myAddTypeCntMap.set(addType, myAddTypeCntMap.get(addType) + 1);
            }
        }
        if(leagueFarm.unlockTime >= nowSeconds()) {
            let index = allLands.indexOf(leagueFarm.fieldId);
            if(index > -1) allLands.splice(index, 1);
        }

        // 玩家数据
        if(leagueFarm.lockRoleId == roleId && leagueFarm.unlockTime >= nowSeconds()) {
            myFieldCnt++;
            if(leagueFarm.harvestTime == 0) myLockField++;
        }
    }
    return { maxAddTypeCntMap, myAddTypeCntMap, allLands, myFieldCnt, myLockField }
}

/**
 * 
 * @param myFieldCnt 我已种的田的数量
 * @param myAddTypeCntMap 我已种的田的有加成的田分别的数量
 * @param maxAddTypeCntMap 联军最多可以加成田的数量
 * @param count 本次可以加的田
 * @returns 
 */
function calAddType(myFieldCnt: number, myAddTypeCntMap: Map<number, number>, maxAddTypeCntMap: Map<number, number>, count: number) {
    let result = new Map<number, number>(); // 加成类型, 数量  

    for(let [addType, ratio] of gameData.gvgPlayerFieldAddType) { // 按比例加田
        let maxCnt = maxAddTypeCntMap.get(addType)||0;
        if(maxCnt == 0) continue;
        let needCnt = Math.floor((myFieldCnt + count) * ratio / 100); // 玩家按照比例需要的加成数量
        let cnt = myAddTypeCntMap.get(addType)||0; // 我当前的加成数量
        let resultCnt = needCnt - cnt;
        if(resultCnt < 0) resultCnt = 0;
        if(resultCnt > maxCnt) resultCnt = maxCnt;
        maxAddTypeCntMap.set(addType, maxAddTypeCntMap.get(addType) - resultCnt);
        result.set(addType, resultCnt);
    }
    return result;
}

export async function rollbackUnPlantFields(cost: RewardInter[], plantResult: GVGLeagueFarmType[], session: BackendSession, leagueCode: string) {
    const roleId = session.get('roleId');
    const roleName = session.get('roleName');
    const sid = session.get('sid');
    let add: RewardInter[] = [];
    for(let { id, count } of cost) add.push({ id, count });
    for(let { seedType } of plantResult) {
        let index = add.findIndex(({ id }) => id == getItemIdOfSeedType(seedType));
        if(index != -1) {
            add[index].count --;
            if(add[index].count <= 0) add.splice(index);
        }
    }
    await addGVGReward(roleId, roleName, leagueCode, sid, add, [], ITEM_CHANGE_REASON.PLANT_ROLLBACK);
    return add;
}

// 计算实际消耗的种子
export function getCostSeedReward(plantResult: GVGLeagueFarmType[]) {
    const reward: RewardInter[] = [], leagueReward: RewardInter[] = [];
    for(let { seedType } of plantResult) {
        let itemId = getItemIdOfSeedType(seedType);
        let dicGVGItem = gameData.gvgItem.get(itemId);
        if(!dicGVGItem) continue;
        for(let { id, count } of dicGVGItem.reward) {
            let rewardObj = reward.find(({ id }) => id == id);
            rewardObj? rewardObj.count += count: reward.push({ id, count: count });
        }
        for(let { id, count } of dicGVGItem.leagueReward) {
            let rewardObj = leagueReward.find(({ id }) => id == id);
            rewardObj? rewardObj.count += count: leagueReward.push({ id, count: count });
        }
    }
    return { reward, leagueReward }
}

export function getItemIdOfSeedType(seedType: GVG_SEED_TYPE) {
    switch(seedType) {
        case GVG_SEED_TYPE.WHEAT:
            return GVG_ITEM.WHEAT;
        case GVG_SEED_TYPE.CORN:
            return GVG_ITEM.CORN;
        case GVG_SEED_TYPE.RICE:
            return GVG_ITEM.RICE;
    }
}

export async function getLockFieldCnt(leagueCode: string, roleId: string) {
    let { configId } = getGVGConfig();
    const itemIds = [GVG_ITEM.WHEAT, GVG_ITEM.CORN, GVG_ITEM.RICE, GVG_ITEM.PRODUCE_COIN];
    const items = await GVGUserItemModel.findByRoleAndIds(configId, leagueCode, roleId, itemIds);
    return items.reduce((pre, cur) => pre + cur.count, 0);
}

export async function getmyContributeRank(leagueCode: string, members: { roleId: string }[], targetRoleId: string) {
    let { configId } = getGVGConfig();
    
    const roleIds = members.map(member => member.roleId);
    const userDatas = await GVGUserDataModel.findByRoles(configId, leagueCode, roleIds);
    userDatas.sort((a, b) => calProducerContribute(b.contribute) - calProducerContribute(a.contribute));
    for(let i = 0; i < userDatas.length; i++) {
        if(userDatas[i].roleId == targetRoleId) return i + 1;
    }
    return 0
}

export async function getLeagueFarmShowResult(leagueCode: string, roleId: string, farmId: number, needField = true) {
    let { configId } = getGVGConfig();
    const leagueFarms = await GVGLeagueFarmModel.findLockedByFarmId(configId, leagueCode, farmId);
    if(!needField) return { farmId, count: leagueFarms.length };
    
    const fields = await GVGLeagueFarmModel.findByFarmIdAndRoleId(configId, leagueCode, farmId, roleId);
    const result = fields.map(field => new LeagueField(field));
    return { farmId, count: leagueFarms.length, fields: result }
}

export function calFarmOutput(seedType: number, addType: number, farmId: number, cities: number[], activeTech: number[]) {
    // 种子基础产量 * ( 1 + 格子加成 + 农庄等级加成 + 战力城池加成 + 科技树加成)
    let itemId = getItemIdOfSeedType(seedType);
    let dicGVGItem = gameData.gvgItem.get(itemId);
    let dicFarm = gameData.gvgResource.get(farmId);
    if(!dicGVGItem || !dicFarm) return { output: 0, outputStr: '' };
    let { value: foodBase } = dicGVGItem;
    let addRatio = seedType == addType? GVG.GVG_SP_FIELD_ADD * 100: 0;
    let farmRatio = dicFarm.fieldAdd;
    let cityRatio = getCitiesAdd(cities, GVG_RESOURCE_TYPE.FOOD);
    let techRatio = getTechAdd(activeTech);

    return { 
        output: Math.floor(foodBase * (1 + (addRatio + farmRatio + cityRatio + techRatio)/100)),
        outputStr: `Math.floor(${foodBase} * (1 + (${addRatio} + ${farmRatio} + ${cityRatio} + ${techRatio})/100))`
    }
}

export async function sumOutputByResults(myResultFields: GVGLeagueFarmType[]) {
    let [foodSum = 0, rewardSum = new Array<RewardInter>(), leagueRewardSum = new Array<RewardInter>(), activeSum = 0] = [];
    for(let { seedType, output: food } of myResultFields) {
        let itemId = getItemIdOfSeedType(seedType);
        let dicGVGItem = gameData.gvgItem.get(itemId);
        if(!dicGVGItem) continue;
        let {  reward, leagueReward, leagueConsume } = dicGVGItem;
        let produceCoin = getProduceCoinCnt(leagueConsume);
        foodSum += food;
        for(let { id, count } of reward) {
            let rewardObj = rewardSum.find(cur => cur.id == id);
            rewardObj? rewardObj.count += count: rewardSum.push({id, count});
        }
        for(let { id, count } of leagueReward) {
            let rewardObj = leagueRewardSum.find(cur => cur.id == id);
            rewardObj? rewardObj.count += count: leagueRewardSum.push({id, count});
        }
        activeSum += produceCoin;
    }
    return { foodSum, rewardSum, leagueRewardSum, activeSum }
}

/**
 * 计算挖矿产量
 * @param farmId 矿场id
 * @param result 小游戏的结果
 * @param cities 上周占领城镇加成
 * @param activeTech 千机阁加成
 * @returns 
 */
export function calMineOutput(farmId: number, result: [{ type: number, count: number }], cities: number[], activeTech: number[]) {
    // ＝（100＊1.5【中铁块单块产量倍数】＊4【可挖出的中铁块数量】＋100＊2【大铁块单块产量倍数】＊8【可挖出的大铁块数量】＋100＊3【特大铁块单块产量倍数】＊8【可挖出的特大铁块数量】）＊（1＋0.5【大城产量加成】）
    let dicMine = gameData.gvgResource.get(farmId);
    if(!dicMine) return { output: 0, outputStr: '' };
    let output = 0, addStrArr = [];
    for(let { type, count } of result) {
        let dic = dicMine.mineralValue.find(cur => cur.type == type);
        if(!dic) continue;
        output += dic.output * count;
        addStrArr.push(`${dic.output}*${count}`);
    }
    let cityRatio = getCitiesAdd(cities, GVG_RESOURCE_TYPE.MINERAL);
    let techRatio = getTechAdd(activeTech);
    return {
        output: Math.floor(output * (1 + (cityRatio + techRatio)/100)),
        outputStr: `Math.floor(${addStrArr.join('+')} * (1 + (${cityRatio} + ${techRatio})/100))`
    }
}

/**
 * 计算木块产量
 * @param farmId 林场id
 * @param result 小游戏的结果
 * @param cities 上周占领城镇加成
 * @param activeTech 千机阁加成
 * @returns 
 */
export function calForestryOutput(farmId: number, result: number, cities: number[], activeTech: number[]) {
    // ＝100＊2【10级木堆木块单块基础产量倍数】＊（1＋0.3【城镇加成倍数】）＊40【使用铁斧头最大分块】
    let dicForestry = gameData.gvgResource.get(farmId);
    if(!dicForestry) return { output: 0, outputStr: '' };
    let base = dicForestry.woodOutput;
    let cityRatio = getCitiesAdd(cities, GVG_RESOURCE_TYPE.WOOD);
    let techRatio = getTechAdd(activeTech);
    return {
        output: Math.floor( base * (1 + (cityRatio + techRatio)/100) * result),
        outputStr: `Math.floor(${base} * (1 + (${cityRatio} + ${techRatio})/100) * ${result})`
    }

}

// 城池加成，多城池取最大加成
function getCitiesAdd(cities: number[], resourceType: number) {
    let outputAdd: number = 0;
    for(let cityId of cities) {
        let dicCity = gameData.gvgCity.get(cityId);
        let dicCityAdd = gameData.gvgCityAdd.get(dicCity?.cityType);
        if(!dicCity || !dicCityAdd) continue;
        if(resourceType == GVG_RESOURCE_TYPE.FOOD) {
            outputAdd += dicCityAdd.foodAdd;
        } else if (resourceType == GVG_RESOURCE_TYPE.MINERAL) {
            outputAdd += dicCityAdd.mineralAdd;
        } else if (resourceType == GVG_RESOURCE_TYPE.WOOD) {
            outputAdd += dicCityAdd.woodAdd;
        }
    }
    return outputAdd;
}

// 科技树加成 叠加
function getTechAdd(activeTech: number[]) {
    let ratio = 0;
    for(let techId of activeTech) {
        let dicTech = gameData.gvgTech.get(techId);
        if(dicTech.type == GVG_TECH_TYPE.PRODUCE_UP) {
            ratio += dicTech.param[0];
        }
    }
    return ratio;
}

// 锁定
export async function lockMineOrForestry(leagueCode: string, roleId: string, farmId: number, itemId: number) {
    let { configId } = getGVGConfig(); 
    const leagueFarms = await GVGLeagueFarmModel.findByFarmId(configId, leagueCode, farmId);       
    // 查询现有的田
    let fieldId = getUnlockedMine(farmId, leagueFarms);
    if(!fieldId) return null;

    let dicFarm = gameData.gvgResource.get(farmId);

    let lockResult = await GVGLeagueFarmModel.lockMineOrForestry(configId, leagueCode, farmId, dicFarm?.type, roleId, fieldId, itemId);
    if(!lockResult) return null;

    return lockResult;
}

function getUnlockedMine(farmId: number, leagueFarms: GVGLeagueFarmType[]) {
    let leagueFarmMap = arrToMap(leagueFarms, obj => obj.fieldId);
    let fieldId = 0;
    let sum = gameData.gvgResource.get(farmId)?.sum??0;
    for(let i = 1; i <= sum; i++) {
        if(!leagueFarmMap.has(i)) {
            fieldId = i; break;
        }
    }
    return fieldId;
}

export function checkItemCanUserInFarm(farmId: number, itemId: number) {
    let dicFarm = gameData.gvgResource.get(farmId);
    let dicGVGItem = gameData.gvgItem.get(itemId);
    if(!dicFarm || !dicGVGItem) return false;
    return dicFarm.type == dicGVGItem.type;
}

// 推送千机阁状态变化
export async function pushTechChange(guildCodes: string[], isActive: boolean, id: number, progress: number, maxProgress: number) {
    for(let guildCode of guildCodes) {
        await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.LEAGUE_TECH_CHANGE, {
            id, progress, maxProgress, isActive
        });
    }
}
