package com.bizzan.bitrade.controller.swap;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.bizzan.bitrade.annotation.AccessLog;
import com.bizzan.bitrade.constant.AdminModule;
import com.bizzan.bitrade.constant.PageModel;
import com.bizzan.bitrade.constant.TransactionType;
import com.bizzan.bitrade.entity.Admin;
import com.bizzan.bitrade.entity.ContractOrderEntrust;
import com.bizzan.bitrade.entity.FollowAccount;
import com.bizzan.bitrade.entity.FollowWallet;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberContractPosition;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.entity.QFollowWallet;
import com.bizzan.bitrade.entity.QUserPurse;
import com.bizzan.bitrade.entity.UserPurse;
import com.bizzan.bitrade.model.screen.FollowAccountScreen;
import com.bizzan.bitrade.model.screen.TradingAccountScreen;
import com.bizzan.bitrade.model.vo.FollowAccountVo;
import com.bizzan.bitrade.model.vo.TradingAccountVo;
import com.bizzan.bitrade.service.ContractOrderEntrustService;
import com.bizzan.bitrade.service.FollowWalletService;
import com.bizzan.bitrade.service.MemberContractPositionService;
import com.bizzan.bitrade.service.MemberService;
import com.bizzan.bitrade.service.MemberTransactionService;
import com.bizzan.bitrade.service.MemberWalletService;
import com.bizzan.bitrade.service.RedisLock;
import com.bizzan.bitrade.service.SysConfigService;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.util.PredicateUtils;
import com.bizzan.bitrade.vo.MemberContractPositionWithCut;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.sparkframework.security.Encrypt;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

@Api(description="跟单账户接口")
@RestController
@RequestMapping("/follow/account")
@Slf4j
public class FollowAccountController {

	@Autowired
    private RestTemplate restTemplate;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private MemberService memberService;
    @Autowired
	private SysConfigService sysConfigService;
    @Autowired
    private MemberWalletService memberWalletService;
    @Autowired
    private ContractOrderEntrustService contractOrderEntrustService;
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private FollowWalletService followWalletService;
    @Autowired
	private MemberContractPositionService positionService;
    @Autowired
    private MemberTransactionService memberTransactionService;
    @Autowired
    private RedisLock redisLock;
    
