package org.black_ixx.bossshop.core;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.black_ixx.bossshop.BossShop;
import org.black_ixx.bossshop.core.BSEnums.BSBuyType;
import org.black_ixx.bossshop.core.BSEnums.BSPriceType;
import org.black_ixx.bossshop.events.BSPlayerPurchaseEvent;
import org.black_ixx.bossshop.events.BSPlayerPurchasedEvent;
import org.black_ixx.bossshop.lottery.LotteryHolder;
import org.black_ixx.bossshop.lottery.LotteryStatus;
import org.black_ixx.bossshop.mail.MailManager;
import org.black_ixx.bossshop.managers.ItemStackChecker;
import org.black_ixx.bossshop.managers.MultiplierHandler;
import org.black_ixx.bossshop.managers.RecordManager;
import org.black_ixx.bossshop.managers.ShopCustomizer;
import org.black_ixx.bossshop.managers.StringManager;
import org.black_ixx.bossshop.managers.TransactionLog;
import org.black_ixx.bossshop.managers.VaultHandler;
import org.black_ixx.bossshop.managers.WorthHandler;
import org.black_ixx.bossshop.misc.Enchant;
import org.black_ixx.bossshop.nbt.NBTEditManager;
import org.black_ixx.bossshop.sale.SaleManager;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import cc.util.bossshop.JsonExtra;
import cc.util.bossshop.NMSHelper;
import cc.util.bossshop.JsonExtra.ClickAction;
import cc.util.bossshop.JsonExtra.Color;
import cc.util.bossshop.JsonExtra.HoverAction;
import cc.util.bossshop.JsonExtra.Style;

public class BSBuy{

    private BossShop mPlugin;
    private int numberLimit=-1;
    private int personalLimit=-1;
    private int mLocation;
    private int mWeight=0;
    private int mHideItemFlag=0;
    private boolean perm_is_group=false;
    private boolean mHideNoStock=false;
    private boolean mHideNotTime=false;
    private String permission;
    private String mName;
    private String mMsg="undefine";
    private UUID owner=null;
    private Object mReward=null;
    private Object mPrice=null;
    private Date stopTime=null;
    private Date startTime=null;
    private BSShop mShop=null;
    private BSPriceType priceT=null;
    private BSBuyType buyT=null;
    private boolean mNeedEdit=true;
    
    public BSBuy(BossShop pPlugin,BSBuyParameter parameter){
        this.mPlugin=pPlugin;
        
        this.mShop=parameter.shop;
        this.priceT=parameter.priceT;
        this.buyT=parameter.buyT;
        this.mReward=parameter.reward;
        this.mPrice=parameter.price;
        this.mName=parameter.name;
        this.mMsg=transformMessage(parameter.msg);
        this.owner=parameter.owner;
        this.mLocation=parameter.location;
        this.numberLimit=parameter.numberLimit;
        this.personalLimit=parameter.personalLimit;
        this.startTime=parameter.startTime;
        this.stopTime=parameter.stopTime;
        this.mHideNoStock=parameter.hideNoStock;
        this.mHideNotTime=parameter.hideNotTime;
        this.mHideItemFlag=parameter.hideItemFlag & 0xFFFFFFFF;
        this.mWeight=parameter.weight;
        if(StringUtils.isNotEmpty(parameter.permission)){
            permission=parameter.permission;
            if(permission.startsWith("[")&&permission.endsWith("]")&&permission.length()>2){
                String group=permission.substring(1,permission.length()-1);
                if(group!=null){
                    this.permission=group;
                    perm_is_group=true;
                }
            }
        }
    }

    public int getWeight(){
        return this.mWeight;
    }
    
    /**
     * 如果为null,则使用默认
     * @return
     */
    public int getHideItemFlag(){
        return this.mHideItemFlag;
    }
    
    /**
     * 获取该商品的所有者,如果不存在则为null
     * @return
     */
    public UUID getOwner(){
        return this.owner;
    }
    
    public boolean hasOwner(){
        return this.owner!=null;
    }
    
    public BSBuyType getBuyType(){
        return buyT;
    }

    public BSPriceType getPriceType(){
        return priceT;
    }

    public boolean isHideNoStock(){
        return this.mHideNoStock;
    }
    
    public boolean isHideNotTime(){
        return this.mHideNotTime;
    }
    
    public String getShopName(){
        return this.mShop.getShopName();
    }
    
    public BSShop getShop(){
        return this.mShop;
    }
    
    public Object getReward(){
        return this.mReward;
    }

    public Object getPrice(){
        return this.mPrice;
    }

    public String getMessage(){
        return this.mMsg;
    }

