package com.bizzan.bitrade.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bizzan.bitrade.constant.BooleanEnum;
import com.bizzan.bitrade.constant.NotifyType;
import com.bizzan.bitrade.constant.PageModel;
import com.bizzan.bitrade.constant.TransactionType;
import com.bizzan.bitrade.constant.WithdrawStatus;
import com.bizzan.bitrade.dao.WithdrawRecordDao;
import com.bizzan.bitrade.entity.*;
import com.bizzan.bitrade.es.ESUtils;
import com.bizzan.bitrade.pagination.Criteria;
import com.bizzan.bitrade.pagination.PageListMapResult;
import com.bizzan.bitrade.pagination.PageResult;
import com.bizzan.bitrade.pagination.Restrictions;
import com.bizzan.bitrade.service.Base.BaseService;
import com.bizzan.bitrade.util.HttpClientUtil;
import com.bizzan.bitrade.util.InnerInvokeUtil;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.vo.WithdrawAddressControlVo;
import com.bizzan.bitrade.vo.WithdrawControlVo;
import com.bizzan.bitrade.vo.WithdrawRecordVO;
import com.bizzan.bitrade.vo.WithdrawTransferControlVo;
import com.querydsl.core.types.*;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

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

import static com.bizzan.bitrade.constant.BooleanEnum.IS_FALSE;
import static com.bizzan.bitrade.constant.WithdrawStatus.*;
import static com.bizzan.bitrade.entity.QWithdrawRecord.withdrawRecord;
import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notNull;

/**
 * @author 
 * @date 2020年01月29日
 */
@Service
@Slf4j
public class WithdrawRecordService extends BaseService {
    private Logger logger = LoggerFactory.getLogger(WithdrawRecordService.class);
    @Autowired
    private WithdrawRecordDao withdrawApplyDao;
    @Autowired
    private MemberWalletService walletService;
    @Autowired
    private CoinService coinService;
    @Autowired
    private MemberTransactionService transactionService;
    @Autowired
    ESUtils esUtils;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private MemberWalletService memberWalletService;
    @Autowired
    private MemberService memberService;

    public WithdrawRecord save(WithdrawRecord withdrawApply) {
        return withdrawApplyDao.save(withdrawApply);
    }

    @Override
    public List<WithdrawRecord> findAll() {
        return withdrawApplyDao.findAll();
    }

    public WithdrawRecord findOne(Long id) {
        return withdrawApplyDao.findOne(id);
    }
    
    public long countAll() {
        return withdrawApplyDao.count();
    }

