package com.izouma.nineth.service;

import com.izouma.nineth.ApplicationTests;
import com.izouma.nineth.config.Constants;
import com.izouma.nineth.domain.Asset;
import com.izouma.nineth.domain.MintMaterial;
import com.izouma.nineth.domain.MintOrder;
import com.izouma.nineth.domain.User;
import com.izouma.nineth.dto.PayQuery;
import com.izouma.nineth.enums.AssetStatus;
import com.izouma.nineth.enums.MintOrderStatus;
import com.izouma.nineth.enums.PayMethod;
import com.izouma.nineth.event.OrderNotifyEvent;
import com.izouma.nineth.repo.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;

class MintOrderServiceTest extends ApplicationTests {

    @Autowired
    private MintOrderService mintOrderService;
    @Autowired
    private UserRepo         userRepo;
    @Autowired
    private MintOrderRepo    mintOrderRepo;
    @Autowired
    private MintMaterialRepo mintMaterialRepo;
    @Autowired
    private TokenHistoryRepo tokenHistoryRepo;
    @Autowired
    private AssetRepo        assetRepo;
    @Autowired
    private OrderPayService  orderPayService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Test
    void exchange() throws InterruptedException {
        mintOrderService.create(9972L, Arrays.asList(134607L, 132435L, 132433L));
        Thread.sleep(1000);
    }

    @Test
    public void test1() {
        User user = userRepo.findByIdAndDelFalse(9972L).orElse(null);
        mintOrderService.create(user, Arrays.asList(151339L), 151155L, null);
    }

    @Test
    public void test2() {
        mintOrderService.mintNotify(151376L, PayMethod.ALIPAY, "xxx");
    }

    @Test
    public void test3() {
        List<MintOrder> all = mintOrderRepo.findAll();
        all.forEach(a -> {
            List<MintMaterial> material = a.getMaterial();
            if (CollectionUtils.isNotEmpty(material)) {
                material.forEach(m -> {
                    m.setOrderId(a.getId());
                    mintMaterialRepo.save(m);
                });
            }

        });
    }

    @Test
    public void fixDuplicated() throws ExecutionException, InterruptedException {
        new ForkJoinPool(3000).submit(() -> {
            List<MintOrder> mintOrders = mintOrderRepo.findByStatusAndCreatedAtAfter(MintOrderStatus.AIR_DROP, LocalDateTime.of(2022, 5, 20, 0, 0, 0));
            List<MintOrder> errOrders = new ArrayList<>();
            mintOrders.parallelStream().forEach(mintOrder -> {
                mintOrder.setMaterial(mintMaterialRepo.findAllByOrderIdAndDelFalse(mintOrder.getId()));
            });
            mintOrders.parallelStream().collect(Collectors.groupingBy(MintOrder::getUserId))
                    .forEach((userId, orders) -> {
                        Iterator<MintOrder> iterator = orders.iterator();
                        List<MintOrder> userErrOrders = new ArrayList<>();
                        while (iterator.hasNext()) {
                            MintOrder next = iterator.next();
                            List<Long> assetIds = next.getMaterial().stream().map(MintMaterial::getAssetId)
                                    .collect(Collectors.toList());
                            for (MintOrder order : orders) {
                                if (!order.getId().equals(next.getId())) {
                                    List<Long> assetIds1 = order.getMaterial().stream().map(MintMaterial::getAssetId)
                                            .collect(Collectors.toList());
                                    if (CollectionUtils.intersection(assetIds, assetIds1).size() > 0) {
                                        iterator.remove();
                                        errOrders.add(next);
                                        userErrOrders.add(next);
                                        break;
                                    }
                                }
                            }
                        }
                        if (userErrOrders.size() > 0) {
                            Set<Long> errAssetIds = userErrOrders.stream()
                                    .flatMap(mintOrder -> mintOrder.getMaterial().stream())
                                    .map(MintMaterial::getAssetId).collect(Collectors.toSet());
                            Set<Long> orderAssetIds = orders.stream()
                                    .flatMap(mintOrder -> mintOrder.getMaterial().stream())
                                    .map(MintMaterial::getAssetId).collect(Collectors.toSet());
                            Collection toSetNormal = CollectionUtils.subtract(errAssetIds, orderAssetIds);
                            System.out.println(toSetNormal);
                            for (MintOrder userErrOrder : userErrOrders) {
                                userErrOrder.setStatus(MintOrderStatus.CANCELLED);
                                userErrOrder.setCancelTime(LocalDateTime.now());
                                mintOrderRepo.save(userErrOrder);
                                PayMethod payMethod = userErrOrder.getPayMethod();
                                if (PayMethod.ALIPAY == payMethod) {
                                    if (StringUtils.length(userErrOrder.getTransactionId()) == 28) {
                                        payMethod = PayMethod.HMPAY;
                                    } else if (StringUtils.length(userErrOrder.getTransactionId()) == 30) {
                                        payMethod = PayMethod.SANDPAY;
                                    }
                                }
                                try {
                                    switch (payMethod) {
                                        case HMPAY:
                                            orderPayService.refund(userErrOrder.getId()
                                                    .toString(), userErrOrder.getTransactionId(), userErrOrder.getGasPrice(), "hmPay");
                                            break;
                                        case SANDPAY:
                                            orderPayService.refund(userErrOrder.getId()
                                                    .toString(), userErrOrder.getTransactionId(), userErrOrder.getGasPrice(), "sandPay");
                                            break;
                                        case PAYEASE:
                                            orderPayService.refund(userErrOrder.getId()
                                                    .toString(), userErrOrder.getTransactionId(), userErrOrder.getGasPrice(), "payEase");
                                            break;
                                    }
                                } catch (Exception e) {
                                }
                            }
                            for (Object assetId : toSetNormal) {
                                Asset asset = assetRepo.findById((Long) assetId).get();
                                asset.setStatus(AssetStatus.NORMAL);
                                asset.getPrivileges().forEach(p -> {
                                    if (p.getName().contains("铸造")) {
                                        p.setOpened(false);
                                        p.setOpenTime(null);
                                        p.setOpenedBy(null);
                                    }
                                });
                                asset.setOwnerId(asset.getUserId());
                                assetRepo.saveAndFlush(asset);
                                userRepo.updateAssetOwner(asset.getUserId());
                                tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(asset.getTokenId()).stream()
                                        .findFirst()
                                        .ifPresent(tokenHistory -> {
                                            if (tokenHistory.getToUserId().equals(1435297L)) {
                                                tokenHistoryRepo.delete(tokenHistory);
                                            }
                                        });
                            }
                        }
                    });
            for (MintOrder errOrder : errOrders) {
                System.out.println("remove orderId=" + errOrder.getId() + ", userId=" + errOrder.getUserId() + ", activityId=" + errOrder.getMintActivityId());
            }
        }).get();

    }