    public int getInventoryLocation(){
        return this.mLocation;
    }

    public String getName(){
        return this.mName;
    }
    
    public boolean isBuyTime(){
        Date now=new Date();
        if(this.startTime!=null&&this.startTime.after(now)) return false;
        if(this.stopTime!=null&&this.stopTime.before(now)) return false;
        return true;
    }
    
    public boolean hasBuyTime(){
        return this.startTime!=null||this.stopTime!=null;
    }
    
    public Date getStartTime(){
        return this.startTime;
    }
    
    public Date getStopTime(){
        return this.stopTime;
    }
    
    public int getPersonalLimit(){
        return this.personalLimit;
    }
    
    public boolean hasPersonalLimit(){
        return this.personalLimit>-1;
    }
    
    public String C(String pNode){
        return this.mPlugin.getLangManager().getNode(pNode);
    }
    
    /**
     * 获取物品的配置节点
     */
    public ConfigurationSection getConfigurationSection(){
        return ((BSShop)mShop).getConfig().getConfigurationSection("shop."+mName);
    }

    public boolean hasPermission(Player p,boolean msg){
        if(StringUtils.isEmpty(permission)) return true;
        if(perm_is_group){
            boolean no_group=true;
            for(String group : this.mPlugin.getManager(VaultHandler.class).getPermission().getPlayerGroups(p)){
                no_group=false;
                if(group.equalsIgnoreCase(permission)){ return true; }
            }
            if(no_group&&permission.equalsIgnoreCase("default")){ return true; }
            if(msg){
                this.mPlugin.send(p,C("Main.NoPermitToBuyThisItem"));
            }
            return false;
        }
        if(p.hasPermission(permission)) return true;
        if(msg){
            this.mPlugin.send(p,C("Main.NoPermitToBuyThisItem"));
        }
        return false;
    }

    public boolean isExtraPermissionExisting(){
        if(StringUtils.isEmpty(permission)) return false;
        return true;
    }

    @SuppressWarnings("unchecked")
    public boolean alreadyBought(Player p){
        if(buyT==BSBuyType.Permission){
            for(String s : (List<String>)mReward){ // Loop durch Perms
                if(!p.hasPermission(s)){ // Sobald ein Spieler eine Perm nicht
                    return false;
                }
            }
            return true; // Hat ein Spieler alle Perms wird true returned
        }
        return false;
    }
    
    public JsonExtra getPersonalChatMenuItem(Player pPlayer,Player pOwner){
        String saleChat=BossShop.replaceParam(pOwner,C("Main.PlayerSaleItem"));
        String[] saleCharPart=saleChat.split("%item%");
        //生成Json消息
        JsonExtra itemSaleMsg=new JsonExtra(saleCharPart.length==0?"玩家"+pOwner.getName()+"上架了 ":saleCharPart[0]+" ",Color.green);
        //设置消息物品
        JsonExtra itemMsg=new JsonExtra("",Color.white);
        ShopCustomizer shopCusMan=this.mPlugin.getManager(ShopCustomizer.class);
        ItemStack perItem=shopCusMan.createPersonalMenuItem(pPlayer,this,this.mShop.getMenuItem(this.mLocation));
        String displayeItemName=perItem.getType().name();
        if(perItem.hasItemMeta()&&perItem.getItemMeta().hasDisplayName()){
            displayeItemName=perItem.getItemMeta().getDisplayName();
        }else{
            displayeItemName=displayeItemName.replace("_"," ");
            displayeItemName=displayeItemName.charAt(0)+displayeItemName.substring(1).toLowerCase();
            itemMsg.setInherit(false);
            itemMsg.setStyle(Style.bold,Style.underline);
        }
        itemMsg.setText(displayeItemName);
        itemMsg.setHoverEvent(HoverAction.show_item,NMSHelper.getItemJson(perItem));
        itemMsg.setClickEvent(ClickAction.run_command,"/BS open "+this.mShop.getShopName());
        itemSaleMsg.addExtra(itemMsg);
        
        if(saleCharPart.length>1)
            itemSaleMsg.addNormalWords(saleCharPart[1],true);//继承父节点样式
        return itemSaleMsg;
    }
    
