package com.izouma.nineth.web;

import com.izouma.nineth.domain.TokenHistory;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.AssetDTO;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.dto.PageWrapDTO;
import com.izouma.nineth.dto.UserHistory;
import com.izouma.nineth.dto.collection.AssetExcelDTO;
import com.izouma.nineth.enums.AccountStatus;
import com.izouma.nineth.enums.AssetStatus;
import com.izouma.nineth.enums.ItemType;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.lock.RedisLockException;
import com.izouma.nineth.repo.AssetRepo;
import com.izouma.nineth.repo.UserWareHouseRepo;
import com.izouma.nineth.service.*;
import com.izouma.nineth.service.pay.UniformPayConstants;
import com.izouma.nineth.service.pay.UniformPayUtil;
import com.izouma.nineth.utils.SecurityUtils;
import com.izouma.nineth.utils.excel.ExcelUtils;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/asset")
@Slf4j
@AllArgsConstructor
public class AssetController extends BaseController {
    private static Thread cndThread;
    private static Thread sellPriceDataThread;

    private AssetService assetService;
    private AssetRepo assetRepo;
    private GiftOrderService giftOrderService;
    private RocketMQTemplate rocketMQTemplate;
    private GeneralProperties generalProperties;
    private UserService userService;
    private UserWareHouseRepo userWareHouseRepo;
    private AirDropService airDropService;

    private ProjectService projectService;

    private UserFundAccountForLianlianpayService userFundAccountForLianlianpayService;

    private UniformPayUtil uniformPayUtil;

    //@PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/save")
    public Asset save(@RequestBody Asset record) {
        throw new BusinessException("不支持保存资产的操作");
//        if (record.getId() != null) {
//            Asset orig = assetRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
//            ObjUtils.merge(orig, record);
//            return assetRepo.save(orig);
//        }
//        return assetRepo.save(record);
    }


    //@PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/all")
    public PageWrapDTO<AssetDTO> all(@RequestBody PageQuery pageQuery) {
        //限定普通用户查询范围
        User user = currentUser();
        if(!user.isAdmin()){
            pageQuery.getQuery().put("userId", user.getId());
        }
        PageWrapDTO<Asset> assets = assetService.all(pageQuery);
        List<AssetDTO> list = assetService.convertToAssetDTO(assets.getContent());
        PageWrapDTO newPageWrapDTO = new PageWrapDTO(list, assets.getPageable(), assets.getTotalElements());
        newPageWrapDTO.setTotalAmount(assets.getTotalAmount());
        return newPageWrapDTO;
    }

    /**
     * 同类藏品查询
     * @param pageQuery
     * @return
     */
    @PostMapping("/allSameCollection")
    public Page<AssetDTO> allSameCollection(@RequestBody PageQuery pageQuery) {
        //限定普通用户查询范围
        User user = currentUser();
        pageQuery.getQuery().put("userId", user.getId());
        Page<AssetDTO> page = assetService.allSameCollection(pageQuery);
        return page;
    }

    @GetMapping("/mySellAmount")
    public BigDecimal mySellAmount() {
        return assetService.sellAmountFromUserId(SecurityUtils.getAuthenticatedUser().getId());
    }


