package com.bizzan.bitrade.service;

import static com.bizzan.bitrade.util.BigDecimalUtils.sub;

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

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

import com.bizzan.bitrade.constant.TransactionType;
import com.bizzan.bitrade.constant.WithdrawStatus;
import com.bizzan.bitrade.dao.TransferRecordDao;
import com.bizzan.bitrade.entity.Coin;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberTransaction;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.entity.TransferRecord;
import com.bizzan.bitrade.pagination.Criteria;
import com.bizzan.bitrade.pagination.Restrictions;
import com.bizzan.bitrade.service.Base.BaseService;
import com.bizzan.bitrade.vo.TransferRecordVo;
import com.querydsl.core.types.Predicate;

/**
 * @author 
 * @date 2020年02月27日
 */
@Service
public class TransferRecordService extends BaseService {
    @Autowired
    private TransferRecordDao transferRecordDao;
    @Autowired
    private MemberService memberService;
    @Autowired
    private MemberWalletService memberWalletService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private MemberTransactionService memberTransactionService ;

    public TransferRecord save(TransferRecord transferRecord){
        return transferRecordDao.save(transferRecord);
    }
    
    public TransferRecord findOne(Long id){
        return transferRecordDao.findOne(id);
    }

    @Transactional(readOnly = true)
    public Page<TransferRecord> findAllByMemberId(Long memberId, int page, int pageSize) {
        Sort orders = Criteria.sortStatic("id.desc");
        PageRequest pageRequest = new PageRequest(page, pageSize, orders);
        Criteria<TransferRecord> specification = new Criteria<>();
        specification.add(Restrictions.eq("memberId", memberId, false));
        return transferRecordDao.findAll(specification, pageRequest);
    }
    
    public Page<TransferRecord> findAll(Predicate predicate, Pageable pageable) {
        return transferRecordDao.findAll(predicate, pageable);
    }
    
    public List<TransferRecordVo> stat(Long memberId) {
    	return transferRecordDao.stat(memberId);
    }
    
    public BigDecimal sumTotalByTime(String startTime, String endTime) {
    	BigDecimal total =  transferRecordDao.sumTotalByTime(startTime, endTime);
    	if(total!=null)
    		return total;
    	else
    		return BigDecimal.ZERO;
    }
    
    /**
     * 站内转账（直接到账）：扣减发送方余额、增加接收方余额、写双边流水、保存转账记录，全部在同一事务中执行。
     */
    @Transactional(rollbackFor = Exception.class)
    public TransferRecord doDoubleTransfer(Coin coin, MemberWallet fromWallet, Member targetMember,
                                           BigDecimal amount, String address, String remark, Long depositId) {
        BigDecimal fee = coin.getWithdrawFeeValue();
        BigDecimal targetAmount = sub(amount, fee);

        // 1. 扣减发送方余额
        int deducted = memberWalletService.decreaseBalance(fromWallet.getId(), amount);
        if (deducted <= 0) {
            throw new RuntimeException("INSUFFICIENT_BALANCE");
        }
        kafkaTemplate.send("trading-account-change", fromWallet.getMemberId() + "");

        // 2. 增加接收方余额
        MemberWallet targetWallet = memberWalletService.findByCoinAndMemberId(coin, targetMember.getId());
        memberWalletService.increaseBalance(targetWallet.getId(), targetAmount);
        kafkaTemplate.send("trading-account-change", targetMember.getId() + "");

        // 3. 写发送方流水
        MemberTransaction memberTransaction = new MemberTransaction();
        memberTransaction.setMemberId(fromWallet.getMemberId());
        memberTransaction.setAddress(address);
        memberTransaction.setAmount(BigDecimal.ZERO.subtract(amount));
        memberTransaction.setSymbol(coin.getUnit());
        memberTransaction.setCreateTime(new Date());
        memberTransaction.setType(TransactionType.TRANSFER_ACCOUNTS);
        memberTransaction.setFee(fee);
        memberTransaction.setRealFee(fee + "");
        memberTransaction.setDiscountFee("0");
        memberTransactionService.save(memberTransaction);

        // 4. 写接收方流水
        MemberTransaction targetMemberTransaction = new MemberTransaction();
        targetMemberTransaction.setMemberId(targetMember.getId());
        targetMemberTransaction.setAddress(targetMember.getUsername());
        targetMemberTransaction.setAmount(targetAmount);
        targetMemberTransaction.setSymbol(coin.getUnit());
        targetMemberTransaction.setCreateTime(new Date());
        targetMemberTransaction.setType(TransactionType.TRANSFER_ACCOUNTS);
        targetMemberTransaction.setFee(BigDecimal.ZERO);
        targetMemberTransaction.setRealFee("0");
        targetMemberTransaction.setDiscountFee("0");
        memberTransactionService.save(targetMemberTransaction);

        // 5. 保存转账记录
        TransferRecord transferRecord = new TransferRecord();
        transferRecord.setAmount(amount);
        transferRecord.setCoin(coin);
        transferRecord.setMemberId(fromWallet.getMemberId());
        transferRecord.setFee(fee);
        transferRecord.setAddress(address);
        transferRecord.setTargetMemberId(targetMember.getId());
        transferRecord.setRemark(remark);
        transferRecord.setDealTime(new Date());
        transferRecord.setStatus(WithdrawStatus.SUCCESS);
        if (depositId != null) {
            transferRecord.setDepositId(depositId);
        }
        return transferRecordDao.save(transferRecord);
    }