    /**
     * 检查该用户是否能够购买该物品<br />
     * 如果不能购买,失败原因会立刻在本函数中显示给玩家
     * <p>所有检查项目包括:<br />
     * 购买事件是否通过<br />
     * 是否是购买自己的物品(只有寄售物品存在这条)<br />
     * 是否有足够的库存<br />
     * 是否已经达到个人购买上限<br />
     * 是否在购买时间<br />
     * 是否有足够的钱<br />
     * 是否已经购买过了(例如权限)<br />
     * 是否购买的物品合法(例如不适合的附魔)<br />
     * </p>
     * @return 是否能购买
     */
    public boolean canBuy(Player pPlayer,BSShopHolder pHolder){
        //抽奖检查
        if(pHolder instanceof LotteryHolder){
            LotteryHolder lHolder=(LotteryHolder)pHolder;
            LotteryStatus tStatus=lHolder.getStatus();
            if(tStatus==LotteryStatus.WAIT){
                // 此两句多余,因为getShopItem里面已经做过判断
                if(this.getBuyType()!=BSBuyType.Lottery) return false;
                if(this.getWeight()>0) return false;
            }else if(tStatus==LotteryStatus.RESULT){
//                if(!this.canBuy(pPlayer,pHolder)) return false;
//                this.stockOut(pPlayer,pHolder);
//                lHolder.resetLottery();
            }else return false;//其他情况不允许点击抽奖背包
        }
        //权限检查
        if(!this.hasPermission(pPlayer,true)) return false;
        //产生购买事件
        BSPlayerPurchaseEvent e1=new BSPlayerPurchaseEvent(pPlayer,this.mShop,this);// Custom
        Bukkit.getPluginManager().callEvent(e1);
        if(e1.isCancelled()) return false;
        
        //是不是购买自己的物品,op排除在外
        if(this.hasOwner()&&!pPlayer.isOp()){
            if(this.owner.equals(pPlayer.getUniqueId())){
                this.mPlugin.send(pPlayer,C("Main.CannotBuyYourselfGoods"));
                return false;
            }
        }
        //库存数量是否够用
        if(this.numberLimit==0){
            this.mPlugin.send(pPlayer,C("Main.AlreadySoldOut"));
            return false;
        }
        //是否已经达到个人购买上限
        if(this.personalLimit>-1){
            int nLimit=this.mPlugin.getManager(RecordManager.class).getBuyRecord(this.mShop.getShopName(),this.mName,pPlayer.getUniqueId());
            if(nLimit>=this.personalLimit){
                this.mPlugin.send(pPlayer,C("Main.OutOfPersonalLimit"));
                return false;
            }
        }
        //是否在购买时间
        Date date=new Date();
        if(this.startTime!=null&&date.before(this.startTime)){
            this.mPlugin.send(pPlayer,C("Main.GoodsNotArriveTime"));
            return false;
        }
        if(this.stopTime !=null&&date.after (this.stopTime )){
            this.mPlugin.send(pPlayer,C("Main.GoodsOutOfDate"));
            return false;
        }
        //是否有足够的钱
        try{
            if(!this.hasPrice(pPlayer)) return false;
        }catch(Exception exp){
            this.mPlugin.severe(exp.getMessage(),exp);
            this.mPlugin.send(pPlayer,C("Main.ErrorHappend"));
            return false;
        }
        //是否已经购买过了
        if(this.alreadyBought(pPlayer)){
            this.mPlugin.send(pPlayer,C("Main.AlreadyBought"));
            return false;
        }
        //购买的物品是否合法
        if(this.buyT==BSBuyType.Enchantment){
            Enchant e=(Enchant)this.mReward;
            ItemStack tItem=pPlayer.getItemInHand();
            if(tItem==null|!ItemStackChecker.isValidEnchantment(tItem,e.getType(),e.getLevel())&!this.mPlugin.getConfigManager().allowUnsafeEnchantments()){
                this.mPlugin.send(pPlayer,C("Enchantment.Invalid"));
                return false;
            }
        }
        return true;
    }
    
