package com.bizzan.bitrade.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bizzan.bitrade.constant.WalletType;
import com.bizzan.bitrade.dao.MemberContractWalletDao;
import com.bizzan.bitrade.entity.ContractCoin;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberContractWallet;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.entity.WalletTransRecord;
import com.bizzan.bitrade.service.Base.BaseService;
import com.bizzan.bitrade.util.InnerInvokeUtil;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.vo.PositionVO;
import com.querydsl.core.types.Predicate;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

@Service
@Slf4j
public class MemberContractWalletService extends BaseService {
    @Autowired
    private MemberContractWalletDao memberContractWalletDao;
    @Autowired
    private MemberWalletService memberWalletService;
    @Autowired
    private WalletTransRecordService walletTransRecordService;
    @Autowired
    private LocaleMessageSourceService msService;
    @Autowired
    private InnerInvokeUtil invokeUtil;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    public MemberContractWallet findOne(Long id) {
        return memberContractWalletDao.findOne(id);
    }

    public MemberContractWallet findByMemberIdAndContractCoin(Long memberId, ContractCoin contractCoin) {
        return memberContractWalletDao.findByMemberIdAndContractCoin(memberId, contractCoin);
    }
    public MemberContractWallet findNoCacheByMemberIdAndContractCoin(Long memberId, ContractCoin contractCoin) {
        return memberContractWalletDao.findNoCacheByMemberIdAndContractCoin(memberId, contractCoin.getId());
    }
    public List<MemberContractWallet> findAllWithPosition() {
        return memberContractWalletDao.findAllWithPosition();
    }
    public Page<MemberContractWallet> findAll(Predicate predicate, Pageable pageable) {
        return memberContractWalletDao.findAll(predicate, pageable);
    }
    public MemberContractWallet save(MemberContractWallet memberContractWallet){
        return memberContractWalletDao.saveAndFlush(memberContractWallet);
    }

    public List<MemberContractWallet> findAllByMember(Member member) {
        return memberContractWalletDao.findAllByMemberId(member.getId());
    }

    public List<MemberContractWallet> findAllByMemberId(Long memberId) {
        return memberContractWalletDao.findAllByMemberId(memberId);
    }
    
    public List<MemberContractWallet> findEnabledByMemberId(Long memberId) {
        return memberContractWalletDao.findEnabledByMemberId(memberId);
    }

    public BigDecimal getTotalBalanceByIds(List<Long> ids) {
    	return memberContractWalletDao.getTotalBalanceByIds(ids);
    }
    
    public void updateIsLock(Long id, int isLock) {
    	memberContractWalletDao.updateIsLock(id, isLock);
    }

    /**
     * 冻结钱包（与余额有关）
     *
     * @param memberWallet
     * @param amount
     * @return
     */
    public MessageResult freezeUsdtBalance(MemberContractWallet memberWallet, BigDecimal amount) {
        int ret = memberContractWalletDao.freezeUsdtBalance(memberWallet.getId(), amount);
        if (ret > 0) {
            return MessageResult.success();
        } else {
            return MessageResult.error("Information Expired");
        }
    }

    /**
     * 解冻钱包（与余额有关）
     *
     * @param memberWallet
     * @param amount
     * @return
     */
    public MessageResult thawUsdtBalance(MemberContractWallet memberWallet, BigDecimal amount) {
        int ret = memberContractWalletDao.thawUsdtBalance(memberWallet.getId(), amount);
        if (ret > 0) {
            return MessageResult.success();
        } else {
            return MessageResult.error("Information Expired");
        }
    }
    
    public void freezeUsdtBalance(Long memberId, ContractCoin contractCoin, BigDecimal amount) {
    	Long walletId = this.findByMemberIdAndContractCoin(memberId, contractCoin).getId();
        memberContractWalletDao.freezeUsdtBalance(walletId, amount);
    }
    
    public void thawUsdtBalance(Long memberId, ContractCoin contractCoin, BigDecimal amount) {
    	Long walletId = this.findByMemberIdAndContractCoin(memberId, contractCoin).getId();
        memberContractWalletDao.thawUsdtBalance(walletId, amount);
    }

