package com.bizzan.bitrade.controller.swap;

import static com.bizzan.bitrade.constant.SysConstant.SESSION_MEMBER;

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

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.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.bizzan.bitrade.annotation.AccessLog;
import com.bizzan.bitrade.constant.AdminModule;
import com.bizzan.bitrade.constant.PageModel;
import com.bizzan.bitrade.controller.common.BaseAdminController;
import com.bizzan.bitrade.entity.Admin;
import com.bizzan.bitrade.entity.CloseType;
import com.bizzan.bitrade.entity.CoinThumb;
import com.bizzan.bitrade.entity.ContractCoin;
import com.bizzan.bitrade.entity.ContractOrderEntrust;
import com.bizzan.bitrade.entity.ContractOrderEntrustStatus;
import com.bizzan.bitrade.entity.ContractPositionStatus;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberContractPosition;
import com.bizzan.bitrade.entity.MemberContractWallet;
import com.bizzan.bitrade.entity.QMemberContractPosition;
import com.bizzan.bitrade.entity.transform.AuthMember;
import com.bizzan.bitrade.model.screen.MemberContractPositionScreen;
import com.bizzan.bitrade.service.AdminService;
import com.bizzan.bitrade.service.ContractCoinService;
import com.bizzan.bitrade.service.ContractOrderEntrustService;
import com.bizzan.bitrade.service.MemberContractPositionService;
import com.bizzan.bitrade.service.MemberService;
import com.bizzan.bitrade.service.MemberWalletService;
import com.bizzan.bitrade.service.UserPurseService;
import com.bizzan.bitrade.util.DateUtil;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.util.PredicateUtils;
import com.bizzan.bitrade.vo.MemberContractPositionWithCut;
import com.bizzan.bitrade.vo.TradingAccount;
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;

@Slf4j
@Api(description="仓位接口")
@RestController
@RequestMapping("/swap/position-new")
public class MemberContractPositionController extends BaseAdminController {

	@Autowired
	private MemberContractPositionService positionService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ContractCoinService contractCoinService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private UserPurseService userPurseService;
    @Autowired
    private MemberWalletService memberWalletService;
    @Autowired
    private ContractOrderEntrustService contractOrderEntrustService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private AdminService adminService;
    @Value("${spark.system.md5.key}")
    private String md5Key;
	
	@ApiOperation(value="获取仓位", notes="获取当前仓位")
	@RequiresPermissions("swap:position:page-query")
    @PostMapping("page-query")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约用户持仓管理 列表")
	public MessageResult<Page<MemberContractPositionWithCut>> list(PageModel pageModel,
            MemberContractPositionScreen screen) {
		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> priceList =responseEntity.getBody();
        
        if (pageModel.getDirection() == null && pageModel.getProperty() == null) {
            ArrayList<Sort.Direction> directions = new ArrayList<>();
            directions.add(Sort.Direction.DESC);
            pageModel.setDirection(directions);
            List<String> property = new ArrayList<>();
            property.add("memberId"); // 默认金额排序
            pageModel.setProperty(property);
        }
        
        Predicate predicate = getPredicate(screen);
        
        Page<MemberContractPositionWithCut> positions = positionService.findAllWithCut(predicate, pageModel.getPageable());
		positionService.addCurrentPrice(positions, priceList);
		for(MemberContractPositionWithCut position: positions) {
			position.setCurrencyBalance(memberWalletService.sumBalanceByMemberId(position.getMemberId()));
			TradingAccount tradingAccount = userPurseService.findOneVO(position.getMemberId());
			tradingAccount.setCurrentPriceMap(priceList);
			position.setContractBalance(tradingAccount.getCurrentCapital());
			BigDecimal blastRate = tradingAccount.getEstimatedBlastRate();
			if(blastRate.doubleValue()==0 || (blastRate.doubleValue() > 1 && blastRate.doubleValue() < 1000))
				position.setEstimatedBlastPrice(BigDecimal.valueOf(-1));
			else {
				if (position.getCurrentPrice()!=null)
					position.setEstimatedBlastPrice(position.getCurrentPrice().multiply(BigDecimal.ONE.subtract(blastRate)));
			}
		}
		
		MessageResult<Page<MemberContractPositionWithCut>> result = MessageResult.success("success");
        result.setData(positions);
        return result;
	}
	
