package com.copy.trade.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.copy.trade.constant.DogeApi;
import com.copy.trade.constant.KafkaTopic;
import com.copy.trade.constant.RedisKey;
import com.copy.trade.dao.CustomerMapper;
import com.copy.trade.dao.MessageMapper;
import com.copy.trade.entity.dto.CustomerMessage;
import com.copy.trade.entity.form.TraderForm;
import com.copy.trade.entity.form.TraderQueryForAdminForm;
import com.copy.trade.entity.po.Customer;
import com.copy.trade.entity.po.Follow;
import com.copy.trade.entity.po.Message;
import com.copy.trade.entity.po.Position;
import com.copy.trade.entity.po.TraderData;
import com.copy.trade.entity.po.TraderLevel;
import com.copy.trade.entity.po.TraderSymbol;
import com.copy.trade.entity.vo.CustomerVo;
import com.copy.trade.entity.vo.TraderTotalVo;
import com.copy.trade.enums.FollowStatusEnum;
import com.copy.trade.enums.MessageTypeEnum;
import com.copy.trade.enums.TraderEnum;
import com.copy.trade.service.ICustomerFollowSymbolService;
import com.copy.trade.service.ICustomerService;
import com.copy.trade.service.IFavoriteService;
import com.copy.trade.service.IFollowService;
import com.copy.trade.service.IMessageService;
import com.copy.trade.service.IPositionService;
import com.copy.trade.service.IStatisticsDataService;
import com.copy.trade.service.ITraderDataService;
import com.copy.trade.service.ITraderLevelService;
import com.copy.trade.service.ITraderSymbolService;
import com.copy.trade.service.ITraderTagService;
import com.copy.trade.util.DogeContractUtils;
import com.copy.trade.util.LocalDateUtils;
import com.copy.trade.util.RedisUtils;
import com.copy.trade.util.WebAssert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author wz
 * @since 2022-11-08
 */