    /**
     * 增加余额
     * @param walletId
     * @param amount
     */
    public void increaseUsdtBalance(Long walletId, BigDecimal amount){
        memberContractWalletDao.increaseUsdtBalance(walletId,amount);
    }

    /**
     * 减少余额
     * @param walletId
     * @param amount
     */
    public void decreaseUsdtBalance(Long walletId, BigDecimal amount) {
        memberContractWalletDao.decreaseUsdtBalance(walletId, amount);
    }

    /**
     * 增加做多保证金（与余额有关）
     * @param walletId
     * @param amount
     */
    public void increaseUsdtBuyPrincipalAmount(Long walletId, BigDecimal amount) {
        memberContractWalletDao.increaseUsdtBuyPrincipalAmount(walletId, amount);
    }

    /**
     * 增加做多保证金（与冻结余额有关）
     * @param walletId
     * @param amount
     */
    public void increaseUsdtBuyPrincipalAmountWithFrozen(Long walletId, BigDecimal amount) {
        memberContractWalletDao.increaseUsdtBuyPrincipalAmountWithFrozen(walletId, amount);
    }

    /**
     * 增加做空保证金（与余额有关）
     * @param walletId
     * @param amount
     */
    public void increaseUsdtSellPrincipalAmount(Long walletId, BigDecimal amount) {
        memberContractWalletDao.increaseUsdtSellPrincipalAmount(walletId, amount);
    }

    /**
     * 增加做空保证金（与冻结余额有关）
     * @param walletId
     * @param amount
     */
    public void increaseUsdtSellPrincipalAmountWithFrozen(Long walletId, BigDecimal amount) {
        memberContractWalletDao.increaseUsdtSellPrincipalAmountWithFrozen(walletId, amount);
    }

    /**
     * 减少做多保证金（与余额有关）
     * @param walletId
     * @param amount
     */
    public void decreaseUsdtBuyPrincipalAmount(Long walletId, BigDecimal amount) {
        memberContractWalletDao.decreaseUsdtBuyPrincipalAmount(walletId, amount);
    }

    /**
     * 减少做多保证金（与余额有关）
     * @param walletId
     * @param amount
     */
    public void decreaseUsdtBuyPrincipalAmountWithoutBalance(Long walletId, BigDecimal amount) {
        memberContractWalletDao.decreaseUsdtBuyPrincipalAmountWithoutBalance(walletId, amount);
    }

    /**
     * 减少做空保证金（与余额有关）
     * @param walletId
     * @param amount
     */
    public void decreaseUsdtSellPrincipalAmount(Long walletId, BigDecimal amount) {
        memberContractWalletDao.decreaseUsdtSellPrincipalAmount(walletId, amount);
    }

    /**
     * 减少做空保证金（与余额有关）
     * @param walletId
     * @param amount
     */
    public void decreaseUsdtSellPrincipalAmountWithoutBalance(Long walletId, BigDecimal amount) {
        memberContractWalletDao.decreaseUsdtSellPrincipalAmountWithoutBalance(walletId, amount);
    }
    /**
     * 增加冻结资产（与余额无关）
     * @param id
     * @param amount
     */
    public void increaseUsdtFrozen(Long id, BigDecimal amount) {
        memberContractWalletDao.increaseUsdtFrozen(id, amount);
    }

    /**
     * 减少冻结资产（与余额无关）
     * @param walletId
     * @param amount
     */
    public void decreaseUsdtFrozen(Long walletId, BigDecimal amount){
        memberContractWalletDao.decreaseUsdtFrozen(walletId,amount);
    }

    /**
     * 更新开仓价格和持仓量（多仓）
     * @param walletId
     * @param avaPrice
     * @param volume
     */
    public void updateUsdtBuyPriceAndPosition(Long walletId, BigDecimal avaPrice, BigDecimal volume, long openTime) {
        memberContractWalletDao.updateUsdtBuyPriceAndPosition(walletId, avaPrice, volume, openTime);
    }