    @GetMapping("/get/{id}")
    public AssetDTO get(@PathVariable Long id) {
        Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("指定资产未找到"));
        //假数据处理
        assetService.mockLimitData(asset);
        AssetDTO assetDTO = new AssetDTO(asset);
        PreEmptionPrivilege preEmptionPrivilege = assetService.preEmptionPrivilege(assetDTO);
        assetDTO.setPreEmptionPrivilege(preEmptionPrivilege);
        return assetDTO;
    }

    @PostMapping("/del/{id}")
    public void del(@PathVariable Long id) {
        throw new BusinessException("不支持保存资产删除操作");
        //assetRepo.softDelete(id);
    }

    /**
     * 导出数据
     * @param response
     * @param pageQuery
     * @throws IOException
     */
    @PostMapping("/excel")
    @ResponseBody
    public void excel(HttpServletResponse response, @RequestBody PageQuery pageQuery) throws IOException {
        Page<Asset> page = assetService.all(pageQuery);
        List<AssetDTO> list = assetService.convertToAssetDTO(page.getContent());
        List<AssetExcelDTO> assetExcelDTOS = list.stream().map(AssetExcelDTO::new).collect(Collectors.toList());
        ExcelUtils.export(response, assetExcelDTOS);
    }


    @PostMapping("/publicShow")
    @ApiOperation("公开展示")
    public void publicShow(@RequestParam Long id) {
        User user = currentUser();
        assetService.publicShow(user.getId(),id);
    }

    @PostMapping("/cancelPublic")
    @ApiOperation("取消展示")
    public void cancelPublic(@RequestParam Long id) {
        User user = currentUser();
        assetService.cancelPublic(user.getId(), id);
    }

    @PostMapping("/usePrivilege")
    @ApiOperation("使用特权")
    public void usePrivilege(@RequestParam Long assetId, @RequestParam Long privilegeId) {
        assetService.usePrivilege(assetId, privilegeId);
    }

    @PostMapping("/consignment")
    @ApiOperation("寄售")
    public void consignment(@RequestParam Long id, @RequestParam BigDecimal price) {
        User user = currentUser();
        //是否需要开通钱包
        String defaultPayPlatform = uniformPayUtil.getDefaultPayPlatform();
        if(UniformPayConstants.NOTPAY_PLATFORM.equals(defaultPayPlatform)){
            //支付通道已关闭时，需要检查
            UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(user.getId());
            if(lianlianAccount == null || lianlianAccount.getAccountStatus() != AccountStatus.NORMAL){
                throw new BusinessException("请开通钱包后再寄售");
            }
        }

        assetService.consignment(user.getId(), id, price);
    }

    @PostMapping("/cancelConsignment")
    @ApiOperation("取消寄售")
    public void cancelConsignment(@RequestParam Long id) {
        User user  = currentUser();
        assetService.cancelConsignment(user.getId(), id);
    }

    @PostMapping("/cancelAllConsignment")
    @ApiOperation("一键取消自己寄售藏品")
    public void cancelAllConsignment(){
        User user = currentUser();
        List<Asset> assets = assetRepo.findAllConsignmentByUserIdAndStatus(user.getId(), AssetStatus.NORMAL);
        assets.forEach(asset -> assetService.cancelConsignment(user.getId(), asset.getId()));
    }

    @PostMapping("/gift")
    @ApiOperation("转赠")
    public GiftOrder gift(@RequestParam Long assetId, @RequestParam Long toUserId, @RequestParam(required = false) String tradeCode) {
        final User user = currentUser();
        //是否需要开通钱包
        boolean hasWallet = false;
        UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(user.getId());
        if (lianlianAccount != null && lianlianAccount.getAccountStatus() == AccountStatus.NORMAL) {
            hasWallet = true;
        }

        String defaultPayPlatform = uniformPayUtil.getDefaultPayPlatform();
        if (UniformPayConstants.NOTPAY_PLATFORM.equals(defaultPayPlatform) && !hasWallet) {
            //支付通道已关闭时，需要检查
            throw new BusinessException("请开通钱包后再转赠");
        }

        try {
            if (!hasWallet) {
                userService.verifyTradeCode(user.getId(), tradeCode);
            }
            //取消寄售
            assetService.cancelPublic(user.getId(), assetId);
            //创建转赠订单
            GiftOrder giftOrder = giftOrderService.createGiftOrder(user.getId(), assetId, toUserId);
            return giftOrder;
        } catch (RedisLockException err) {
            log.error("用户锁定转增订单失败，当前用户ID:{},资产ID:{}, 目标用户ID：{}", user.getId(), assetId, toUserId);
            throw new BusinessException("正在转赠中，请稍后再试");
        }

    }

    @PostMapping("/openSurpriseBox")
    public SurpriseBoxLog openSurpriseBox(@RequestParam Long surpriseBoxId) {
        User user = currentUser();
        if (user == null) return null;
        return airDropService.openSurpriseBox(currentUser(), surpriseBoxId);
    }

    @PostMapping("/getSurpriseBox")
    public List<UserWareHouse> getSurpriseBox(@RequestParam Long assetId, Integer projectId) {
        if (assetId == null) {
            return userWareHouseRepo.findUserIdBox(currentUser().getId(), ItemType.SURPRISE_BOX.ordinal(), projectId);
        } else {
            return userWareHouseRepo.findUserIdBoxByAssetId(currentUser().getId(), assetId, ItemType.SURPRISE_BOX.ordinal());
        }
    }

    @GetMapping("/tokenHistory")
    @ApiOperation("交易历史")
    public List<TokenHistory> tokenHistory(@RequestParam(required = false) String tokenId, @RequestParam(required = false) Long assetId) {
        return assetService.tokenHistory(tokenId, assetId);
    }

    @GetMapping("/userHistory")
    @ApiOperation("交易历史")
    public Page<UserHistory> userHistory(int projectId, Integer type, Pageable pageable) {
        return assetService.userHistory(SecurityUtils.getAuthenticatedUser().getId(), projectId, type, pageable);
    }

    @PostMapping("/holdQuery")
    public String holdQuery(@RequestParam List<String> names, @RequestParam LocalDateTime start, @RequestParam LocalDateTime end) {
        Map<User, Integer> match = assetService.holdQuery(names, start, end);
        StringBuilder builder = new StringBuilder("用户ID,昵称,手机,持有套数\n");
        for (Map.Entry<User, Integer> e : match.entrySet()) {
            User user = e.getKey();
            builder.append(String.format("%d,%s,%s,%d%n", user.getId(), user.getNickname(), user.getPhone(), e.getValue()));
        }
        return builder.toString();
    }

    @GetMapping("/mint")
    public String mint() {
        for (Asset asset : assetRepo.toMint(LocalDateTime.now())) {
            rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
        }
        return "ok";
    }

    @PostMapping("/cdn")
    @PreAuthorize("hasRole('ADMIN')")
    public synchronized String cdn() {
        if (cndThread != null && cndThread.isAlive()) {
            throw new BusinessException("任务正在进行，请稍后再试");
        }

        cndThread = new Thread(() -> {
            try {
                assetService.transferCDN();
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        });
        cndThread.start();
        return "异步调用成功，请勿重复操作";
    }


    /**
     * 初始化低价锁定
     *
     * @return
     */
    @PostMapping("/initLimitSellPriceData")
    @PreAuthorize("hasRole('ADMIN')")
    public synchronized String initLimitSellPriceData() {
        if (sellPriceDataThread != null && sellPriceDataThread.isAlive()) {
            throw new BusinessException("任务正在进行，请稍后再试");
        }

        sellPriceDataThread = new Thread(() -> {
            Integer changeCount = assetService.initLimitSellPriceData();
            log.info("限制售卖价格的变更：" + changeCount);
        });

        sellPriceDataThread.start();
        return "异步调用成功，请勿重复操作";
    }

    /**
     * 手动开启盲盒
     *
     * @param assetId
     * @return
     */
    @PostMapping("/variableAsset")
    public Asset variableAsset(Long assetId) {
        try {
            return assetService.variableAsset(assetId, currentUser());
        } catch (RedisLockException err) {
            log.error("抽盲合没获取资产ID：{}", assetId);
            throw new BusinessException("用力过猛，请重试一次");
        }
    }


    /**
     * 用户个人藏品数量
     */
    @PostMapping("/userHoldNum")
    public Map<String, Object> userHoldNum(@RequestBody User user) {
        return assetService.userHoldNum(user.getId(),projectService.getProjectId());
    }


}