    /**
     * 商品出货一件,此处进行相关库存管理工作,寄售的工作
     * <p>
     * 所有的工作项目包括:<br />
     * 库存的调整<br />
     * 个人购买数量的记录<br />
     * 价格的收取<br />
     * 商品的发放<br />
     * 购买消息的发送<br />
     * 寄售物品报酬的给予<br />
     * </p>
     * @param pPlayer 出货的对象
     */
    public void stockOut(Player pPlayer,BSShopHolder pHolder){
        //收取价格
        String tLeft=this.takePrice(pPlayer);
        //给予商品
        if(pHolder instanceof LotteryHolder){
            LotteryHolder tHoler=(LotteryHolder)pHolder;
            if(this.buyT==BSBuyType.Lottery){
                //如果是乐透商品,则开始抽奖
                tHoler.startLottery();
            }else{
                //如果是普通商品,在获取商品后,关闭结果界面并回到WAIT状态
                tHoler.resetLottery();
            }
        }
        this.giveReward(pPlayer);
        //给予寄售者的寄售的利润
        if(this.hasOwner()) this.mPlugin.getManager(SaleManager.class).giveSaleReward(this);
        //如果存在数量限制,库存减1
        if(this.hasLimit()){
            if(this.numberLimit==0) throw new IllegalStateException("库存为0时,不能进行出货函数的调用,这可能是程序bug,请通知作者");
            this.numberLimit--;
            if(this.hasOwner()&&this.numberLimit==0){
                mShop.removeItem(this.mName,true);
                if(this.buyT==BSEnums.BSBuyType.Item&&this.mPlugin.getConfigManager().isAutoClearNBT())
                    this.mPlugin.getManager(NBTEditManager.class).clearNBT();
            }
            else{
                ConfigurationSection sec=getConfigurationSection();
                if(sec!=null){
                    sec.set("NumberLimit",this.getLimit());
                    this.mShop.saveConfig();
                }
            }
        }
        //如果存在个人购买数量限制,增加购买记录到文件
        if(this.hasPersonalLimit()){
            this.mPlugin.getManager(RecordManager.class).addBuyRecord(this.mShop.getShopName(),this.mName,pPlayer.getUniqueId());
        }
        //购买后消息的生成
        if(StringUtils.isNotEmpty(this.mMsg)){
            String tMsg=BossShop.replaceParam(pPlayer,this.mMsg);
            if(StringUtils.isNotEmpty(tLeft)&&tMsg.contains("%left%"))
                tMsg=tMsg.replace("%left%",tLeft);
            this.mPlugin.send(pPlayer,tMsg);
        }
        //购买记录的记录(非正式)
        if(this.mPlugin.getConfigManager().transactionLogEnabled())
            this.mPlugin.getManager(TransactionLog.class).addTransaction(pPlayer,this);
        //更新所有依据该商店生成的背包
        if(this.hasLimit()) mShop.updateAllInventory();
        else mShop.updateInventory(pHolder.getInventory(),pPlayer);
        //事件产生
        BSPlayerPurchasedEvent e2=new BSPlayerPurchasedEvent(pPlayer,mShop,this);// Custom
        Bukkit.getPluginManager().callEvent(e2);// Custom Event end
    }
    
    /**
     * 玩家是否满足显示该商品的条件
     * <p>如果商品是寄售商品,且已经卖完或或者不在售卖时间,将会调用{@link BSShop #removeItem(String, boolean)}</p>
     * @param pPlayer 玩家
     * @return 是否显示
     */
    public boolean disaplyToPlayer(Player pPlayer){
        if(!this.hasPermission(pPlayer,false)&&this.mPlugin.getConfigManager().hideNoPermitItem()) return false;
        if(this.numberLimit==0){
            if(this.hasOwner()){
                this.mShop.removeItem(this.getName(),true);
                if(this.getBuyType()==BSEnums.BSBuyType.Item&&this.mPlugin.getConfigManager().isAutoClearNBT())
                    this.mPlugin.getManager(NBTEditManager.class).clearNBT();
                return false;
            }
            if(this.isHideNoStock()) return false;
        }
        if(!this.isBuyTime()){
            if(this.hasOwner()){
                this.mShop.removeItem(this.getName(),true);
                String tMsg=BossShop.replaceParam(pPlayer,C("Mail.FromOutDateSale"));
                this.mPlugin.getManager(MailManager.class).addMail(this,tMsg);
                return false;
            }
            if(this.isHideNotTime()) return false;
        }
        return true;
    }
    
    /**
     * 获取BSBuy库存
     * @return
     */
    public int getLimit(){
        return this.numberLimit;
    }
    
    public boolean hasLimit(){
        return this.numberLimit>-1;
    }
    