	private Predicate getPredicate(MemberContractPositionScreen screen) {
		ArrayList<BooleanExpression> booleanExpressions = new ArrayList<>();
		QMemberContractPosition qMemberContractPosition = QMemberContractPosition.memberContractPosition;
		
		if (screen.getContractId() != null) {
            ContractCoin coin = contractCoinService.findOne(screen.getContractId());
            booleanExpressions.add(qMemberContractPosition.coin.eq(coin));
        }
        if(screen.getMemberId() != null) {
            booleanExpressions.add(qMemberContractPosition.memberId.eq(screen.getMemberId()));
        }
        if(StringUtils.isNotEmpty(screen.getPhone())) {
            Member member = memberService.findByPhone(screen.getPhone());
            booleanExpressions.add(qMemberContractPosition.memberId.eq(member.getId()));
        }
        if(StringUtils.isNotEmpty(screen.getEmail())) {
            Member member = memberService.findByEmail(screen.getEmail());
            booleanExpressions.add(qMemberContractPosition.memberId.eq(member.getId()));
        }
        if(StringUtils.isNotEmpty(screen.getStatus())) {
        	booleanExpressions.add(qMemberContractPosition.status.eq(ContractPositionStatus.valueOf(screen.getStatus())));
        }
        return PredicateUtils.getPredicate(booleanExpressions);
	}
	
	@ApiOperation(value="获取历史仓位", notes="获取历史仓位")
    @GetMapping("history")
	public MessageResult<List<MemberContractPosition>> history() {
		List<MemberContractPosition> positions = positionService.findHistoryPositions();
		MessageResult<List<MemberContractPosition>> result = MessageResult.success("success");
        result.setData(positions);
        return result;
	}
	
	@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("force-close-all")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约用户持仓管理 强制平仓")
    public MessageResult forceCloseAll(String username, String password) {
    	password = Encrypt.MD5(password + md5Key);
        log.info("==================>"+password);
        Admin admin = adminService.login(username,password);
        if(admin == null) {
        	return error("操作失败，密码错误");
        }
        List<MemberContractPosition> list = positionService.findCurrentPositions();
        for(MemberContractPosition position : list) {
        	position.setCloseType(CloseType.ADMIN_CLOSE);
        	kafkaTemplate.send("swap-order-close-all", JSON.toJSONString(position));
        }
        return MessageResult.success("操作成功");
    }
	