    @ApiOperation(value="获取跟单账户列表", notes="获取跟单账户列表")
	@RequiresPermissions("swap:account:page-query")
    @PostMapping("page-query")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "跟单账户列表")
	public MessageResult<Page<FollowAccountVo>> list(PageModel pageModel,
			FollowAccountScreen screen) {
		log.info("【跟单账户】收到请求 page-query, pageNo={}, pageSize={}, memberId={}, phone={}, email={}, hasPosition={}",
				pageModel.getPageNo(), pageModel.getPageSize(), screen.getMemberId(), screen.getPhone(), screen.getEmail(), screen.getHasPosition());
		String serviceName = "contract-swap-api";
        String marketUrl = "http://" + serviceName + "/swap/symbol-price";

        ParameterizedTypeReference<Map<String, BigDecimal>> typeRef = new ParameterizedTypeReference<Map<String, BigDecimal>>() {};
        ResponseEntity<Map<String, BigDecimal>> responseEntity = restTemplate.exchange(marketUrl, HttpMethod.POST, new HttpEntity<>(null), typeRef);
        Map<String, BigDecimal> currentPriceMap =responseEntity.getBody();
        
        if (pageModel.getDirection() == null && pageModel.getProperty() == null) {
            ArrayList<Sort.Direction> directions = new ArrayList<>();
            directions.add(Sort.Direction.ASC);
            pageModel.setDirection(directions);
            List<String> property = new ArrayList<>();
            property.add("id"); // 默认金额排序
            pageModel.setProperty(property);
        }
        
        Predicate predicate = getPredicate(screen);
        
        Page<FollowWallet> all = followWalletService.findAll(predicate, pageModel.getPageable());
        List<FollowWallet> followWalletList = all.getContent();
		List<FollowAccountVo> retList = new ArrayList<FollowAccountVo>();
		for(FollowWallet followWallet: followWalletList) {
			FollowAccountVo vo = new FollowAccountVo();
			BeanUtils.copyProperties(followWallet, vo);
			vo.setBlastMarginRate(sysConfigService.get().getContractMarginRate());
			//vo.setWalletList(memberContractWalletService.findAllByMemberId(userPurse.getId()));
			vo.setCashBalance(memberWalletService.sumBalanceByMemberId(followWallet.getId()));
			vo.setUsername(memberService.findUserNameById(followWallet.getId()));
			vo.setCurrentPriceMap(currentPriceMap);
			
			BigDecimal blastRate = vo.getEstimatedBlastRate();
			Set<MemberContractPosition> positions = vo.getCurrentPositionList();
			List<MemberContractPositionWithCut> positionList = new ArrayList<MemberContractPositionWithCut>();
			for(MemberContractPosition position: positions) {
				MemberContractPositionWithCut positionWithCut = new MemberContractPositionWithCut();
				BeanUtils.copyProperties(position, positionWithCut);
				if(blastRate.doubleValue()==0 || (blastRate.doubleValue() > 1 && blastRate.doubleValue() < 1000))
					positionWithCut.setEstimatedBlastPrice(BigDecimal.valueOf(-1));
				else {
					if (position.getCurrentPrice()!=null)
						positionWithCut.setEstimatedBlastPrice(position.getCurrentPrice().multiply(BigDecimal.ONE.subtract(blastRate)));
				}
				if(vo.getCurrentMargin().compareTo(BigDecimal.ZERO)>0)
					positionWithCut.setMarginRate(vo.getCurrentCapital().multiply(BigDecimal.valueOf(100)).divide(vo.getCurrentMargin(), 2, BigDecimal.ROUND_DOWN));
				positionList.add(positionWithCut);
			}
			vo.setPositionsWithCut(positionList);
			retList.add(vo);
		}
		Page<FollowAccountVo> newAll = new PageImpl<FollowAccountVo>(retList, pageModel.getPageable(), all.getTotalElements());
		
		MessageResult<Page<FollowAccountVo>> result = MessageResult.success("success");
        result.setData(newAll);
        return result;
	}
	
	private Predicate getPredicate(FollowAccountScreen screen) {
		ArrayList<BooleanExpression> booleanExpressions = new ArrayList<>();
		QFollowWallet qFollowWallet = QFollowWallet.followWallet;
		
        if(screen.getMemberId() != null) {
            booleanExpressions.add(qFollowWallet.id.eq(screen.getMemberId()));
        }
        if(StringUtils.isNotEmpty(screen.getUsername())) {
            Member member = memberService.findByUsername(screen.getUsername());
            booleanExpressions.add(qFollowWallet.id.eq(member.getId()));
        }
        if(StringUtils.isNotEmpty(screen.getPhone())) {
            Member member = memberService.findByPhone(screen.getPhone());
            booleanExpressions.add(qFollowWallet.id.eq(member.getId()));
        }
        if(StringUtils.isNotEmpty(screen.getEmail())) {
            Member member = memberService.findByEmail(screen.getEmail());
            booleanExpressions.add(qFollowWallet.id.eq(member.getId()));
        }
        if(screen.getHasPosition()!=null) {
        	if(screen.getHasPosition()==1)
        		booleanExpressions.add(qFollowWallet.currentPositionList.size().gt(0));
        	else
        		booleanExpressions.add(qFollowWallet.currentPositionList.size().eq(0));
        }
        return PredicateUtils.getPredicate(booleanExpressions);
	}
	
	@ApiOperation(value="取消所有用户跟单仓位", notes="取消所有用户跟单仓位")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "username",value = "用户名",required = true,paramType = "form",dataType = "String"),
    		@ApiImplicitParam(name = "password",value = "密码",required = true,paramType = "form",dataType = "String")
    })
	@RequiresPermissions("swap:order:force-close")
    @PostMapping("cancelAll")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "跟单用户持仓管理 强制平仓")
    public MessageResult cancelAllFollow() {
		ConcurrentHashMap<Long, FollowAccount> map = followWalletService.findAllWithPositions();
        for(Long id: map.keySet()) {
        	FollowAccount followAccount = map.get(id);
        	BigDecimal openFee = BigDecimal.ZERO;
			for(MemberContractPosition position : followAccount.getCurrentPositionList()) {
	        	List<ContractOrderEntrust> openOrders = contractOrderEntrustService.queryOrdersByPositionId(position.getId());
	        	for(ContractOrderEntrust order : openOrders) {
	        		openFee = openFee.add(order.getOpenFee());
	        		Long transId = memberTransactionService.getLastByMemberIdAndType(position.getMemberId(), TransactionType.FOLLOW_FEE).getId();
	        		memberTransactionService.delete(transId);
	        	}
	        	positionService.delete(position.getId());
	        }
        	followWalletService.cancelTrade(id, openFee);
        }
        return MessageResult.success("操作成功");
    }
    
    @ApiOperation(value="获取用户跟单账户与资产账户余额", notes="用于资产划转弹窗展示")
    @ApiImplicitParam(name = "memberId",value = "用户ID",required = true,paramType = "form",dataType = "Long")
    @RequiresPermissions("swap:account:transfer")
    @PostMapping("balance-by-member")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "查询用户跟单与资产账户余额")
    public MessageResult balanceByMember(@RequestParam("memberId") Long memberId) {
        log.info("[balance-by-member] 请求入参 memberId={}", memberId);
        if (memberId == null) {
            return MessageResult.error("用户ID不能为空");
        }
        FollowWallet followWallet = followWalletService.findOne(memberId);
        log.info("[balance-by-member] memberId={} followWallet={}", memberId, followWallet != null ? "存在" : "null");
        BigDecimal followBalance = BigDecimal.ZERO;
        boolean hasPosition = false;
        if (followWallet != null) {
            followBalance = followWallet.getBalance() != null ? followWallet.getBalance() : BigDecimal.ZERO;
            hasPosition = followWallet.getCurrentPositionList() != null && followWallet.getCurrentPositionList().size() > 0;
            log.info("[balance-by-member] memberId={} followBalance={} hasPosition={}", memberId, followBalance, hasPosition);
        }
        BigDecimal spotBalance = memberWalletService.sumBalanceByMemberId(memberId);
        log.info("[balance-by-member] memberId={} spotBalance={}", memberId, spotBalance);
        String username = memberService.findUserNameById(memberId);
        Map<String, Object> data = new HashMap<>();
        data.put("followBalance", followBalance);
        data.put("spotBalance", spotBalance != null ? spotBalance : BigDecimal.ZERO);
        data.put("hasPosition", hasPosition);
        data.put("username", username);
        log.info("[balance-by-member] memberId={} 返回 data={}", memberId, data);
        return MessageResult.success("success", data);
    }
    
    @ApiOperation(value="跟单账户划转到资金账户", notes="管理员操作：将跟单账户资金划转到资金账户（币币钱包）")
    @ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "memberId",value = "用户ID",required = true,paramType = "form",dataType = "Long"),
    		@ApiImplicitParam(name = "amount",value = "划转金额",required = true,paramType = "form",dataType = "BigDecimal")
    })
    @RequiresPermissions("swap:account:transfer")
    @PostMapping("transfer-to-spot")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "跟单账户划转到资金账户")
    public MessageResult transferToSpot(
            @RequestParam(value = "memberId", required = false) Long memberId,
            @RequestParam(value = "amount", required = false) BigDecimal amount) {

        if (memberId == null || amount == null) {
            return MessageResult.error("参数不能为空");
        }
        Member member = memberService.findOne(memberId);
        if (member == null) {
            return MessageResult.error("用户不存在");
        }
        if (!redisLock.lockFollowAccount(memberId)) {
            return MessageResult.error("操作失败，请稍后重试");
        }
        try {
            MessageResult result = followWalletService.transferOutToSpot(memberId, amount);
            if (result.getCode() == 0) {
                log.info("[管理员划转] 跟单账户划转到资金账户成功, memberId={}, amount={}", memberId, amount);
            }
            return result;
        } catch (Exception e) {
            log.error("[管理员划转] 划转失败, memberId={}, amount={}, error={}", memberId, amount, e.getMessage(), e);
            return MessageResult.error("划转失败：" + e.getMessage());
        } finally {
            redisLock.unlockFollowAccount(memberId);
        }
    }
}