    /**
     * 更新开仓价格和持仓量（空仓）
     * @param walletId
     * @param avaPrice
     * @param volume
     */
    public void updateUsdtSellPriceAndPosition(Long walletId, BigDecimal avaPrice, BigDecimal volume, long openTime) {
        memberContractWalletDao.updateUsdtSellPriceAndPosition(walletId, avaPrice, volume, openTime);
    }

    public void decreaseUsdtSellPositionAndPrincipalAmount(Long walletId, BigDecimal volume, BigDecimal principalAmount) {
        memberContractWalletDao.decreaseUsdtSellPositionAndPrincipalAmount(walletId, volume, principalAmount);
    }

    public void decreaseUsdtFrozenSellPositionAndPrincipalAmount(Long walletId, BigDecimal volume, BigDecimal principalAmount) {
        memberContractWalletDao.decreaseUsdtFrozenSellPositionAndPrincipalAmount(walletId, volume, principalAmount);
    }

    public void decreaseUsdtBuyPositionAndPrincipalAmount(Long walletId, BigDecimal volume, BigDecimal principalAmount) {
        memberContractWalletDao.decreaseUsdtBuyPositionAndPrincipalAmount(walletId, volume, principalAmount);
    }
    public void decreaseUsdtFrozenBuyPositionAndPrincipalAmount(Long walletId, BigDecimal volume, BigDecimal principalAmount) {
        memberContractWalletDao.decreaseUsdtFrozenBuyPositionAndPrincipalAmount(walletId, volume, principalAmount);
    }
    public void freezeUsdtSellPosition(Long walletId, BigDecimal volume) {
        memberContractWalletDao.freezeUsdtSellPosition(walletId, volume);
    }
    public void thrawUsdtSellPosition(Long walletId, BigDecimal volume) {
        memberContractWalletDao.thrawUsdtSellPosition(walletId, volume);
    }
    public void freezeUsdtBuyPosition(Long walletId, BigDecimal volume) {
        memberContractWalletDao.freezeUsdtBuyPosition(walletId, volume);
    }
    public void thrawUsdtBuyPosition(Long walletId, BigDecimal volume) {
        memberContractWalletDao.thrawUsdtBuyPosition(walletId, volume);
    }
    public void modifyUsdtBuyLeverage(Long walletId, BigDecimal leverage) {
        memberContractWalletDao.modifyUsdtBuyLeverage(walletId, leverage);
    }

    public void modifyUsdtSellLeverage(Long walletId, BigDecimal leverage) {
        memberContractWalletDao.modifyUsdtSellLeverage(walletId, leverage);
    }


    public List<MemberContractWallet> findAllNeedSync(ContractCoin contractCoin) {
        return memberContractWalletDao.findAllNeedSync(contractCoin.getId());
    }

    public void blastBuy(Long walletId) {
        memberContractWalletDao.blastBuy(walletId);
    }

    public void blastSell(Long walletId) {
        memberContractWalletDao.blastSell(walletId);
    }

    public void updateShareNumber(Long walletId, BigDecimal shareNumber) {
        memberContractWalletDao.updateShareNumber(walletId, shareNumber);
    }

    public void increaseUsdtProfit(Long walletId, BigDecimal pL) {
        memberContractWalletDao.increaseUsdtProfit(walletId, pL);
    }

    public void increaseUsdtLoss(Long walletId, BigDecimal pL) {
        memberContractWalletDao.increaseUsdtLoss(walletId, pL);
    }

    //合约调整倍率
    @Transactional(rollbackFor = Exception.class)
    public void modifyUsdtBuyAndSellLeverage(Long id, BigDecimal leverage) {
        memberContractWalletDao.updateUsdtBuyAndSellLeverage(id,leverage);
    }
    
    public BigDecimal sumBalance() {
    	BigDecimal balance = memberContractWalletDao.sumBalance();
    	if(balance == null)
    		return BigDecimal.ZERO;
    	return balance;
    }
    
    public BigDecimal sumBalanceByMemberId(Long memberId) {
    	BigDecimal balance = memberContractWalletDao.sumBalanceByMemberId(memberId);
    	if(balance == null)
    		return BigDecimal.ZERO;
    	return balance;
    }
    