    @SuppressWarnings("unchecked")
    public boolean hasPrice(Player p){
        MultiplierHandler multipMan=this.mPlugin.getManager(MultiplierHandler.class);
        WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class);
        switch(priceT){
        // EXP
        case Exp:
            // return hasExp(p, (Integer)price); old
            return wHandler.hasExp(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice));
        // Item
        case Item:
            return wHandler.hasItems(p,(List<ItemStack>)mPrice);
        // Money
        case Money:
            if(mPrice instanceof Integer){
                // return hasMoney(p, (Integer)price); old
                return wHandler.hasMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice));
            }
            // return hasMoney(p, (Integer)price); old
            return wHandler.hasMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Double)mPrice));
        // Points
        case Points:
            // return hasPoints(p, (Integer)price); old
            return wHandler.hasPoints(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice));
        // Free
        case Free:
            return true;
        // Nothing
        case Nothing:
            return true;
        }
        return false;
    }

    

    @SuppressWarnings("unchecked")
    public String takePrice(Player p){
        MultiplierHandler multipMan=this.mPlugin.getManager(MultiplierHandler.class);
        WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class);
        switch(priceT){
        // EXP
        case Exp:
            return wHandler.takeExp(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice));
        // Item
        case Item:
            return wHandler.takeItems(p,(List<ItemStack>)mPrice);
        // Money
        case Money:
            if(mPrice instanceof Integer){ return wHandler.takeMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice)); }
            return wHandler.takeMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Double)mPrice));
        // Points
        case Points:
            return wHandler.takePoints(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice));
        // Free
        case Free:
            return null;
        // Nothing
        case Nothing:
            return null;
        }
        return null;
    }
    
    @SuppressWarnings("unchecked")
    public void giveReward(Player p){
        WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class);
        switch(buyT){
        case Command:
            wHandler.giveRewardCommand(p,(List<String>)mReward);
            return;
        case PlayerCommand:
            wHandler.giveRewardPlayerCommand(p,(List<String>)mReward);
            return;
        case TimeCommand:
            wHandler.giveRewardTimeCommand(p,(HashMap<Integer,String>)mReward);
            return;
        case Item:
            wHandler.giveRewardItem(p,(List<ItemStack>)mReward);
            return;
        case Permission:
            wHandler.giveRewardPermission(p,(List<String>)mReward);
            return;
        case Money:
            if(mReward instanceof Integer){
                wHandler.giveRewardMoney(p,(Integer)mReward);
                return;
            }
            wHandler.giveRewardMoney(p,(Double)mReward);
            return;
        case Points:
            wHandler.giveRewardPoints(p,(Integer)mReward);
            return;
        case Shop:
            wHandler.giveRewardShop(p,(String)mReward);
            return;
        case Enchantment:
            wHandler.giveRewardEnchantment(p,(Enchant)mReward);
            return;
        case Lottery:
            break;
        case Nothing:
            break;
        }
    }

    /**
     * 翻译BSBuy物品的属性
     */
    public String transformMessage(String msg){
        if(mReward==null){
            mReward=1;
        }
        if(mPrice==null){
            mPrice=1;
        }
        if(msg==null||msg.length()==0) return "";
        msg=StringManager.transform(msg);
        String priceM=""+mPrice;
        if(mPrice instanceof List<?>){
            List<?> list=(List<?>)mPrice;
            if((!list.isEmpty())&&list.get(0) instanceof ItemStack){
                String m="";
                int x=0;
                for(Object i : list){
                    if(i instanceof ItemStack){
                        x++;
                        String material=((ItemStack)i).getType().name().toLowerCase();
                        material=material.replaceFirst(material.substring(0,1),material.substring(0,1).toUpperCase());
                        m+=""+((ItemStack)i).getAmount()+" "+material+(x<list.size()?", ":"");
                    }
                }
                priceM=m;
            }
        }
        String rewardM=""+mReward;
        if(mReward instanceof List<?>){
            List<?> list=(List<?>)mReward;
            if((!list.isEmpty())&&list.get(0) instanceof ItemStack){
                String m="";
                int x=0;
                for(Object i : list){
                    if(i instanceof ItemStack){
                        x++;
                        String material=((ItemStack)i).getType().name().toLowerCase();
                        material=material.replaceFirst(material.substring(0,1),material.substring(0,1).toUpperCase());
                        m+=""+((ItemStack)i).getAmount()+" "+material+(x<list.size()?", ":"");
                    }
                }
                rewardM=m;
            }
        }
        if(mName!=null&&mName!=""&&mName.length()>0){
            msg=msg.replace("%itemname%",mName);
            msg=msg.replace("%shopitemname%",mName);
        }
        if(priceT!=null&&priceT.name()!=""&&priceT.name().length()>0){
            msg=msg.replace("%pricetype%",priceT.name());
        }
        if(priceM!=null&&priceM!=""&&priceM.length()>0){
            msg=msg.replace("%price%",priceM);
        }
        if(buyT!=null&&buyT.name()!=""&&buyT.name().length()>0){
            msg=msg.replace("%rewardtype%",buyT.name());
        }
        if(rewardM!=null&&rewardM!=""&&rewardM.length()>0){
            msg=msg.replace("%reward%",rewardM);
        }
        return msg;
    }

    @Deprecated
    public void setInventoryLocation(int i){
        mLocation=i;
    }

    public void setNeedEdit(boolean enable){
        this.mNeedEdit=enable;
    }
    
    public boolean isNeedEdit(){
        return this.mNeedEdit;
    }

}