    /**
     * 条件查询对象
     *
     * @param predicateList
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Transactional(readOnly = true)
    public PageResult<WithdrawRecord> query(List<Predicate> predicateList, Integer pageNo, Integer pageSize) {
        List<WithdrawRecord> list;
        JPAQuery<WithdrawRecord> jpaQuery = queryFactory.selectFrom(withdrawRecord);
        if (predicateList != null) {
            jpaQuery.where(predicateList.toArray(new Predicate[predicateList.size()]));
        }
        if (pageNo != null && pageSize != null) {
            list = jpaQuery.offset((pageNo - 1) * pageSize).limit(pageSize).fetch();
        } else {
            list = jpaQuery.fetch();
        }
        return new PageResult<>(list, jpaQuery.fetchCount());
    }

    @Transactional(readOnly = true)
    public void test() {
        //查询字段
        List<Expression> expressions = new ArrayList<>();
        expressions.add(QWithdrawRecord.withdrawRecord.memberId.as("memberId"));
        //查询表
        List<EntityPath> entityPaths = new ArrayList<>();
        entityPaths.add(QWithdrawRecord.withdrawRecord);
        entityPaths.add(QMember.member);
        //查询条件
        List<Predicate> predicates = new ArrayList<>();
        predicates.add(QWithdrawRecord.withdrawRecord.memberId.eq(QMember.member.id));
        //排序
        List<OrderSpecifier> orderSpecifierList = new ArrayList<>();
        orderSpecifierList.add(QWithdrawRecord.withdrawRecord.id.desc());
        PageListMapResult pageListMapResult = super.queryDslForPageListResult(expressions, entityPaths, predicates, orderSpecifierList, 1, 10);
        System.out.println(pageListMapResult);

    }

    /**
     * 修改 根据withdrawApply.id[]修改withdrawApply的WithdrawStatus
     *
     * @param ids    withdrawApply.id[]
     * @param status WithdrawStatus
     */
    @Transactional(rollbackFor = Exception.class)
    public void audit(Long[] ids, WithdrawStatus status, String remark, boolean autoWithdraw) {
        WithdrawRecord withdrawRecord;
        for (Long id : ids) {
            //20	4.70000000	0	2020-02-27 17:47:37		0.30000000	0	28	0	5.00000000			GalaxyChain
            withdrawRecord = withdrawApplyDao.findOne(id);
            //确认提现申请存在
            notNull(withdrawRecord, "不存在");
        	
            //确认订单状态是审核中
            if(status == WAITING)
            	isTrue(withdrawRecord.getStatus() == PROCESSING, "id为" + id + "不是审核状态的提现");
            //确认提现类型不是自动提现
            isTrue(withdrawRecord.getIsAuto() == IS_FALSE, "id为" + id + "不是人工审核提现");
            //审核
            if (status == FAIL) {
            	isTrue(withdrawRecord.getStatus()==WithdrawStatus.PROCESSING || withdrawRecord.getStatus()==WithdrawStatus.WAITING, "id为" + id + "已成功提现");
                //审核不通过
            	String finalUnit;
            	MemberWallet wallet;
            	if(withdrawRecord.getCoin().getUnit().equals("EUSDT")||withdrawRecord.getCoin().getUnit().equals("TUSDT")||withdrawRecord.getCoin().getUnit().equals("USDT-BEP20")) {
            		finalUnit = "USDT";
            		wallet = walletService.findByCoinAndMemberId(coinService.findByUnit(finalUnit), withdrawRecord.getMemberId());
            		//wallet = InnerInvokeUtil.fineWallet(withdrawRecord.getMemberId(), "USDT");
            	} else {
            		finalUnit = withdrawRecord.getCoin().getUnit();
            		wallet = walletService.findByCoinAndMemberId(withdrawRecord.getCoin(), withdrawRecord.getMemberId());
            		//wallet = InnerInvokeUtil.fineWallet(withdrawRecord.getMemberId(), withdrawRecord.getCoin().getUnit());
            	}
                notNull(wallet, "wallet null!");
                //wallet.setBalance(wallet.getBalance().add(withdrawRecord.getTotalAmount()));
                //wallet.setFrozenBalance(wallet.getFrozenBalance().subtract(withdrawRecord.getTotalAmount()));
                //walletService.save(wallet);
                walletService.thawBalance(wallet.getId(), withdrawRecord.getTotalAmount());
                kafkaTemplate.send("trading-account-change", withdrawRecord.getMemberId()+"");
                //invokeUtil.thawBalance(finalUnit, withdrawRecord.getMemberId(), withdrawRecord.getTotalAmount());
            } else {
            	if(autoWithdraw) {
            		log.info("autoWithdraw={}", withdrawRecord);
            		kafkaTemplate.send("auto-withdraw", JSON.toJSONString(withdrawRecord));
            		
            	}
            	//isTrue(InnerInvokeUtil.validateWithdrawAddress(withdrawRecord.getMemberId(),withdrawRecord.getCoin().getUnit(),withdrawRecord.getId(),withdrawRecord.getAddress()), "申请记录异常，请联系管理员");
            }
            withdrawRecord.setRemark(remark);
            withdrawRecord.setStatus(status);
            withdrawRecord.setDealTime(new Date());
            withdrawApplyDao.save(withdrawRecord);
        }
    }
    
    @Transactional
    public void autoWithdrawSuccess(Long withdrawId, String txid) {
    	WithdrawRecord record = withdrawApplyDao.findOne(withdrawId);
    	if(record.getStatus() == WithdrawStatus.SUCCESS) {
    		log.info("提币已是成功状态={}", record);
    		return;
    	}
        record.setTransactionNumber(txid);
        record.setStatus(WithdrawStatus.SUCCESS);
        Coin coin = record.getCoin();
        if(coin.getUnit().equals("TUSDT")||coin.getUnit().equals("EUSDT")||coin.getUnit().equals("USDT-BEP20")) {
        	coin = coinService.findByUnit("USDT");
        }
        MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(coin, record.getMemberId());
        int modifiedCount = memberWalletService.decreaseFrozen(memberWallet.getId(), record.getTotalAmount());

    	log.info("modifiedCount={}",modifiedCount);
    	if(modifiedCount <= 0)
        	return;
    	
    	kafkaTemplate.send("trading-account-change", memberWallet.getMemberId()+"");
        
        record = withdrawApplyDao.save(record);
        
    	net.sf.json.JSONObject jsonObject = net.sf.json.JSONObject.fromObject(record);
    	jsonObject.put("unit", coin.getUnit());
    	jsonObject.put("orderId", withdrawId);
    	jsonObject.put("arrivedAmount", record.getArrivedAmount());
    	jsonObject.put("address", record.getAddress());
    	jsonObject.put("transactionNumber", record.getTransactionNumber());
    	memberService.notifyByEmail(record.getMemberId(), NotifyType.WITHDRAW, jsonObject);

    	log.info("MemberTransaction start");
        MemberTransaction memberTransaction = new MemberTransaction();
        memberTransaction.setMemberId(record.getMemberId());
        memberTransaction.setAddress(record.getAddress());
        memberTransaction.setAmount(BigDecimal.ZERO.subtract(record.getTotalAmount()));
        memberTransaction.setSymbol(coin.getUnit());
        memberTransaction.setCreateTime(record.getCreateTime());
        memberTransaction.setType(TransactionType.WITHDRAW);
        memberTransaction.setFee(record.getFee());
        memberTransaction.setRealFee(record.getFee()+"");
        memberTransaction.setDiscountFee("0");
        memberTransaction= transactionService.save(memberTransaction);
    	log.info("MemberTransaction end");
    }
    