@Service
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements ICustomerService {

    private final Integer AUTOMATIC_UNFOLLOW_DAYS = 14;
    
    @Value("${doge.host_inner}")
    private String dogeHost;
    
    @Autowired
    private ITraderLevelService traderLevelServiceImpl;

    @Autowired
    private IFollowService followServiceImpl;

    @Autowired
    private IPositionService positionServiceImpl;
    
    @Autowired
    private KafkaTemplate kafkaTemplate;
    
    @Autowired
    private RedisUtils redisUtils;
    
    @Autowired
    private IFavoriteService favoriteServiceImpl;
    
    @Autowired
    private ITraderDataService traderDataServiceImpl;
    
    @Autowired
    private ICustomerFollowSymbolService customerFollowSymbolServiceImpl;
    
    @Autowired
    private ITraderSymbolService traderSymbolServiceImpl;
    
    @Autowired
    private ITraderTagService traderTagServiceImpl;
    
    @Autowired
    private IMessageService messageServiceImpl;
    
    @Autowired
    private IStatisticsDataService statisticsDataServiceImpl;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String FOLLOW_SPECIAL_AMOUNT_KEY = "FOLLOW_SPECIAL_AMOUNT";

    @Override
    //@Cacheable(cacheNames = "customer")
    public Customer getByMemberId(Long memberId) {
        QueryWrapper<Customer> customerQueryWrapper = new QueryWrapper<>();
        customerQueryWrapper.eq("member_id",memberId);
        Customer customerDB = getOne(customerQueryWrapper);
        return customerDB;
    }

    @Override
    public Boolean approvalTrader(Long traderId,Long levelId) {
        Customer trader = getById(traderId);
        WebAssert.isNotNull(trader,"不存在的交易员");
       WebAssert.isTrue(trader.getTrader().equals(TraderEnum.APPLYING_TRADER.getStatus()),"用户还未申请");
       trader.setTrader(TraderEnum.TRADER.getStatus());
        TraderLevel level = traderLevelServiceImpl.getById(levelId);
        WebAssert.isNotNull(level,"不存在的等级");
        trader.setTraderLevelId(levelId);
        trader.setTraderTime(new Date());
        
        TraderSymbol traderSymbol = new TraderSymbol();
        traderSymbol.setSymbol("BTC/USDT");
        traderSymbol.setTraderId(traderId);
        traderSymbol.setContractSymbolId(1L);
        traderSymbolServiceImpl.save(traderSymbol);
        TraderSymbol traderSymbol2 = new TraderSymbol();
        traderSymbol2.setSymbol("ETH/USDT");
        traderSymbol2.setTraderId(traderId);
        traderSymbol2.setContractSymbolId(2L);
        traderSymbolServiceImpl.save(traderSymbol2);
        
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("memberId", trader.getMemberId());
        jsonObject.put("isTrader", levelId);
        kafkaTemplate.send("trader-status-change", JSON.toJSONString(jsonObject));
        
        return updateById(trader);
    }

    @Override
    public Boolean reject(Long traderId) {
        Customer trader = getById(traderId);
        WebAssert.isNotNull(trader,"不存在的交易员");
        WebAssert.isTrue(trader.getTrader().equals(TraderEnum.APPLYING_TRADER.getStatus()),"用户还未申请");
        //trader.setTrader(TraderEnum.REJECTION.getStatus());
        trader.setTrader(TraderEnum.NO_TRADER.getStatus());
        return updateById(trader);
    }

    @Override
    public Boolean automaticUnfollow() {
        LocalDateTime now = LocalDateTime.now();
        //获取14天前的时间戳
        LocalDateTime minus = LocalDateUtils.minus(now, AUTOMATIC_UNFOLLOW_DAYS, ChronoUnit.DAYS);
        long time = LocalDateUtils.getStartTimeOfDay(minus);
        //1.获取开启取消自动取消跟随的用户
        QueryWrapper<Customer> customerQueryWrapper = new QueryWrapper<>();
        customerQueryWrapper.eq("trader",TraderEnum.NO_TRADER.getStatus())
                .eq("automatic_unfollow",1);
        List<Customer> customerList = list(customerQueryWrapper);
        customerList.forEach(customer -> {
            //2.查询用户跟随交易员
            List<Follow> followList = followServiceImpl.getByCustomerId(customer.getId());
            followList.forEach(follow -> {
               //3.查询交易员交易14有无持仓记录
                QueryWrapper<Position> positionQueryWrapper = new QueryWrapper<>();
                positionQueryWrapper.eq("customer_id",follow.getTraderId())
                        .eq("trader_id",follow.getTraderId())  
                        .ge("open_time",time);
                QueryWrapper<Position> positionQueryWrapperAll = new QueryWrapper<>();
                positionQueryWrapperAll.eq("customer_id",follow.getTraderId())
                        .eq("trader_id",follow.getTraderId());
                
                int countPositionAll = positionServiceImpl.count(positionQueryWrapperAll);
                int countPosition = positionServiceImpl.count(positionQueryWrapper);
                //跟随时间是否超过14天
                Boolean timeFlag = (new Date().getTime() - follow.getUpdatedTime().getTime()) > AUTOMATIC_UNFOLLOW_DAYS * 24* 3600000;
                //取消关注的条件:1,老师超过14天没有开仓,14天前开过,且跟随时间超过14天;   2,老师一直没有开仓过,跟随时间超过14天也取消关注
                if((countPosition == 0 && countPositionAll > 0 && timeFlag) || (countPositionAll == 0 && timeFlag) ){
	                //调用取消关注方法
	                followServiceImpl.unfollow(follow, FollowStatusEnum.UNFOLLOW);
                }
            });
        });
        return true;
    }

    @Override
    public List<Customer> traderList() {
        QueryWrapper<Customer> customerQueryWrapper = new QueryWrapper<>();
        customerQueryWrapper.eq("trader",TraderEnum.TRADER.getStatus());
        return list(customerQueryWrapper);
    }

	@Override
	public Page<Customer> applyList(Page page) {
		// TODO Auto-generated method stub
		List<Customer> list = ((CustomerMapper)baseMapper).listByTraderStatus(page, TraderEnum.APPLYING_TRADER.getStatus());
        page.setRecords(list);
        return page;
	}

	@Override
	public Page<CustomerVo> traderList(TraderQueryForAdminForm traderQueryForAdminForm) {
		Page page = traderQueryForAdminForm.newFormPage();
		List<CustomerVo> list = ((CustomerMapper)baseMapper).traderList(page, traderQueryForAdminForm);
		list.forEach(customerVo -> {
			Integer followerCount = followServiceImpl.followerCount(customerVo.getId());
			customerVo.setFollowCustomer(followerCount == null ? 0L : followerCount.longValue());
			Integer unfollowCount = followServiceImpl.followerCountByStatus(customerVo.getId(), FollowStatusEnum.UNFOLLOW);
			customerVo.setCancelFollowerCustomer(unfollowCount == null ? 0 : unfollowCount);
			Integer removeCount = followServiceImpl.followerCountByStatus(customerVo.getId(), FollowStatusEnum.REMOVE);
			customerVo.setRemoveFollowerCustomer(removeCount == null ? 0 : removeCount);
		});
		page.setRecords(list);
        return page;
	}
	
	@Override
	public Page<TraderTotalVo> traderTotalList(TraderQueryForAdminForm traderQueryForAdminForm) {
		Page page = traderQueryForAdminForm.newFormPage();
		List<TraderTotalVo> list = ((CustomerMapper)baseMapper).traderTotalList(page, traderQueryForAdminForm);
		list.forEach(traderTotalVo -> {
			
			
			BigDecimal balance = DogeContractUtils.asset(dogeHost + DogeApi.CONTRACT_ASSET_NEW_STUDENT,traderTotalVo.getMemberId());
			traderTotalVo.setBalance(balance);
			//总分润
			Position position = new Position();
	    	position.setTraderId(traderTotalVo.getId());
	    	BigDecimal dividendFeeAll = positionServiceImpl.selectDividendFee(position);
			traderTotalVo.setTotalFollowProfit(dividendFeeAll);
            //总手续费
			BigDecimal commission = positionServiceImpl.selectCommission(position);
			traderTotalVo.setCommission(commission);
			
			//跟单团队余额
			List<Long> followering = followServiceImpl.followering(traderTotalVo.getId());
			traderTotalVo.setFollowing((followering == null && followering.size() > 0) ? 0 : followering.size());
			if(traderTotalVo.getFollowing() > 0) {
				BigDecimal followTeamBalance = DogeContractUtils.assetAll(dogeHost + DogeApi.CONTRACT_ASSET_NEW_STUDENT,followering);
				traderTotalVo.setFollowTeamBalance(followTeamBalance);
			}
			
			if(followering != null && followering.size() > 0) {
				TraderTotalVo followInOutStudent = DogeContractUtils.getFollowInOut(dogeHost + DogeApi.FOLLOW_IN_OUT,followering);
				traderTotalVo.setInAmount(followInOutStudent.getInAmount());
				traderTotalVo.setOutAmount(followInOutStudent.getOutAmount());
			}
			
			followering.clear();
			followering.add(traderTotalVo.getMemberId());
			TraderTotalVo followInOutTeacher = DogeContractUtils.getFollowInOut(dogeHost + DogeApi.FOLLOW_IN_OUT,followering);
			traderTotalVo.setUsername(followInOutTeacher.getUsername());
			
			traderTotalVo.setLevelName(traderTotalVo.getLevelName() + "交易员");
		});
		
		page.setRecords(list);
		return page;
	}

	@Override
	public Boolean cancelTrader(Long traderId) {
		// TODO Auto-generated method stub
		Customer customerDB = this.getById(traderId);
    	WebAssert.isNotNull(customerDB,"chk.copy-trade.null");
    	Integer traderStatus = customerDB.getTrader();
    	
    	//验证是否是交易员
    	WebAssert.isTrue(TraderEnum.TRADER.getStatus() == traderStatus, "chk.cancelTrader.not.trader");
    	
    	Integer positionCount = positionServiceImpl.getPositionCount(customerDB.getId());
    	if(positionCount != null && positionCount > 0) {
    		WebAssert.isTrue(positionCount == 0, "chk.cancelTrader.has.position");
    	}
    	
    	// 修改用户表的字段
    	//customerDB.setTrader(TraderEnum.NO_TRADER.getStatus());
    	//customerDB.setTraderLevelId(null);
    	//customerDB.setTraderTime(null);
    	//updateById(customerDB);  updateById这一方法不能对字段置空.
        ((CustomerMapper) baseMapper).cancelTrader(customerDB.getId());

    	
    	//删除跟随表
    	Map<String, Object> followMap = new HashMap();
    	followMap.put("trader_id", customerDB.getId());
    	followServiceImpl.removeByMap(followMap);
    	//关注表
    	Map<String, Object> favoriteMap = new HashMap();
    	favoriteMap.put("trader_id", customerDB.getId());
    	favoriteServiceImpl.removeByMap(favoriteMap);
    	//交易员标签表
    	Map<String, Object> tagMap = new HashMap();
    	tagMap.put("trader_id", customerDB.getId());
    	traderTagServiceImpl.removeByMap(tagMap);
    	//交易员带单合约
    	Map<String, Object> traderSymbolMap = new HashMap();
    	traderSymbolMap.put("trader_id", customerDB.getId());
    	traderSymbolServiceImpl.removeByMap(traderSymbolMap);
    	//交易员统计数据
    	Map<String, Object> statisticsMap = new HashMap();
    	statisticsMap.put("customer_id", customerDB.getId());
    	statisticsDataServiceImpl.removeByMap(statisticsMap);
    	Map<String, Object> traderDataMap = new HashMap();
    	traderDataMap.put("trader_id", customerDB.getId());
    	traderDataServiceImpl.removeByMap(traderDataMap);
    	
    	//用户跟随交易对
    	Map<String, Object> customerFollowSymbolMap = new HashMap();
    	customerFollowSymbolMap.put("trader_id", customerDB.getId());
    	customerFollowSymbolServiceImpl.removeByMap(customerFollowSymbolMap);
    	
    	JSONObject jsonObject = new JSONObject();
        jsonObject.put("memberId", customerDB.getMemberId());
        jsonObject.put("isTrader", 0);
        kafkaTemplate.send("trader-status-change", JSON.toJSONString(jsonObject));
    	
    	return true;
	}

	@Override
	public Boolean updateTraderLevelId(Long traderId, Long traderLevelId) {
		Customer customer = getById(traderId);
		JSONObject jsonObject = new JSONObject();
        jsonObject.put("memberId", customer.getMemberId());
        jsonObject.put("isTrader", traderLevelId);
        kafkaTemplate.send("trader-status-change", JSON.toJSONString(jsonObject));
		return ((CustomerMapper)baseMapper).updateTraderLevelId(traderId, traderLevelId);
	}

	@Override
	public Boolean updateTraderTop(Long traderId, Integer isTop) {
		return ((CustomerMapper)baseMapper).updateTraderTop(traderId, isTop);
	}
	
	@Override
	public Boolean updateTraderParams(Long traderId, TraderForm traderForm) {
		Customer customer = getById(traderId);
		Boolean paramsFlag = false;
		boolean clearMaxPrincipalAmount = false;
		if(traderForm.getMinFollowAmount() != null) {
			customer.setMinFollowAmount(traderForm.getMinFollowAmount());
			paramsFlag = true;
		}
		if(traderForm.getDefaultFollowCustomer() != null) {
			customer.setDefaultFollowCustomer(traderForm.getDefaultFollowCustomer());
			paramsFlag = true;
		}
		if(traderForm.getEditDataSwitch() != null) {
			paramsFlag = true;
			TraderData traderData = traderDataServiceImpl.getByTraderId(traderId);
			if(traderForm.getEditDataSwitch() && traderData == null) {
				traderData = new TraderData();
				traderData.setTraderId(traderId);
				traderDataServiceImpl.save(traderData);
			}
			customer.setEditDataSwitch(traderForm.getEditDataSwitch());
			
		}
		if(traderForm.getDefaultRegisterDays() != null) {
			paramsFlag = true;
			customer.setDefaultRegisterDays(traderForm.getDefaultRegisterDays());
		}
		if(traderForm.getMaxPrincipalAmount() != null) {
			BigDecimal maxPrincipalAmount = traderForm.getMaxPrincipalAmount();
			// 前端传 0 视为清空该限制，避免后续跟单金额判断被 0 全部拦截
			if (maxPrincipalAmount.compareTo(BigDecimal.ZERO) == 0) {
				customer.setMaxPrincipalAmount(null);
				clearMaxPrincipalAmount = true;
			} else {
				String limitStr = stringRedisTemplate.opsForValue().get(FOLLOW_SPECIAL_AMOUNT_KEY);
				if (limitStr != null && !limitStr.trim().isEmpty()) {
					BigDecimal limit = new BigDecimal(limitStr.trim());
					WebAssert.isTrue(
							maxPrincipalAmount.compareTo(limit) <= 0,
							"最高跟随金额不能超过系统配置上限 " + limit.toPlainString()
					);
				}
				customer.setMaxPrincipalAmount(maxPrincipalAmount);
			}
			paramsFlag = true;
		}
		WebAssert.isTrue(paramsFlag,"chk.trade.params.not.null");
		boolean updated = updateById(customer);
		if (updated && clearMaxPrincipalAmount) {
			return ((CustomerMapper) baseMapper).clearMaxPrincipalAmount(traderId);
		}
		return updated;
	}

	@Override
	public Boolean synchronousTraderInfor() {
		List<Customer> traderList = traderList();
		for (Customer customer : traderList) {
			synchronousInformation(customer.getMemberId() + "",customer.getNickname(),customer.getAvatar());
		}
		return true;
	}
	
	private void synchronousInformation(String memberId,String nickname,String avatar) {
		CustomerMessage customerMessage = new CustomerMessage();
		customerMessage.setMemberId(memberId);
		customerMessage.setNickname(nickname);
		customerMessage.setAvatar(avatar);
		
			kafkaTemplate.send(KafkaTopic.SYNCHRONOUS_INFORMATION, JSON.toJSONString(customerMessage));
		
	}
}