    public BigDecimal sumPositionByMemberId(Long memberId) {
    	BigDecimal position = memberContractWalletDao.sumPositionByMemberId(memberId);
    	if(position == null)
    		return BigDecimal.ZERO;
    	return position;
    }
    
    public PositionVO sumPositionsByCoin(Long contractId) {
    	Object[][] obj = memberContractWalletDao.sumPositionsByCoin(contractId);
    	log.info("positions={}", obj);
    	log.info("positions.length={}", obj.length);
    	PositionVO vo = new PositionVO();
    	Object[] positions = obj[0];
    	if(positions[0]==null || positions[1]==null) {
    		vo.setBuyPosition(BigDecimal.ZERO);
    		vo.setSellPosition(BigDecimal.ZERO);
    	} else {
    		vo.setBuyPosition(BigDecimal.valueOf(Double.valueOf(positions[0].toString())));
    		vo.setSellPosition(BigDecimal.valueOf(Double.valueOf(positions[1].toString())));
    	}
    	return vo;
    }
    
    public double getBlastPrice(MemberContractWallet data) {
    	double buy_position_amount = data.getUsdtBuyPosition().doubleValue() + data.getUsdtFrozenBuyPosition().doubleValue();
    	double buy_position_money = buy_position_amount * data.getUsdtShareNumber().doubleValue();
    	double buy_minNeedPrinc = buy_position_money / data.getUsdtBuyLeverage().doubleValue();
    	double sell_position_amount = data.getUsdtSellPosition().doubleValue() + data.getUsdtFrozenSellPosition().doubleValue();
        double sell_position_money = sell_position_amount * data.getUsdtShareNumber().doubleValue();
        double sell_minNeedPrinc = sell_position_money / data.getUsdtSellLeverage().doubleValue();
    	double force_closeprice1 = (((data.getContractCoin().getMaintenanceMarginRate().doubleValue() * (buy_minNeedPrinc + sell_minNeedPrinc) -
                (data.getUsdtBuyPrincipalAmount().doubleValue() + data.getUsdtSellPrincipalAmount().doubleValue() + data.getUsdtBalance().doubleValue() + data.getUsdtFrozenBalance().doubleValue())) / data.getUsdtShareNumber().doubleValue()) - (sell_position_amount - buy_position_amount));
        double buy1 = buy_position_amount / (data.getUsdtBuyPrice().doubleValue() == 0.0 ? 1 : data.getUsdtBuyPrice().doubleValue());
        double sell1 = sell_position_amount / (data.getUsdtSellPrice().doubleValue() == 0.0 ? 1 : data.getUsdtSellPrice().doubleValue());
        double force_closeprice2 = buy1 - sell1;
        double force_closeprice = force_closeprice1 / force_closeprice2;
        if(force_closeprice < 0)
        	force_closeprice = 0.0;
    	return force_closeprice;
    }