    @Transactional
    public void autoWithdrawFailure(Long withdrawId, String memo) {
    	WithdrawRecord withdrawRecord = withdrawApplyDao.findOne(withdrawId);
    	MemberWallet wallet = walletService.findByCoinAndMemberId(coinService.findByUnit("USDT"), withdrawRecord.getMemberId());
    	walletService.thawBalance(wallet.getId(), withdrawRecord.getTotalAmount());
    	
    	kafkaTemplate.send("trading-account-change", wallet.getMemberId()+"");
    	
    	withdrawRecord.setRemark(memo);
        withdrawRecord.setStatus(WithdrawStatus.FAIL);
        withdrawRecord.setDealTime(new Date());
        withdrawApplyDao.save(withdrawRecord);
    }

    /**
     * 提现成功处理
     *
     * @param withdrawId
     * @param txid
     */
    @Transactional
    public void withdrawSuccess(Long withdrawId, String txid) {
        WithdrawRecord record = findOne(withdrawId);
        if (record != null) {
            record.setTransactionNumber(txid);
            record.setStatus(WithdrawStatus.SUCCESS);
            MemberWallet wallet = walletService.findByCoinUnitAndMemberId(record.getCoin().getUnit(), record.getMemberId());
            if (wallet != null) {
                wallet.setFrozenBalance(wallet.getFrozenBalance().subtract(record.getTotalAmount()));
                MemberTransaction transaction = new MemberTransaction();
                transaction.setAmount(BigDecimal.ZERO.subtract(record.getTotalAmount()));
                transaction.setSymbol(wallet.getCoin().getUnit());
                transaction.setAddress(wallet.getAddress());
                transaction.setMemberId(wallet.getMemberId());
                transaction.setType(TransactionType.WITHDRAW);
                transaction.setFee(record.getFee());
                transaction.setDiscountFee("0");
                transaction.setRealFee(record.getFee()+"");
                transaction.setCreateTime(new Date());
                transaction = transactionService.save(transaction);

            }
        }
    }

    /**
     * 提现失败处理
     *
     * @param withdrawId
     */
    @Transactional
    public void withdrawFail(Long withdrawId) {
        WithdrawRecord record = findOne(withdrawId);
        if (record == null || record.getStatus() != WithdrawStatus.PROCESSING) {
            return;
        }
        MemberWallet wallet = walletService.findByCoinAndAddress(record.getCoin(), record.getAddress());
        if (wallet != null) {
            wallet.setBalance(wallet.getBalance().add(record.getTotalAmount()));
            wallet.setFrozenBalance(wallet.getFrozenBalance().subtract(record.getTotalAmount()));
            record.setStatus(WithdrawStatus.FAIL);
        }
    }

    /**
     * 自动转币失败，转为人工处理
     *
     * @param withdrawId
     */
    public void autoWithdrawFail(Long withdrawId) {
        WithdrawRecord record = findOne(withdrawId);
        if (record == null || record.getStatus() != WithdrawStatus.WAITING) {
            return;
        }
        logger.info("================  自动转币失败，转为人工处理  ========================");
        logger.info("================ setIsAuto : " + BooleanEnum.IS_FALSE.getNameCn() + "  ========================");
        logger.info("================ setStatus : " + WithdrawStatus.PROCESSING.getCnName() + "  ========================");
        record.setIsAuto(BooleanEnum.IS_FALSE);
        record.setStatus(WithdrawStatus.PROCESSING);
        withdrawApplyDao.save(record);

    }

    @Transactional(readOnly = true)
    public Page<WithdrawRecord> findAllByMemberId(Long memberId, int page, int pageSize) {
        Sort orders = Criteria.sortStatic("id.desc");
        PageRequest pageRequest = new PageRequest(page, pageSize, orders);
        Criteria<WithdrawRecord> specification = new Criteria<WithdrawRecord>();
        specification.add(Restrictions.eq("memberId", memberId, false));
        return withdrawApplyDao.findAll(specification, pageRequest);
    }