	@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("force-close-all-follow")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "跟单用户持仓管理 强制平仓")
    public MessageResult forceCloseAllFollow(String username, String password) {
    	password = Encrypt.MD5(password + md5Key);
        log.info("==================>"+password);
        Admin admin = adminService.login(username,password);
        if(admin == null) {
        	return error("操作失败，密码错误");
        }
        List<MemberContractPosition> list = positionService.findCurrentFollowPositions();
        int fail = 0;
        for (MemberContractPosition position : list) {
            try {
                restTemplate.postForObject(
                    "http://contract-swap-api/swap/admin/force-close-position?positionId=" + position.getId(),
                    null, String.class);
            } catch (Exception e) {
                log.error("[forceCloseAllFollow] positionId={} error: {}", position.getId(), e.getMessage());
                fail++;
            }
        }
        return MessageResult.success("操作成功，共 " + list.size() + " 条，失败 " + fail + " 条");
    }

	@ApiOperation(value="强平用户所有仓位", notes="强平用户所有仓位")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "memberId",value = "用户ID",required = true,paramType = "form",dataType = "int"),
    		@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("force-close-member")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约用户持仓管理 强制平仓")
    public MessageResult forceCloseByMemberId(Long memberId, String username, String password) {
    	password = Encrypt.MD5(password + md5Key);
        Admin admin = adminService.login(username,password);
        if(admin == null) {
        	return error("操作失败，密码错误");
        }
        List<MemberContractPosition> list = positionService.findCurrentPositionsByMemberId(memberId);
        int fail = 0;
        for (MemberContractPosition position : list) {
            try {
                restTemplate.postForObject(
                    "http://contract-swap-api/swap/admin/force-close-position?positionId=" + position.getId(),
                    null, String.class);
            } catch (Exception e) {
                log.error("[forceCloseByMemberId] positionId={} error: {}", position.getId(), e.getMessage());
                fail++;
            }
        }
        return MessageResult.success("操作成功，共 " + list.size() + " 条，失败 " + fail + " 条");
    }
    
	@ApiOperation(value="强平单个仓位", notes="强平单个仓位")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "positionId",value = "仓位ID",required = true,paramType = "form",dataType = "int"),
    		@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("force-close-wallet")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约用户持仓管理 强制平仓")
    public MessageResult forceCloseByWalletId(Long positionId, String username, String password) {
    	password = Encrypt.MD5(password + md5Key);
        Admin admin = adminService.login(username,password);
        if(admin == null) {
        	return error("操作失败，密码错误");
        }
        restTemplate.postForObject(
            "http://contract-swap-api/swap/admin/force-close-position?positionId=" + positionId,
            null, String.class);
        return MessageResult.success("操作成功");
    }

    /**
     * 强平指定交易员在某交易对上的全部仓位（含其跟单员仓位）。
     */
    @ApiOperation(value = "强平带单员仓位（含跟单员）", notes = "强平指定交易员在某交易对上的全部仓位及其所有跟单员仓位")
    @RequiresPermissions("swap:order:force-close")
    @PostMapping("force-close-trader")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "跟单持仓统计 交易员一键平仓")
    public MessageResult forceCloseTrader(Long traderMemberId, String symbol, String username, String password) {
        password = Encrypt.MD5(password + md5Key);
        Admin admin = adminService.login(username, password);
        if (admin == null) return error("操作失败，密码错误");

        int count = 0, fail = 0;
        // 1. 交易员自己的仓位（followPositionId IS NULL）
        List<MemberContractPosition> ownList = positionService.findCurrentPositionsByMemberId(traderMemberId);
        for (MemberContractPosition pos : ownList) {
            if (pos.getFollowPositionId() != null) continue;
            if (pos.getCoin() == null || !symbol.equals(pos.getCoin().getSymbol())) continue;
            try {
                restTemplate.postForObject(
                    "http://contract-swap-api/swap/admin/force-close-position?positionId=" + pos.getId(),
                    null, String.class);
                count++;
            } catch (Exception e) {
                log.error("[ForceCloseTrader] positionId={} error: {}", pos.getId(), e.getMessage());
                fail++;
            }
        }
        // 2. 所有跟随该交易员的跟单员仓位
        List<MemberContractPosition> followList = positionService.findCurrentFollowPositions();
        for (MemberContractPosition pos : followList) {
            if (!traderMemberId.equals(pos.getFollowMemberId())) continue;
            if (pos.getCoin() == null || !symbol.equals(pos.getCoin().getSymbol())) continue;
            try {
                restTemplate.postForObject(
                    "http://contract-swap-api/swap/admin/force-close-position?positionId=" + pos.getId(),
                    null, String.class);
                count++;
            } catch (Exception e) {
                log.error("[ForceCloseTrader] positionId={} error: {}", pos.getId(), e.getMessage());
                fail++;
            }
        }
        log.info("[ForceCloseTrader] traderMemberId={} symbol={} 成功={} 失败={}", traderMemberId, symbol, count, fail);
        return MessageResult.success("操作成功，已平仓 " + count + " 条，失败 " + fail + " 条");
    }

    /**
     * 强平单个跟单员仓位（detail 页面「市价平仓」按钮）。
     */
    @ApiOperation(value = "强平单个跟单员仓位", notes = "按 positionId 强平跟单员仓位")
    @RequiresPermissions("swap:order:force-close")
    @PostMapping("force-close-follow-one")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "跟单持仓明细 单笔平仓")
    public MessageResult forceCloseFollowOne(Long positionId, String username, String password) {
        password = Encrypt.MD5(password + md5Key);
        Admin admin = adminService.login(username, password);
        if (admin == null) return error("操作失败，密码错误");

        restTemplate.postForObject(
            "http://contract-swap-api/swap/admin/force-close-position?positionId=" + positionId,
            null, String.class);
        log.info("[ForceCloseFollowOne] positionId={}", positionId);
        return MessageResult.success("操作成功");
    }

	@ApiOperation(value="锁定单个仓位", notes="锁定单个仓位")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "id",value = "仓位ID",required = true,paramType = "form",dataType = "int")
    })
    @RequiresPermissions("member:update")
    @PostMapping(value = "lock")
    @AccessLog(module = AdminModule.MEMBER, operation = "锁仓仓位")
    public MessageResult lock(Long id) {
    	positionService.updateIsLock(id, 1);
        return success();
    }
    
	@ApiOperation(value="解锁单个仓位", notes="解锁单个仓位")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "id",value = "仓位ID",required = true,paramType = "form",dataType = "int")
    })
    @RequiresPermissions("member:update")
    @PostMapping(value = "unlock")
    @AccessLog(module = AdminModule.MEMBER, operation = "解锁仓位")
    public MessageResult unlock(Long id) {
    	positionService.updateIsLock(id, 0);
        return success();
    }
	
	@RequiresPermissions("member:update")
    @PostMapping(value = "cancel-cut")
    public MessageResult cancelCut() {
		List<ContractOrderEntrust> list = contractOrderEntrustService.findAllRunningCuts();
		for(ContractOrderEntrust order: list) {
			if(order.getVolume().compareTo(BigDecimal.ZERO)>0) {
				positionService.thawPosition(order.getPositionId(), order.getVolume());
				//order.setStatus(ContractOrderEntrustStatus.ENTRUST_CANCEL);
		        //order.setDealTime(DateUtil.getTimeMillis());
		        //contractOrderEntrustService.save(order);
			}
		}
        return success();
    }
}