    /**
     * 划转：币币钱包 ↔ 合约钱包 / 合约钱包之间
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult transWallet(Long memberId, String unit,
                                     WalletType from, WalletType to,
                                     Long fromWalletId, Long toWalletId,
                                     BigDecimal amount) {
        if (fromWalletId != null && fromWalletId.equals(toWalletId)) {
            return MessageResult.error(msService.getMessage("WALLET_NOT_SELECT"));
        }
        if (from != WalletType.SPOT && from != WalletType.SWAP) {
            return MessageResult.error(msService.getMessage("WALLET_NOT_OUT"));
        }
        if (to != WalletType.SPOT && to != WalletType.SWAP) {
            return MessageResult.error(msService.getMessage("WALLET_NOT_IN"));
        }

        if (from == WalletType.SPOT && to == WalletType.SWAP) {
            MemberWallet walletFrom = memberWalletService.findByCoinUnitAndMemberId(unit, memberId);
            MemberContractWallet walletTo = memberContractWalletDao.findOne(toWalletId);
            if (walletFrom == null || walletTo == null) {
                return MessageResult.error(msService.getMessage("NO_IN_OUT_WALLET"));
            }
            if (walletFrom.getBalance().compareTo(amount) < 0) {
                return MessageResult.error(msService.getMessage("WALLET_NOT_AMOUNT_OUT"));
            }
            if (unit.equals("USDT")) {
                int updated = memberWalletService.deductBalance(walletFrom, amount);
                if (updated != 1) {
                    return MessageResult.error(msService.getMessage("WALLET_NOT_AMOUNT_OUT"));
                }
                increaseUsdtBalance(walletTo.getId(), amount);
                JSONObject jsonObj = new JSONObject();
                jsonObj.put("symbol", walletTo.getContractCoin().getSymbol());
                jsonObj.put("walletId", walletTo.getId());
                kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));
            }

        } else if (from == WalletType.SWAP && to == WalletType.SPOT) {
            MemberContractWallet walletFrom = memberContractWalletDao.findOne(fromWalletId);
            if (walletFrom == null) {
                return MessageResult.error(msService.getMessage("NO_IN_OUT_WALLET"));
            }
            BigDecimal allPosition = walletFrom.getUsdtBuyPosition().add(walletFrom.getUsdtFrozenBuyPosition())
                    .add(walletFrom.getUsdtSellPosition()).add(walletFrom.getUsdtFrozenSellPosition());
            if (allPosition.compareTo(BigDecimal.ZERO) > 0) {
                return MessageResult.error("您当前持有仓位，请在平仓后再划转");
            }
            MemberWallet walletTo = memberWalletService.findByCoinUnitAndMemberId(unit, memberId);
            if (walletTo == null) {
                return MessageResult.error(msService.getMessage("NO_IN_OUT_WALLET"));
            }
            if (unit.equals("USDT")) {
                if (walletFrom.getUsdtBalance().compareTo(amount) < 0) {
                    return MessageResult.error(msService.getMessage("WALLET_NOT_AMOUNT_OUT"));
                }
                decreaseUsdtBalance(walletFrom.getId(), amount);
                memberWalletService.increaseBalance(walletTo.getId(), amount);
                JSONObject jsonObj = new JSONObject();
                jsonObj.put("symbol", walletFrom.getContractCoin().getSymbol());
                jsonObj.put("walletId", walletFrom.getId());
                kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));
            }

        } else if (from == WalletType.SWAP && to == WalletType.SWAP) {
            MemberContractWallet walletFrom = memberContractWalletDao.findOne(fromWalletId);
            MemberContractWallet walletTo = memberContractWalletDao.findOne(toWalletId);
            if (walletFrom == null || walletTo == null) {
                return MessageResult.error(msService.getMessage("NO_IN_OUT_WALLET"));
            }
            BigDecimal allPosition = walletFrom.getUsdtBuyPosition().add(walletFrom.getUsdtFrozenBuyPosition())
                    .add(walletFrom.getUsdtSellPosition()).add(walletFrom.getUsdtFrozenSellPosition());
            if (allPosition.compareTo(BigDecimal.ZERO) > 0) {
                return MessageResult.error("您当前持有仓位，请在平仓后再划转");
            }
            if (unit.equals("USDT")) {
                if (walletFrom.getUsdtBalance().compareTo(amount) < 0) {
                    return MessageResult.error(msService.getMessage("WALLET_NOT_AMOUNT_OUT"));
                }
                decreaseUsdtBalance(walletFrom.getId(), amount);
                increaseUsdtBalance(walletTo.getId(), amount);
                JSONObject jsonObj = new JSONObject();
                jsonObj.put("symbol", walletFrom.getContractCoin().getSymbol());
                jsonObj.put("walletId", walletFrom.getId());
                kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));
                JSONObject jsonObj2 = new JSONObject();
                jsonObj2.put("symbol", walletTo.getContractCoin().getSymbol());
                jsonObj2.put("walletId", walletTo.getId());
                kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj2));
            }

        } else {
            return MessageResult.error(msService.getMessage("NO_IN_OUT_WALLET"));
        }

        WalletTransRecord record = new WalletTransRecord();
        record.setAmount(amount);
        record.setUnit(unit);
        record.setSource(from);
        record.setTarget(to);
        record.setMemberId(memberId);
        walletTransRecordService.save(record);
        return MessageResult.success();
    }
}