    @Transactional(readOnly = true)
    public WithdrawRecord findById(Long Id) {
        return withdrawApplyDao.findOne(Id);
    }
    
    public int countProcessingByMemberId(Long memberId) {
    	return withdrawApplyDao.countProcessingByMemberId(memberId);
    }

    public Page<WithdrawRecordVO> joinFind(List<Predicate> predicates, PageModel pageModel) {
        List<OrderSpecifier> orderSpecifiers = pageModel.getOrderSpecifiers();
        JPAQuery<WithdrawRecordVO> query = queryFactory.select(
                Projections.fields(WithdrawRecordVO.class,
                        QWithdrawRecord.withdrawRecord.id.as("id"),
                        QWithdrawRecord.withdrawRecord.memberId.as("memberId"),
                        QWithdrawRecord.withdrawRecord.coin.unit,
                        QMember.member.username.as("memberUsername"),
                        QMember.member.realName.as("memberRealName"),
                        QMember.member.mobilePhone.as("phone"),
                        QMember.member.email,
                        QWithdrawRecord.withdrawRecord.dealTime.as("dealTime"),
                        QWithdrawRecord.withdrawRecord.totalAmount.as("totalAmount"),
                        QWithdrawRecord.withdrawRecord.arrivedAmount.as("arrivedAmount"),
                        QWithdrawRecord.withdrawRecord.status,
                        QWithdrawRecord.withdrawRecord.isAuto.as("isAuto"),
                        QWithdrawRecord.withdrawRecord.address,
                        QWithdrawRecord.withdrawRecord.createTime.as("createTime"),
                        QWithdrawRecord.withdrawRecord.fee,
                        QWithdrawRecord.withdrawRecord.transactionNumber.as("transactionNumber"),
                        QWithdrawRecord.withdrawRecord.remark)
        ).from(QWithdrawRecord.withdrawRecord, QMember.member).where(predicates.toArray(new BooleanExpression[predicates.size()]));
        query.orderBy(orderSpecifiers.toArray(new OrderSpecifier[orderSpecifiers.size()]));
        List<WithdrawRecordVO> list = query.offset((pageModel.getPageNo() - 1) * pageModel.getPageSize()).limit(pageModel.getPageSize()).fetch();
        long total = query.fetchCount();
        return new PageImpl<>(list, pageModel.getPageable(), total);

    }

    public long countAuditing(){
        return withdrawApplyDao.countAllByStatus(WithdrawStatus.PROCESSING);
    }
    
    public List<WithdrawControlVo> statControl(String startTime, String endTime) {
    	List<WithdrawControlVo> list = new ArrayList<>();
    	Object[][] objArray = withdrawApplyDao.statControl(startTime, endTime);
    	for (Object[] obj : objArray) {
    		WithdrawControlVo vo = new WithdrawControlVo();
        	if(obj[0]!=null)
        		vo.setMemberId(Long.parseLong(obj[0].toString()));
        	if(obj[1]!=null)
        		vo.setSmallCount(Integer.parseInt(obj[1].toString()));
        	if(obj[2]!=null)
        		vo.setSmallAmount(BigDecimal.valueOf(Double.parseDouble(obj[2].toString())));
        	list.add(vo);
    	}
    	return list;
    }
    
    public List<WithdrawAddressControlVo> statAddressControl(String startTime, String endTime) {
    	List<WithdrawAddressControlVo> list = new ArrayList<>();
    	Object[][] objArray = withdrawApplyDao.statAddressControl(startTime, endTime);
    	for (Object[] obj : objArray) {
    		WithdrawAddressControlVo vo = new WithdrawAddressControlVo();
        	if(obj[0]!=null)
        		vo.setMemberId(Long.parseLong(obj[0].toString()));
        	if(obj[1]!=null)
        		vo.setAddressCount(Integer.parseInt(obj[1].toString()));
        	list.add(vo);
    	}
    	return list;
    }
    
    public List<WithdrawTransferControlVo> statTransferControl(String startTime, String endTime) {
    	List<WithdrawTransferControlVo> list = new ArrayList<>();
    	Object[][] objArray = withdrawApplyDao.statTransferControl(startTime, endTime);
    	for (Object[] obj : objArray) {
    		WithdrawTransferControlVo vo = new WithdrawTransferControlVo();
        	if(obj[0]!=null)
        		vo.setMemberId(Long.parseLong(obj[0].toString()));
        	if(obj[1]!=null)
        		vo.setAddressCount(Integer.parseInt(obj[1].toString()));
        	list.add(vo);
    	}
    	return list;
    }
}