    @Test
    public void fixDuplicated1() throws ExecutionException, InterruptedException {
        new ForkJoinPool(3000).submit(() -> {
            List<MintOrder> mintOrders = mintOrderRepo.findByStatusAndCreatedAtAfterOrderByCreatedAtDesc(MintOrderStatus.AIR_DROP, LocalDateTime.of(2022, 5, 20, 0, 0, 0));
            for (MintOrder userErrOrder : mintOrders.subList(0, 100)) {
                System.out.println(userErrOrder.getId());
                userErrOrder.setMaterial(mintMaterialRepo.findAllByOrderIdAndDelFalse(userErrOrder.getId()));
                userErrOrder.setStatus(MintOrderStatus.CANCELLED);
                userErrOrder.setCancelTime(LocalDateTime.now());
                mintOrderRepo.save(userErrOrder);
                PayMethod payMethod = userErrOrder.getPayMethod();
                if (PayMethod.ALIPAY == payMethod) {
                    if (StringUtils.length(userErrOrder.getTransactionId()) == 28) {
                        payMethod = PayMethod.HMPAY;
                    } else if (StringUtils.length(userErrOrder.getTransactionId()) == 30) {
                        payMethod = PayMethod.SANDPAY;
                    }
                }
                try {
                    switch (payMethod) {
                        case HMPAY:
                            orderPayService.refund(userErrOrder.getId()
                                    .toString(), userErrOrder.getTransactionId(), userErrOrder.getGasPrice(), "hmPay");
                            break;
                        case SANDPAY:
                            orderPayService.refund(userErrOrder.getId()
                                    .toString(), userErrOrder.getTransactionId(), userErrOrder.getGasPrice(), "sandPay");
                            break;
                        case PAYEASE:
                            orderPayService.refund(userErrOrder.getTransactionId(), userErrOrder.getTransactionId(), userErrOrder.getGasPrice(), "payEase");
                            break;
                    }
                } catch (Exception e) {
                }
                for (MintMaterial material : userErrOrder.getMaterial()) {
                    Long assetId = material.getAssetId();
                    Asset asset = assetRepo.findById(assetId).get();
                    asset.setStatus(AssetStatus.NORMAL);
                    asset.getPrivileges().forEach(p -> {
                        if (p.getName().contains("铸造")) {
                            p.setOpened(false);
                            p.setOpenTime(null);
                            p.setOpenedBy(null);
                        }
                    });
                    asset.setOwnerId(asset.getUserId());
                    assetRepo.saveAndFlush(asset);
                    userRepo.updateAssetOwner(asset.getUserId());
                    tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(asset.getTokenId()).stream().findFirst()
                            .ifPresent(tokenHistory -> {
                                if (tokenHistory.getToUserId().equals(1435297L)) {
                                    tokenHistoryRepo.delete(tokenHistory);
                                }
                            });
                }
            }
        }).get();

    }

    @Test
    public void fix() {
        for (MintOrder order : mintOrderRepo.findByMintActivityIdAndStatusOrderById(7584417L, MintOrderStatus.AIR_DROP)
                .subList(0, 3)) {
            PayQuery query = orderPayService.query(order.getId().toString());
            if (query.isExist()) {
                order.setStatus(MintOrderStatus.NOT_PAID);
                mintOrderRepo.saveAndFlush(order);
                rocketMQTemplate.syncSend("order-notify-topic",
                        new OrderNotifyEvent(order.getId(), Constants.PayChannel.HM.equals(query.getChannel()) ? PayMethod.HMPAY : PayMethod.SANDPAY,
                                query.getTransactionId(), System.currentTimeMillis(), OrderNotifyEvent.TYPE_MINT_ORDER));
            }
        }
    }
}