package com.izouma.nineth.service;

import com.izouma.nineth.TokenHistory;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.enums.AirDropType;
import com.izouma.nineth.enums.CollectionType;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.utils.JpaUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Slf4j
public class AirDropService {
    @Autowired
    private AirDropRepo             airDropRepo;
    @Autowired
    private CouponRepo              couponRepo;
    @Autowired
    private UserCouponRepo          userCouponRepo;
    @Autowired
    private CollectionRepo          collectionRepo;
    @Autowired
    private UserRepo                userRepo;
    @Lazy
    @Autowired
    private AssetService            assetService;
    @Autowired
    private CollectionService       collectionService;
    @Autowired
    private ShowroomService         showroomService;
    @Autowired
    private TokenHistoryRepo        tokenHistoryRepo;
    @Autowired
    private AssetRepo               assetRepo;
    @Autowired
    private CollectionPrivilegeRepo collectionPrivilegeRepo;
    @Autowired
    private SysConfigService        sysConfigService;

    public Page<AirDrop> all(PageQuery pageQuery) {
        return airDropRepo
                .findAll(JpaUtils.toSpecification(pageQuery, AirDrop.class), JpaUtils.toPageRequest(pageQuery));
    }

    public AirDrop create(AirDrop record) {
        boolean parallel = sysConfigService.getBoolean("parallel_airdrop");
        if (record.getTargets().isEmpty()) throw new BusinessException("空投对象不能为空");
        if (record.getTargets().stream().mapToInt(DropTarget::getNum).sum() > 300)
            throw new BusinessException("空投数量不能超过300");
        if (AirDropType.coupon == record.getType()) {
            Coupon coupon = couponRepo.findById(record.getCouponId()).orElseThrow(new BusinessException("兑换券不存在"));
            for (DropTarget target : record.getTargets()) {
                for (int i = 0; i < target.getNum(); i++) {
                    UserCoupon userCoupon = new UserCoupon();
                    BeanUtils.copyProperties(coupon, userCoupon);
                    userCoupon.setId(null);
                    userCoupon.setCouponId(coupon.getId());
                    userCoupon.setUserId(target.getUserId());
                    userCouponRepo.save(userCoupon);
                }
            }
        } else {
            Collection collection = collectionRepo.findById(record.getCollectionId())
                    .orElseThrow(new BusinessException("藏品不存在"));
            if (collection.isSalable()) {
                throw new BusinessException("请先设置藏品为不可购买");
            }
            if (!record.isIgnoreStockCheck() && collection.getStock() < record.getTargets().stream().mapToInt(DropTarget::getNum).sum()) {
                throw new BusinessException("藏品库存不足");
            }

            List<User> users = userRepo.findByIdInAndDelFalse(record.getTargets().stream()
                    .map(DropTarget::getUserId).collect(Collectors.toList()));

            Stream<DropTarget> stream = record.getTargets().stream();
            if (parallel) {
                stream = stream.parallel();
            }
            stream.forEach(target -> {
                User user = users.stream().filter(u -> u.getId().equals(target.getUserId()))
                        .findFirst().orElse(null);
                if (user == null) return;
                try {
                    for (int i = 0; i < target.getNum(); i++) {
                        if (collection.getType() == CollectionType.BLIND_BOX) {
                            BlindBoxItem winItem = collectionService.draw(target.getUserId(), collection.getId());
                            if (record.isSimulateOrder()) {
                                assetService.createAsset(winItem, user, 0L, collection.getPrice(), "出售",
                                        winItem.getTotal() > 1 ?
                                                collectionService.getNextNumber(winItem.getCollectionId()) : null,
                                        collection.getHoldDays(), false);
                            } else {
                                //查看有无vip权限
                                CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo
                                        .findByCollectionId(record.getCollectionId());
                                if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
                                    if (collectionPrivilege.isVip()) {
                                        //更新vip信息
                                        userRepo.updateVipPurchase(user.getId(), 1);
                                    }
                                }
                                Asset asset = assetService.createAsset(winItem, user, null, null, "空投",
                                        winItem.getTotal() > 1 ?
                                                collectionService.getNextNumber(winItem.getCollectionId()) : null,
                                        collection.getHoldDays(), false);
                                //铸造空投的t+0
                                if (record.isAuto()) {
                                    assetRepo.updateHoldDays(asset.getId(), 0);
                                    log.info("合成{}，T+0", asset.getId());
                                }
                            }
                        } else {
                            if (record.isSimulateOrder()) {
                                assetService.createAsset(collection, user, 0L, collection.getPrice(),
                                        "出售", collectionService.getNextNumber(collection), false);
                            } else {
                                //查看有无vip权限
                                CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo
                                        .findByCollectionId(record.getCollectionId());
                                if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
                                    if (collectionPrivilege.isVip()) {
                                        //更新vip信息
                                        userRepo.updateVipPurchase(user.getId(), 1);
                                    }
                                }
                                Asset asset = assetService.createAsset(collection, user, null, null,
                                        "空投", collectionService.getNextNumber(collection), false);

                                //创建展厅
                                if (collection.getType() == CollectionType.SHOWROOM) {
                                    asset.setOasisId(record.getOasisId());
                                    showroomService.save(asset);
                                }
                                //铸造空投的t+0
                                if (record.isAuto()) {
                                    assetRepo.updateHoldDays(asset.getId(), 0);
                                    log.info("合成{}，T+0", asset.getId());
                                }
                            }

                        }
                        collectionService.decreaseStock(collection.getId(), 1);
                        collectionService.increaseSale(collection.getId(), 1);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("空投出错", e);
                }
            });
        }
        return airDropRepo.save(record);
    }

    @Async
    public void asyncDrop(Long collectionId, Long userId, int num, LocalDateTime time) {
        drop(collectionId, userId, num, time);
    }

    /**
     * 空投（交易历史显示为购买）
     *
     * @param collectionId 藏品id
     * @param userId       用户id
     * @param num          空投数量
     * @param time         购买时间
     */
    public void drop(Long collectionId, Long userId, int num, LocalDateTime time) {
        Collection collection = collectionRepo.findById(collectionId)
                .orElseThrow(new BusinessException("藏品不存在"));
        User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
        try {
            for (int i = 0; i < num; i++) {
                Asset asset;
                if (collection.getType() == CollectionType.BLIND_BOX) {
                    BlindBoxItem winItem = collectionService.draw(userId, collection.getId());
                    asset = assetService.createAsset(winItem, user, 0L, collection.getPrice(), "出售",
                            collectionService.getNextNumber(winItem), collection.getHoldDays(), true);
                } else {
                    asset = assetService.createAsset(collection, user, 0L, collection.getPrice(), "出售",
                            collectionService.getNextNumber(collection), true);
                }
                assetRepo.flush();
                tokenHistoryRepo.flush();

                asset.setCreatedAt(time.plusSeconds((long) (Math.random() * 120)));
                assetRepo.save(asset);
                for (TokenHistory tokenHistory : tokenHistoryRepo
                        .findByTokenIdOrderByCreatedAtDesc(asset.getTokenId())) {
                    new Thread(() -> {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        tokenHistoryRepo.findById(tokenHistory.getId())
                                .ifPresent(t -> {
                                    t.setCreatedAt(time.plusSeconds((long) (Math.random() * 120)));
                                    tokenHistoryRepo.save(t);
                                });
                    }).start();
                }
                log.info("空投成功{}/{} collectionId={}, userId={}", i + 1, num, collectionId, userId);
            }
        } catch (Exception e) {
            log.error("空投出错", e);
        }
    }
}
