package com.bizzan.bitrade.service;

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

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

import com.alibaba.fastjson.JSON;
import com.bizzan.bitrade.constant.BooleanEnum;
import com.bizzan.bitrade.constant.WalletType;
import com.bizzan.bitrade.dao.FollowWalletDao;
import com.bizzan.bitrade.entity.ContractCoin;
import com.bizzan.bitrade.entity.FollowAccount;
import com.bizzan.bitrade.entity.FollowWallet;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.entity.WalletTransRecord;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.util.RedisManager;
import com.querydsl.core.types.Predicate;

import org.springframework.kafka.core.KafkaTemplate;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class FollowWalletService implements WalletService {
	@Autowired
	private FollowWalletDao dao;
	@Autowired
	private MemberTransactionService transactionService;
	@Autowired
	private MemberService memberService;
	@Autowired
	private ContractCoinService contractCoinService;
	@Autowired
	private SysConfigService sysConfigService;
    @Autowired
    private MemberContractWalletService memberContractWalletService;
	@Autowired
	private RedisManager redisManager;
    @Autowired
    private MemberWalletService memberWalletService;
	@Autowired
	private RedisLock redisLock;
	@Autowired
	private WalletTransRecordService walletTransRecordService;
	@Autowired
	private KafkaTemplate<String, String> kafkaTemplate;

	@Transactional(rollbackFor = Exception.class)
	public void transferIn(Long id, BigDecimal amount) {
		dao.increaseBalance(id, amount);
	}
	@Transactional(rollbackFor = Exception.class)
	public boolean transferOut(Long id, BigDecimal amount) {
		return dao.decreaseBalance(id, amount) > 0;
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void clearOpenFee(Long id, ContractCoin coin, BigDecimal margin, BigDecimal openFee) {
		dao.clearOpenFee(id, margin, openFee);
		//contractCoinService.increaseTotalCloseFee(coin.getId(), openFee);
		
		transactionService.recordFollowFee(id, coin.getBaseSymbol(), openFee, 0);
		
		//memberContractWalletService.thawUsdtBalance(id, coin, margin.add(openFee));
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void clearOpenFee(Long id, ContractCoin coin, BigDecimal margin, BigDecimal openFee, Long time) {
		dao.clearOpenFee(id, margin, openFee);
		//contractCoinService.increaseTotalCloseFee(coin.getId(), openFee);
		
		transactionService.recordFollowFee(id, coin.getBaseSymbol(), openFee, 0, new Date(time));
		
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void clearContractCloseFee(Long id, ContractCoin coin, BigDecimal amount) {
		dao.clearCloseFee(id, amount);
		//contractCoinService.increaseTotalCloseFee(coin.getId(), amount);
		
		transactionService.recordFollowFee(id, coin.getBaseSymbol(), amount, 1);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void clearProfitOrLoss(Long id, ContractCoin coin, BigDecimal amount) {
		dao.increaseBalance(id, amount);
		//if(amount.compareTo(BigDecimal.ZERO)>0)
		//	contractCoinService.increaseTotalProfit(coin.getId(), amount);
		//else
		//	contractCoinService.increaseTotalLoss(coin.getId(), BigDecimal.ZERO.subtract(amount));
		
		transactionService.recordFollowProfitOrLoss(id, coin.getBaseSymbol(), amount);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void clearFollowDividend(Long id, Long traderId, ContractCoin coin, BigDecimal amount) {
		MemberWallet memberWallet = memberWalletService.findByCoinUnitAndMemberId("USDT", traderId);
		memberWalletService.increaseBalance(memberWallet.getId(), amount);
		redisManager.removeTradingAccount(traderId);

		dao.increaseBalance(id, BigDecimal.ZERO.subtract(amount));
		transactionService.recordFollowDividend(id, coin.getBaseSymbol(), BigDecimal.ZERO.subtract(amount));
		transactionService.recordFollowDividend(traderId, coin.getBaseSymbol(), amount);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void clearContractOvernightFee(Long id, ContractCoin coin, BigDecimal amount) {
		int count = dao.clearContractOvernightFee(id, amount);
		//contractCoinService.increaseTotalCloseFee(coin.getId(), amount);
		if(count!=0)
			transactionService.recordFollowOvernightFee(id, coin.getBaseSymbol(), amount);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public boolean freezeMarginAndFee(Long id, ContractCoin contractCoin, BigDecimal amount) {
		Member member = memberService.findOne(id);
		if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return false;
        }
		if (member.getIsMoneyLock()!=null && member.getIsMoneyLock()==1) {
            return false;
        }

		int successCount = dao.freezeMargin(id, amount);
		if(successCount == 0)
			return false;

		//memberContractWalletService.freezeUsdtBalance(id, contractCoin, amount);
		return true;
	}

	/**
	 * 批量直接开仓专用：balance -= (principal+fee)，frozen_balance += principal，跳过 frozen_margin 中转。
	 * 同时记录开仓手续费流水。
	 */
	@Transactional(rollbackFor = Exception.class)
	public boolean freezeMarginDirectly(Long id, ContractCoin coin, BigDecimal principal, BigDecimal fee) {
		return freezeMarginDirectly(id, coin, principal, fee, null);
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean freezeMarginDirectly(Long id, ContractCoin coin, BigDecimal principal, BigDecimal fee, Long openTime) {
		// 批量跟单开仓路径由 copy-trade 保证只下发活跃用户，跳过 member 全量查询以减少热路径 DB 次数
		BigDecimal required = principal.add(fee);
		int result = dao.freezeBalanceDirect(id, required, principal);
		if (result == 0) {
			FollowWallet wallet = dao.findOne(id);
			log.warn("【批量开仓】保证金不足, memberId={}, 应冻结={}, 实际余额={}",
					id, required, wallet != null ? wallet.getBalance() : "查询失败");
			return false;
		}
		if (openTime != null) {
			transactionService.recordFollowFee(id, coin.getBaseSymbol(), fee, 0, new Date(openTime));
		} else {
			transactionService.recordFollowFee(id, coin.getBaseSymbol(), fee, 0);
		}
		return true;
	}
	@Transactional(rollbackFor = Exception.class)
	public void cancelTrade(Long id, BigDecimal openFee) {
		dao.cancelTrade(id, openFee);
	}
	
	//解冻保证金
	@Transactional(rollbackFor = Exception.class)
	public void thawMargin(Long id, ContractCoin contractCoin, BigDecimal amount) {
		dao.thawMargin(id, amount);
		
		//memberContractWalletService.thawUsdtBalance(id, contractCoin, amount);
	}
	
	//释放已使用保证金
	@Transactional(rollbackFor = Exception.class)
	public void releaseMargin(Long id, BigDecimal amount) {
		dao.releaseMargin(id, amount);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void increaseBalance(Long id, BigDecimal amount) {
		dao.increaseBalance(id, amount);
	}
	
	//跟随特级
	@Transactional(rollbackFor = Exception.class)
	public boolean followSpecial(Long id) {
		return dao.followSpecial(id, BigDecimal.valueOf(sysConfigService.get().getFollowSpecialAmount())) == 1;
	}
	//退出跟单时释放所有残留 frozenMargin
	@Transactional(rollbackFor = Exception.class)
	public void releaseAllFrozenMargin(Long id) {
		dao.releaseAllFrozenMargin(id);
	}

	//停止跟随特级
	@Transactional(rollbackFor = Exception.class)
	public boolean unfollowSpecial(Long id) {
		if(!redisLock.lockSpecialFollowAccount(id))
			return false;
		
		try {
			return dao.unfollowSpecial(id) == 1;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return false;
		} finally {
			redisLock.unlockSpecialFollowAccount(id);
		}
	}
	
	public FollowWallet findOne(Long id) {
		FollowWallet entity = dao.findOne(id);
		if(entity == null) {
			entity = new FollowWallet();
			entity.setId(id);
			entity.setBalance(BigDecimal.ZERO);
			entity.setFrozenBalance(BigDecimal.ZERO);
			entity.setFrozenMargin(BigDecimal.ZERO);
			entity.setSpecialBalance(BigDecimal.ZERO);
			entity.setSpecialFrozenBalance(BigDecimal.ZERO);
			entity.setSpecialFrozenMargin(BigDecimal.ZERO);
			entity.setLeverage(BigDecimal.TEN);
			dao.save(entity);
		}
		return entity;
	}
	
	public ConcurrentHashMap<Long, FollowAccount> findAllWithPositions() {
		ConcurrentHashMap<Long, FollowAccount> accountMap = new ConcurrentHashMap<>();
		List<FollowWallet> list = dao.findAllWithPositions();
		for(FollowWallet userPurse: list) {
			FollowAccount vo = new FollowAccount();
			BeanUtils.copyProperties(userPurse, vo);
			//vo.setBlastMarginRate(sysConfigService.get().getContractMarginRate());
			//vo.setWalletList(memberContractWalletService.findEnabledByMemberId(userPurse.getId()));
			accountMap.put(vo.getId(), vo);
		}
		return accountMap;
	}
	
	public FollowAccount findOneVO(Long id) {
		FollowWallet entity = findOne(id);
		FollowAccount vo = new FollowAccount();
		BeanUtils.copyProperties(entity, vo);
		vo.setBlastMarginRate(sysConfigService.get().getContractMarginRate());
		vo.setWalletList(memberContractWalletService.findEnabledByMemberId(id));
		//vo.setTodayRevenue(this.getTodayRevenue(id));
		return vo;
	}
	
	public FollowAccount findOneVOFromRedis(Long id) {
		FollowAccount account = findOneVO(id);
		redisManager.saveFollowAccount(id, JSON.toJSONString(account));
		return account;
	}
	
	public List<FollowWallet> findByIds(List<Long> ids) {
		return dao.findByIds(ids);
	}
	
	public void deleteVOFromRedis(Long id) {
		redisManager.removeFollowAccount(id);
	}
	
	public BigDecimal getBalanceById(Long id) {
		return dao.getBalanceById(id);
	}
	
	public void updatePercent(Long id, BigDecimal profitPercent, BigDecimal lossPercent) {
		dao.updatePercent(id, profitPercent, lossPercent);
	}
	
	public Page<FollowWallet> findAll(Predicate predicate, Pageable pageable) {
		return dao.findAll(predicate, pageable);
	}
	
	public BigDecimal sumBalance() {
		return dao.sumBalance();
	}
	
	public BigDecimal sumBalanceByMemberIds(List<Long> ids) {
		return dao.sumBalanceByMemberIds(ids);
	}
	
	public long countWithBalanceByMemberIds(List<Long> ids) {
		return dao.countWithBalanceByMemberIds(ids);
	}
	@Override
	public void clearContractOvernightFee(Long id, ContractCoin coin, BigDecimal amount, BigDecimal rebateFee) {
		// TODO Auto-generated method stub
		int count = dao.clearContractOvernightFee(id, amount.subtract(rebateFee));
		//contractCoinService.increaseTotalCloseFee(coin.getId(), amount);
		if(count!=0) {
			transactionService.recordFollowOvernightFee(id, coin.getBaseSymbol(), amount);
			if(rebateFee.compareTo(BigDecimal.ZERO)!=0)
				transactionService.recordFollowOvernightFee(id, coin.getBaseSymbol(), BigDecimal.ZERO.subtract(rebateFee));
		}
	}

    /**
     * 资金账户划入跟单账户
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult transferInFromSpot(Long memberId, BigDecimal amount) {
        MemberWallet spotWallet = memberWalletService.findByCoinUnitAndMemberId("USDT", memberId);
        if (spotWallet == null) {
            return MessageResult.error("币币钱包不存在");
        }
        if (spotWallet.getBalance().compareTo(amount) < 0) {
            return MessageResult.error("币币账户余额不足");
        }
        int updated = memberWalletService.deductBalance(spotWallet, amount);
        if (updated != 1) {
            return MessageResult.error("币币账户扣款失败");
        }
        dao.increaseBalance(memberId, amount);

        WalletTransRecord record = new WalletTransRecord();
        record.setAmount(amount);
        record.setUnit("USDT");
        record.setSource(WalletType.SPOT);
        record.setTarget(WalletType.FOLLOW);
        record.setMemberId(memberId);
        walletTransRecordService.save(record);

        kafkaTemplate.send("follow-account-change", memberId.toString());
        kafkaTemplate.send("trading-account-change", memberId.toString());
        return MessageResult.success();
    }

    /**
     * 跟单账户划出到资金账户
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult transferOutToSpot(Long memberId, BigDecimal amount) {
        FollowWallet followWallet = findOne(memberId);
        if (followWallet == null) {
            return MessageResult.error("跟单账户不存在");
        }
        if (followWallet.getCurrentPositionList() != null && !followWallet.getCurrentPositionList().isEmpty()) {
            return MessageResult.error("存在持仓，无法划转");
        }
        if (followWallet.getBalance().compareTo(amount) < 0) {
            return MessageResult.error("跟单账户余额不足");
        }
        boolean ok = dao.decreaseBalance(memberId, amount) > 0;
        if (!ok) {
            return MessageResult.error("跟单账户扣款失败");
        }
        MemberWallet spotWallet = memberWalletService.findByCoinUnitAndMemberId("USDT", memberId);
        if (spotWallet == null) {
            return MessageResult.error("币币钱包不存在");
        }
        memberWalletService.increaseBalance(spotWallet.getId(), amount);

        WalletTransRecord record = new WalletTransRecord();
        record.setAmount(amount);
        record.setUnit("USDT");
        record.setSource(WalletType.FOLLOW);
        record.setTarget(WalletType.SPOT);
        record.setMemberId(memberId);
        walletTransRecordService.save(record);

        kafkaTemplate.send("follow-account-change", memberId.toString());
        kafkaTemplate.send("trading-account-change", memberId.toString());
        return MessageResult.success();
    }
}
