import { CURRENCY_TYPE, HANDLE_REWARD_TYPE, CURRENCY_BY_TYPE } from '../../consts';
import { RoleModel, RoleType } from '../../db/Role';
import { ItemModel, ItemType } from '../../db/Item';
import { ItemInter, RewardInter, } from '../../pubUtils/interface';
import { gameData } from '../../pubUtils/data';
import { sortItems } from './util';

export class CheckMeterial {
    private roleId: string;
    private itemsIndb: Map<number, number> = new Map(); // 玩家已有的东西
    private notEnoughItems: Map<number, number> = new Map(); // 缺少的数量
    private consumes: ItemInter[] = []; // 消耗，正向数
    private canReplace: boolean = false; // 是否可以被通用的啥代替代替
    private tempConsumes: ItemInter[] = [];

    private goldId = CURRENCY_BY_TYPE.get(CURRENCY_TYPE.GOLD);
    private coinId = CURRENCY_BY_TYPE.get(CURRENCY_TYPE.COIN);

    constructor(roleId: string, role?: RoleType, items?: ItemType[]) {
        this.roleId = roleId;
        if(role) {
            this.itemsIndb.set(this.goldId, role.gold);
            this.itemsIndb.set(this.coinId, role.coin);
        }
        if(items && items.length) {
            for(let {id, count} of items) {
                this.itemsIndb.set(id, count);
            }
        }
    }

    private pushToNotEnoughItems(id: number, count: number) {
        if(!this.notEnoughItems.has(id)) {
            this.notEnoughItems.set(id, 0);
        }
        this.notEnoughItems.set(id, this.notEnoughItems.get(id) + count);
    }

    private getNotEnoughItems() {
        let map = new Map<number, number>();
        for(let [ id, count ] of this.notEnoughItems) {
            map.set(id, count);
        }
        return map;
    }

    public async decrease(goods: {id: number, count: number}[]) {
        this.tempConsumes.splice(0, this.tempConsumes.length);
        this.notEnoughItems.clear();
        let { items, gold, coin } = sortItems(goods, HANDLE_REWARD_TYPE.COST);
        for(let { id, count} of items) {
            console.log('#####', id, count)
            let notEnoughCount = await this.decreaseItem(id, count);
            console.log('##### notEnoughCount', id, notEnoughCount, this.canReplace)
            if(notEnoughCount > 0) {
                let isEnough = await this.checkReplaceItem(id, notEnoughCount);
                if(isEnough) {
                    this.tempConsumes.push({ id, count: count - notEnoughCount });
                } else {
                    return false;
                }
            }
        }

        if(gold.length > 0) {
            let isEnough = await this.decreaseGold(gold);
            if(!isEnough) return false;
        }
        if(coin.length > 0) {
            let isEnough = await this.decreaseCoin(coin);
            if(!isEnough) return false;
        }
        this.consumes.push(...this.tempConsumes);
        return true;
    }

    public setCanReplace(canReplace: boolean) {
        this.canReplace = canReplace;
    }

    private async decreaseItem(id: number, count: number) {
        console.log('#### decreaseItem 1', id, count);
        if(!this.itemsIndb.has(id)) {
            let item = await ItemModel.findbyRoleAndGid(this.roleId, id);
            console.log('#### decreaseItem', item);
            if(!item) {
                this.pushToNotEnoughItems(id, count);
                return count;
            }
            this.itemsIndb.set(id, item.count);
        }
        if(this.itemsIndb.get(id) < count) {
            let notEnoughCount = count - this.itemsIndb.get(id);
            this.pushToNotEnoughItems(id, notEnoughCount);
            this.itemsIndb.set(id, 0);
            return notEnoughCount;
        } else {
            this.itemsIndb.set(id, this.itemsIndb.get(id) - count);
            this.tempConsumes.push({ id, count });
            return 0
        }
    }

    private async checkReplaceItem(id: number, count: number) {
        if(!this.canReplace) return false;

        let relationGoodId = gameData.relationGoods.get(id);
        if(!relationGoodId) return false;

        if(relationGoodId == this.goldId) {
            return await this.decreaseGold([{ count }]);
        } else if (relationGoodId == this.coinId) {
            return await this.decreaseCoin([count]);
        } else {
            let cnt = await this.decreaseItem(relationGoodId, count);
            return cnt == 0
        }
    }

    private async decreaseGold(gold: { count: number }[]) {

        if(!this.itemsIndb.has(this.goldId)) {
            let role = await RoleModel.findByRoleId(this.roleId, 'gold coin');
            this.itemsIndb.set(this.goldId, role.gold);
            this.itemsIndb.set(this.coinId, role.coin);
        }
        let goldCost = gold.reduce((pre, cur) => { return pre + cur.count }, 0);
        if(this.itemsIndb.get(this.goldId) < goldCost) {
            this.pushToNotEnoughItems(this.goldId, goldCost - this.itemsIndb.get(this.goldId));
            this.itemsIndb.set(this.goldId, 0);
            return false;
        }
        this.itemsIndb.set(this.goldId, this.itemsIndb.get(this.goldId) - goldCost);
        this.tempConsumes.push({ id: this.goldId, count: goldCost });
        return true
    }

    private async decreaseCoin(coin: number[]) {

        if(!this.itemsIndb.has(this.coinId)) {
            let role = await RoleModel.findByRoleId(this.roleId, 'gold coin');
            this.itemsIndb.set(this.goldId, role.gold);
            this.itemsIndb.set(this.coinId, role.coin);
        }
        let coinCost = coin.reduce((pre, cur) => pre + cur, 0);
        if(this.itemsIndb.get(this.coinId) < coinCost) {
            this.pushToNotEnoughItems(this.coinId, coinCost - this.itemsIndb.get(this.coinId));
            this.itemsIndb.set(this.coinId, 0);
            return false;
        }
        this.itemsIndb.set(this.coinId, this.itemsIndb.get(this.coinId) - coinCost);
        this.tempConsumes.push({ id: this.coinId, count: coinCost });
        return true;
    }

    private getMaterialEnough(materials: RewardInter[], notEnoughItems: Map<number, number>) {
        let newMaterials: RewardInter[] = [];
        for(let {id, count} of materials) {
            if(notEnoughItems.has(id)) {
                newMaterials.push({ id, count: count - notEnoughItems.get(id) });
            } else {
                newMaterials.push({ id, count });
            }
        }
        return newMaterials;
    }

    // 检查地玉石是否可以合成
    public async composeStone(id: number, count: number) {
        let dicStone = gameData.stone.get(id);
        if(!dicStone || dicStone.composeMaterial.length <= 0) return false; // 1阶石头不能再从下合成返回不行
        let materials = dicStone.composeMaterial.map(cur => ({...cur, count: cur.count * count }));
        let isEnough = await this.decrease(materials);
        if(!isEnough) {
            let notEnoughItems = this.getNotEnoughItems();
            let newMaterials = this.getMaterialEnough(materials, notEnoughItems);
            let isEnough = await this.decrease(newMaterials); // 消耗掉除了不足的部分以外的其他部分
            if(!isEnough) return false;

            let isAllOK = true; // 如果有石头不足，向下补充，isAllOK标识不足的都能补充上
            for(let [id, count] of notEnoughItems) {
                let isEnough = await this.composeStone(id, count);
                if(!isEnough) { 
                    isAllOK = false; break;
                }
            }
            return isAllOK;
        } else {
            return true;
        }
    }

    public getConsume() {
        return this.consumes;
    }
}