    @Transactional(rollbackFor = Exception.class)
    public void audit(Long[] ids, WithdrawStatus status, String remark) {
    	for (Long id : ids) {
    		TransferRecord record = this.findOne(id);
    		
    		if(status == WithdrawStatus.SUCCESS) {
    			MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(record.getCoin(), record.getMemberId());
    			memberWalletService.decreaseFrozen(memberWallet.getId(), record.getAmount());
    			kafkaTemplate.send("trading-account-change", record.getMemberId()+"");
    			
    			BigDecimal fee = record.getFee();
    	        BigDecimal targetAmount = sub(record.getAmount(), fee);
    	        MemberWallet targetMemberWallet = memberWalletService.findByCoinAndMemberId(record.getCoin(), record.getTargetMemberId());
    	        memberWalletService.increaseBalance(targetMemberWallet.getId(), targetAmount);
    	        kafkaTemplate.send("trading-account-change", record.getTargetMemberId()+"");
    	        
    	        MemberTransaction memberTransaction = new MemberTransaction();
    	        memberTransaction.setMemberId(record.getMemberId());
    	        memberTransaction.setAddress(record.getAddress());
    	        memberTransaction.setAmount(BigDecimal.ZERO.subtract(record.getAmount()));
    	        memberTransaction.setSymbol(record.getCoin().getUnit());
    	        memberTransaction.setCreateTime(new Date());
    	        memberTransaction.setType(TransactionType.TRANSFER_ACCOUNTS);
    	        memberTransaction.setFee(fee);
    	        memberTransaction.setRealFee(fee+"");
    	        memberTransaction.setDiscountFee("0");
    	        memberTransactionService.save(memberTransaction);
    	        
    	        Member targetMember = memberService.findOne(record.getTargetMemberId());
    	        MemberTransaction targetMemberTransaction = new MemberTransaction();
    	        targetMemberTransaction.setMemberId(targetMember.getId());
    	        targetMemberTransaction.setAddress(targetMember.getUsername());
    	        targetMemberTransaction.setAmount(targetAmount);
    	        targetMemberTransaction.setSymbol(record.getCoin().getUnit());
    	        targetMemberTransaction.setCreateTime(new Date());
    	        targetMemberTransaction.setType(TransactionType.TRANSFER_ACCOUNTS);
    	        targetMemberTransaction.setFee(BigDecimal.ZERO);
    	        targetMemberTransaction.setRealFee("0");
    	        targetMemberTransaction.setDiscountFee("0");
    	        memberTransactionService.save(targetMemberTransaction);
    	        
    		} else if(status == WithdrawStatus.FAIL) {
    			MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(record.getCoin(), record.getMemberId());
    			memberWalletService.thawBalance(memberWallet.getId(), record.getAmount());
    			kafkaTemplate.send("trading-account-change", record.getMemberId()+"");
    		}

    		record.setRemark(remark);
    		record.setStatus(status);
    		record.setDealTime(new Date());
    		this.save(record);
    	}
    }
}
