package com.bizzan.bitrade.controller.swap;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bizzan.bitrade.annotation.AccessLog;
import com.bizzan.bitrade.constant.AdminModule;
import com.bizzan.bitrade.constant.BooleanEnum;
import com.bizzan.bitrade.constant.PageModel;
import com.bizzan.bitrade.controller.common.BaseAdminController;
import com.bizzan.bitrade.entity.ContractCoin;
import com.bizzan.bitrade.entity.SymbolPriceBias;
import com.bizzan.bitrade.model.ContractStatVo;
import com.bizzan.bitrade.model.screen.ContractCoinScreen;
import com.bizzan.bitrade.service.ContractCoinService;
import com.bizzan.bitrade.service.MemberContractPositionService;
import com.bizzan.bitrade.service.SymbolPriceBiasService;
import com.bizzan.bitrade.util.JDBCUtils;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.vo.PositionVO;
import lombok.extern.slf4j.Slf4j;
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.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.kafka.core.KafkaTemplate;
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 javax.validation.Valid;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/swap-coin")
@Slf4j
public class ContractCoinController extends BaseAdminController {

    @Autowired
    private ContractCoinService contractCoinService;
    @Autowired
    private SymbolPriceBiasService symbolPriceBiasService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    
    @Autowired
    private MemberContractPositionService memberContractPositionService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    JDBCUtils jdbcUtils;
    /**
     * 获取永续合约交易对列表
     * @param pageModel
     * @return
     */
    @RequiresPermissions("swap-coin:page-query")
    @PostMapping("page-query")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "期权合约交易对 列表")
    public MessageResult list(
            PageModel pageModel,
            @RequestParam(required = false) String positionType) {

        if (pageModel.getProperty() == null) {
            List<String> list = new ArrayList<>();
            list.add("sort");
            List<Sort.Direction> directions = new ArrayList<>();
            directions.add(Sort.Direction.DESC);
            pageModel.setProperty(list);
            pageModel.setDirection(directions);
        }
        // 获取实时价格
        Map<String, BigDecimal> priceMap = Collections.emptyMap();
        try {
            ParameterizedTypeReference<Map<String, BigDecimal>> typeRef = new ParameterizedTypeReference<Map<String, BigDecimal>>() {};
            ResponseEntity<Map<String, BigDecimal>> priceResp = restTemplate.exchange(
                    "http://contract-swap-api/swap/symbol-price", HttpMethod.POST, new HttpEntity<>(null), typeRef);
            if (priceResp.getBody() != null) priceMap = priceResp.getBody();
        } catch (Exception e) {
            log.warn("[ContractCoin] 获取实时价格失败，currentPrice 将为空: {}", e.getMessage());
        }
        final Map<String, BigDecimal> finalPriceMap = priceMap;

        Page<ContractCoin> coinList = contractCoinService.findAll(null, pageModel.getPageable());
        List<ContractCoinScreen> list = new ArrayList<>();
        coinList.forEach(contractCoin ->{
        	ContractCoinScreen screen = new ContractCoinScreen();
        	BeanUtils.copyProperties(contractCoin, screen);
        	list.add(screen);
        	PositionVO vo;
        	if ("copy".equals(positionType)) {
        	    vo = memberContractPositionService.sumCopyPositionByCoin(contractCoin.getId());
        	} else if ("self".equals(positionType)) {
        	    vo = memberContractPositionService.sumSelfPositionByCoin(contractCoin.getId());
        	} else {
        	    vo = memberContractPositionService.sumPositionByCoin(contractCoin.getId());
        	}
        	screen.setBuyPosition(vo.getBuyPosition());
        	screen.setSellPosition(vo.getSellPosition());
        	screen.setCurrentPrice(finalPriceMap.getOrDefault(contractCoin.getSymbol(), BigDecimal.ZERO));
        });

        Page<ContractCoinScreen> newCoinList = new PageImpl<ContractCoinScreen>(list, pageModel.getPageable(), coinList.getTotalElements());
        return success(newCoinList);
    }

    /**
     * 获取永续合约交易对详情
     * @param contractId
     * @return
     */
    @RequiresPermissions("swap-coin:detail")
    @PostMapping("detail")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约交易对 详情")
    public MessageResult detail(@RequestParam(value = "symbol") Long contractId) {
        ContractCoin coin = contractCoinService.findOne(contractId);
        if(coin == null){
            return error("交易对不存在");
        }
        return success(coin);
    }

    /**
     * 添加永续合约交易对
     * @param contractCoin
     * @return
     */
    @RequiresPermissions("swap-coin:add")
    @PostMapping("add")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约交易对 新增")
    public MessageResult add(@Valid ContractCoin contractCoin) {
        ContractCoin coin = contractCoinService.findBySymbol(contractCoin.getSymbol());
        if(coin != null) {
            return error("添加失败！交易对 " + contractCoin.getSymbol() + " 已存在！");
        }
        contractCoin.setTotalProfit(BigDecimal.ZERO);
        contractCoin.setTotalCloseFee(BigDecimal.ZERO);
        contractCoin.setTotalLoss(BigDecimal.ZERO);
        contractCoin.setTotalOpenFee(BigDecimal.ZERO);
        //contractCoin = contractCoinService.save(contractCoin);
        contractCoinService.save(contractCoin);
        log.info("contractCoin={}", contractCoin);
        if(contractCoin.getEnable() == 1)
        	addRunningCoin(contractCoin.getSymbol());
        
        com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
    	obj.put("symbol", contractCoin.getSymbol());
    	kafkaTemplate.send("add-contract-to-members", obj.toJSONString());
    	
        return MessageResult.getSuccessInstance("添加交易对成功！", contractCoin);
    }
    
    private void addRunningCoin(String symbol) {
    	com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
    	obj.put("symbol", symbol);
        log.info("json={}", obj.toJSONString());
    	kafkaTemplate.send("add-contract-coin", obj.toJSONString());
    	kafkaTemplate.send("add-contract-coin-to-market", obj.toJSONString());
    }
    
    private void removeRunningCoin(String symbol) {
    	com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
    	obj.put("symbol", symbol);
    	kafkaTemplate.send("remove-contract-coin", obj.toJSONString());
    	kafkaTemplate.send("remove-contract-coin-from-market", obj.toJSONString());
    }

    /**
     * 修改永续合约交易对信息
     * @param id
     * @param symbol
     * @param sort
     * @param enable
     * @param visible
     * @param exchangeable
     * @param enableOpenSell
     * @param enableOpenBuy
     * @param enableMarketSell
     * @param enableMarketBuy
     * @param enableTriggerEntrust
     * @param spreadType
     * @param spread
     * @param leverageType
     * @param leverage
     * @param minShare
     * @param maxShare
     * @param intervalHour
     * @param feePercent
     * @param maintenanceMarginRate
     * @param openFee
     * @param closeFee
     * @param takerFee
     * @param makerFee
     * @return
     */
    @RequiresPermissions("swap-coin:alter")
    @PostMapping("alter")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约交易对 新增")
    public MessageResult alter(
            @RequestParam("id") Long id,
            @RequestParam("symbol") String symbol,
            @RequestParam(value = "name", required = false) String name,
            @RequestParam(value = "sort", required = false) Integer sort, // 排序
            @RequestParam(value = "enable", required = false) Integer enable, // 上下架（1:上,2）
            @RequestParam(value = "visible", required = false) Integer visible, // 是否显示（1:是,2）
            @RequestParam(value = "exchangeable", required = false) Integer exchangeable, // 是否显示（1:是,2）
            @RequestParam(value = "enableOpenSell", required = false) Integer enableOpenSell, // 是否可买（1:是,0:否）
            @RequestParam(value = "enableOpenBuy", required = false) Integer enableOpenBuy, // 是否可买（1:是,0:否）
            @RequestParam(value = "enableMarketSell", required = false) Integer enableMarketSell, // 是否可买（1:是,0:否）
            @RequestParam(value = "enableMarketBuy", required = false) Integer enableMarketBuy, // 是否可买（1:是,0:否）
            @RequestParam(value = "enableTriggerEntrust", required = false) Integer enableTriggerEntrust, // 是否可买（1:是,0:否）
            @RequestParam(value = "spreadType", required = false) Integer spreadType, // 上下架（1:上,2）
            @RequestParam(value = "spread", required = false) BigDecimal spread,
            @RequestParam(value = "leverageType", required = false) Integer leverageType, // 是否显示（1:是,2）
            @RequestParam(value = "leverage", required = false) String leverage, // 允许投注数量
            @RequestParam(value = "coinScale", required = false) Integer coinScale, // 排序
            @RequestParam(value = "baseCoinScale", required = false) Integer baseCoinScale, // 排序
            @RequestParam(value = "minScale", required = false) Integer minScale, // 开仓数量精度
            @RequestParam(value = "minShare", required = false) BigDecimal minShare,
            @RequestParam(value = "maxShare", required = false) BigDecimal maxShare,
            @RequestParam(value = "maxTotalShare", required = false) BigDecimal maxTotalShare,
            @RequestParam(value = "intervalHour", required = false) Integer intervalHour,
            @RequestParam(value = "feePercent", required = false) BigDecimal feePercent,
            @RequestParam(value = "maintenanceMarginRate", required = false) BigDecimal maintenanceMarginRate,
            @RequestParam(value = "openFee", required = false) BigDecimal openFee,
            @RequestParam(value = "closeFee", required = false) BigDecimal closeFee,
            @RequestParam(value = "takerFee", required = false) BigDecimal takerFee,
            @RequestParam(value = "makerFee", required = false) BigDecimal makerFee,
            @RequestParam(value = "icon", required = false) String icon,
            @RequestParam(value = "isHot", required = false) Integer isHot
    ) {
        ContractCoin coin = contractCoinService.findOne(id);
        if(coin == null) {
            return error("交易对 " + coin.getSymbol() + " 不存在！");
        }

        if(name != null) coin.setName(name);
        if(sort != null) coin.setSort(sort);
        boolean enabling = false;
        boolean disabling = false;
        if(enable != null) {
        	if (coin.getEnable() == 0 && enable == 1) {
        		enabling = true;
        	}
        	if (coin.getEnable() == 1 && enable == 0) {
        		disabling = true;
        	}
        	coin.setEnable(enable);
        }
        if(visible != null) coin.setVisible(visible);
        if(exchangeable != null) coin.setExchangeable(exchangeable);
        if(enableOpenSell != null) coin.setEnableOpenSell(enableOpenSell == 1 ? BooleanEnum.IS_TRUE : BooleanEnum.IS_FALSE);
        if(enableOpenBuy != null) coin.setEnableOpenBuy(enableOpenBuy == 1 ? BooleanEnum.IS_TRUE : BooleanEnum.IS_FALSE);
        if(enableMarketSell != null) coin.setEnableMarketSell(enableMarketSell == 1 ? BooleanEnum.IS_TRUE : BooleanEnum.IS_FALSE);
        if(enableMarketBuy != null) coin.setEnableMarketBuy(enableMarketBuy == 1 ? BooleanEnum.IS_TRUE : BooleanEnum.IS_FALSE);
        if(enableTriggerEntrust != null) coin.setEnableTriggerEntrust(enableTriggerEntrust == 1 ? BooleanEnum.IS_TRUE : BooleanEnum.IS_FALSE);
        if(spreadType != null) coin.setSpreadType(spreadType);
        if(spread != null) coin.setSpread(spread);
        if(leverageType != null) coin.setLeverageType(leverageType);
        if(leverage != null) coin.setLeverage(leverage);
        if(coinScale != null) coin.setCoinScale(coinScale);
        if(baseCoinScale != null) coin.setBaseCoinScale(baseCoinScale);
        if(minScale != null) coin.setMinScale(minScale);
        if(minShare != null) coin.setMinShare(minShare);
        if(maxShare != null) coin.setMaxShare(maxShare);
        if(maxTotalShare != null) coin.setMaxTotalShare(maxTotalShare);
        if(intervalHour != null) coin.setIntervalHour(intervalHour);
        if(feePercent != null) coin.setFeePercent(feePercent);
        if(maintenanceMarginRate != null) coin.setMaintenanceMarginRate(maintenanceMarginRate);
        if(openFee != null) coin.setOpenFee(openFee);
        if(closeFee != null) coin.setCloseFee(closeFee);
        if(takerFee != null) coin.setTakerFee(takerFee);
        if(makerFee != null) coin.setMakerFee(makerFee);
        if(icon != null) coin.setIcon(icon);
        if(isHot != null) coin.setHot(isHot);

        contractCoinService.save(coin);
        // 通知撮合引擎刷新内存中的合约配置（含手续费率）
        JSONObject coinUpdateMsg = new JSONObject();
        coinUpdateMsg.put("cid", coin.getId());
        kafkaTemplate.send("update-contract-coin", coinUpdateMsg.toJSONString());
        if (enabling) {
        	this.addRunningCoin(coin.getSymbol());
        }
        if (disabling) {
        	this.removeRunningCoin(coin.getSymbol());
        }
        return success("保存成功");
    }

    /**
     * 批量为用户添加钱包
     * @param contractId
     * @return
     */
    @RequiresPermissions("swap-coin:init-wallet")
    @PostMapping("init-wallet")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约交易对新增钱包 新增")
    public MessageResult generateWallet(@RequestParam("contractId") Long contractId) {
        ContractCoin coin = contractCoinService.findOne(contractId);
        if(coin == null) {
            return MessageResult.error("合约币种配置不存在");
        }
        jdbcUtils.synchronizationMemberContractWallet(null, contractId);
        return MessageResult.success("操作成功");
    }

    /**
     * 定点爆仓
     * @param contractId
     * @param price
     * @return
     */
    @RequiresPermissions("swap-coin:blast")
    @PostMapping("blast")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约 定向爆仓")
    public MessageResult blast(@RequestParam("contractId") Long contractId, @RequestParam("price") BigDecimal price) {
        ContractCoin coin = contractCoinService.findOne(contractId);
        if(coin == null) {
            return MessageResult.error("合约币种配置不存在");
        }

        return MessageResult.success("操作成功");
    }

    /**
     * 戳一下
     * @param contractId
     * @param price
     * @return
     */
    @RequiresPermissions("swap-coin:poke")
    @PostMapping("poke")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约 戳一下")
    public MessageResult poke(@RequestParam("contractId") Long contractId, @RequestParam("price") BigDecimal price) {
        ContractCoin coin = contractCoinService.findOne(contractId);
        if(coin == null) {
            return MessageResult.error("合约币种配置不存在");
        }
        //contractCoinService.savePoke(coin.getSymbol(),price);
        return MessageResult.success("操作成功");
    }
    
    @RequiresPermissions("swap-coin:poke")
    @PostMapping("set-bias")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约 设置值")
    public MessageResult setBias(@RequestParam("id") Long id, @RequestParam("symbol") String symbol, @RequestParam("bias") BigDecimal bias) {
    	SymbolPriceBias symbolPriceBias = new SymbolPriceBias();
    	symbolPriceBias.setId(id);
    	symbolPriceBias.setSymbol(symbol);
    	symbolPriceBias.setBias(bias);
    	symbolPriceBiasService.save(symbolPriceBias);
    	kafkaTemplate.send("set-bias", JSON.toJSONString(symbolPriceBias));
        return MessageResult.success("操作成功");
    }
    @RequiresPermissions("swap-coin:poke")
    @PostMapping("get-bias")
    @AccessLog(module = AdminModule.CONTRACTOPTION, operation = "永续合约 获取值")
    public MessageResult getBias(@RequestParam("id") Long id) {
        return success(symbolPriceBiasService.get(id));
    }
    
    @PostMapping("stat")
    public MessageResult stat() {
        log.info("profitAndLoss...");
        BigDecimal[] profitAndLoss = contractCoinService.sumTotalProfitAndLoss();
        log.info("profitAndLoss={}", profitAndLoss);
        ContractStatVo vo = new ContractStatVo();
        vo.setTotalProfit(profitAndLoss[1]);
        vo.setTotalLoss(profitAndLoss[0]);
        return success(vo);
    }
}
