package com.izouma.nineth.service;

import com.izouma.nineth.domain.TokenHistory;
import com.izouma.nineth.config.RedisKeys;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.AirDropExcelDTO;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.enums.AirDropType;
import com.izouma.nineth.enums.CollectionType;
import com.izouma.nineth.enums.ItemType;
import com.izouma.nineth.enums.OperationType;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.lock.RedisLockException;
import com.izouma.nineth.lock.RedisLockable;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.utils.JpaUtils;
import com.izouma.nineth.utils.excel.ExcelUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Slf4j
public class AirDropService {

    private AirDropRepo airDropRepo;
    private CouponRepo couponRepo;
    private UserCouponRepo userCouponRepo;
    private CollectionRepo collectionRepo;
    private UserRepo userRepo;
    private AssetService assetService;
    private CollectionService collectionService;
    private TokenHistoryRepo tokenHistoryRepo;
    private UserWareHouseRepo userWareHouseRepo;
    private ActivitySurpriseBoxRepo activitySurpriseBoxRepo;
    private SurpriseBoxLogRepo surpriseBoxLogRepo;
    private final Random random = new Random();

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

    /**
     * 惊喜盒子专用
     *
     * @param record
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public AirDrop surpriseBoxAirDrop(AirDrop record) {
        final AtomicInteger dropCount = new AtomicInteger();
        if (record.getDropCount() != null && record.getDropCount() > 0) {
            dropCount.set(record.getDropCount());
        } else {
            dropCount.set(record.getUserIds().size());
        }

        if (AirDropType.SURPRISE_BOX != record.getType()){
            throw new BusinessException("不支持其他空投类型");
        }

        ActivitySurpriseBox surpriseBox = activitySurpriseBoxRepo.findById(record.getSurpriseBoxId())
                .orElseThrow(new BusinessException("惊喜盒子不存在"));

        if (surpriseBox.getBoxSum() < dropCount.get()) {
            throw new BusinessException("盒子库存不足");
        }

        record.getUserIds().forEach(userId -> {

            userWareHouseRepo.save(UserWareHouse.builder()
                    .name(record.getName())
                    .userId(userId)
                    .disabled(true)
                    .projectId(record.getProjectId())
                    .itemId(ItemType.SURPRISE_BOX.ordinal())
                    .itemName(ItemType.SURPRISE_BOX)
                    .overdueTime(surpriseBox.getOverdueTime())
                    .targetCollectionId(record.getSurpriseBoxId()).build());
        });

        //变更数量
        activitySurpriseBoxRepo.decreaseStockAndIncreaseSale(surpriseBox.getId(), dropCount.get());

        record.setDropCount(dropCount.get());
        return airDropRepo.save(record);
    }

    /**
     * 空投锁定，不能并发执行
     *
     * @param record
     * @return
     */
    @RedisLockable(prefix = RedisKeys.CREATE_AIR_DROP_LOCK, key = "#record.couponId + '_' + #record.collectionId", expiration = 360,isWaiting = true, retryCount=10)
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public AirDrop create(AirDrop record) {
        final AtomicInteger dropCount = new AtomicInteger();
        boolean randomDrop = false;
        if (record.getDropCount() != null && record.getDropCount() > 0) {
            dropCount.set(record.getDropCount());
            randomDrop = true;
        } else {
            dropCount.set(record.getUserIds().size());
        }

        if (AirDropType.SURPRISE_BOX == record.getType()) {
            ActivitySurpriseBox surpriseBox = activitySurpriseBoxRepo.findById(record.getSurpriseBoxId()).orElseThrow(new BusinessException("惊喜盒子不存在"));
            if (surpriseBox.getBoxSum() < dropCount.get()) {
                throw new BusinessException("盒子库存不足");
            }

            record.getUserIds().forEach(userId -> userWareHouseRepo.save(UserWareHouse.builder()
                    .name(record.getName())
                    .userId(userId)
                    .disabled(true)
                    .projectId(record.getProjectId())
                    .itemId(ItemType.SURPRISE_BOX.ordinal())
                    .itemName(ItemType.SURPRISE_BOX)
                    .overdueTime(surpriseBox.getOverdueTime())
                    .targetCollectionId(record.getSurpriseBoxId()).build()));
            surpriseBox.setBoxSum(surpriseBox.getBoxSum() - dropCount.get());
            surpriseBox.setGrantCount(surpriseBox.getGrantCount() + dropCount.get());
            activitySurpriseBoxRepo.save(surpriseBox);
        } else if (AirDropType.coupon == record.getType()) {
            Coupon coupon = couponRepo.findById(record.getCouponId()).orElseThrow(new BusinessException("兑换券不存在"));
            record.getUserIds().stream().parallel().forEach(userId -> {
                UserCoupon userCoupon = new UserCoupon();
                BeanUtils.copyProperties(coupon, userCoupon);
                userCoupon.setId(null);
                userCoupon.setCouponId(coupon.getId());
                userCoupon.setUserId(userId);
                userCouponRepo.save(userCoupon);
            });
        } else {
            //不锁定记录，不修改藏品对象
            Collection collection = collectionRepo.findById(record.getCollectionId())
                    .orElseThrow(new BusinessException("藏品不存在"));
            if (collection.getStock() < dropCount.get()) {
                throw new BusinessException("藏品库存不足");
            }
            List<User> users = userRepo.findByIdInAndDelFalse(record.getUserIds());
            String operation = "空投";
            if (record.getOperation() != null) {
                operation = record.getOperation();
                if ("空投".equals(record.getOperation())) {
                    record.setDropPrice(null);
                } else if ("出售".equals(record.getOperation())) {
                    record.setDropPrice(collection.getPrice());
                } else {
                    throw new BusinessException("不支持的操作内容：" + record.getOperation());
                }
            }
            record.setOperation(operation);
            //按指定数量投放
            for (int i = 0; i < dropCount.get(); i++) {
                User dropUser;
                if (randomDrop) {
                    int userIndex = RandomUtils.nextInt(0, users.size());
                    dropUser = users.get(userIndex);
                } else {
                    dropUser = users.get(i);
                }

                Asset dropAsset;
                if (collection.getType() == CollectionType.BLIND_BOX && !collection.isVariableType()) {
                    BlindBoxItem winItem = collectionService.draw(collection.getId());
                    dropAsset = assetService.createAsset(collection,winItem, dropUser, null, record.getDropPrice(), operation);
                } else {
                    dropAsset = assetService.createAsset(collection, dropUser, null, record.getDropPrice(), operation);
                }

                if (randomDrop) {
                    //随机一小时内的时间
                    int randomSecond = RandomUtils.nextInt(0, 60 * 60);
                    LocalDateTime randomTime = LocalDateTime.now().minusSeconds(randomSecond);
                    TokenHistory tokenHistory = tokenHistoryRepo.findByAssetId(dropAsset.getId())
                            .orElseThrow(new BusinessException("未找到交易历史"));
                    tokenHistory.setCreatedAt(randomTime);
                    dropAsset.setCreatedAt(randomTime);

                }

                //变更库存和销量,同时处理
                collectionRepo.decreaseStockAndIncreaseSale(collection.getId(), 1);

            }


        }
        record.setDropCount(dropCount.get());
        if (!record.isNotSave()){
            return airDropRepo.save(record);
        }
        return record;
    }


    /**
     * 空投锁定，不能并发执行 批量
     *
     * @param record
     * @return
     */
    @RedisLockable(prefix = RedisKeys.CREATE_AIR_DROP_LOCK, key = "#record.couponId + '_' + #record.collectionId", expiration = 360,isWaiting = true, retryCount=10)
    @Transactional
    public AirDrop batchCreate(AirDrop record) {
        final AtomicInteger dropCount = new AtomicInteger();
        dropCount.set(record.getUserIds().size() * record.getDropCount());

        if (AirDropType.SURPRISE_BOX == record.getType()) {
            ActivitySurpriseBox surpriseBox = activitySurpriseBoxRepo.findById(record.getSurpriseBoxId()).orElseThrow(new BusinessException("惊喜盒子不存在"));
            if (dropCount.get() > surpriseBox.getBoxSum()) {
                throw new BusinessException("盒子库存不足");
            }
            //按指定数量投放
            List<UserWareHouse> userWareHouselist = new ArrayList<>();
            for (int i = 0; i < record.getDropCount(); i++) {
                record.getUserIds().forEach(userId -> userWareHouselist.add(UserWareHouse.builder()
                        .name(record.getName())
                        .userId(userId)
                        .disabled(true)
                        .projectId(record.getProjectId())
                        .itemId(ItemType.SURPRISE_BOX.ordinal())
                        .itemName(ItemType.SURPRISE_BOX)
                        .overdueTime(surpriseBox.getOverdueTime())
                        .targetCollectionId(record.getSurpriseBoxId()).build()));
            }
            userWareHouseRepo.saveAll(userWareHouselist);
            surpriseBox.setBoxSum(surpriseBox.getBoxSum() - dropCount.get());
            surpriseBox.setGrantCount(surpriseBox.getGrantCount() + dropCount.get());
            activitySurpriseBoxRepo.save(surpriseBox);
        } else if (AirDropType.coupon == record.getType()) {
            Coupon coupon = couponRepo.findById(record.getCouponId()).orElseThrow(new BusinessException("兑换券不存在"));
            List<UserCoupon> userCouponlist = new ArrayList<>();
            for (int i = 0; i < record.getDropCount(); i++) {
                record.getUserIds().stream().parallel().forEach(userId -> {
                    UserCoupon userCoupon = new UserCoupon();
                    BeanUtils.copyProperties(coupon, userCoupon);
                    userCoupon.setId(null);
                    userCoupon.setCouponId(coupon.getId());
                    userCoupon.setUserId(userId);
                    userCouponlist.add(userCoupon);
                });
            }
            userCouponRepo.saveAll(userCouponlist);
        } else {
            //不锁定记录，不修改藏品对象
            Collection collection = collectionRepo.findById(record.getCollectionId()).orElseThrow(new BusinessException("藏品不存在"));
            if (collection.getStock() < dropCount.get()) {
                throw new BusinessException("藏品库存不足");
            }
            String operation = OperationType.AIRDROP.getDescription();
            if (record.getOperation() != null) {
                operation = record.getOperation();
                if (OperationType.AIRDROP.getDescription().equals(record.getOperation())) {
                    record.setDropPrice(null);
                } else if (OperationType.SELL.getDescription().equals(record.getOperation())) {
                    record.setDropPrice(collection.getPrice());
                }
            }
            List<User> users = userRepo.findByIdInAndDelFalse(record.getUserIds());
            record.setOperation(operation);
            //按指定数量投放
            Asset givenAsset = null;
            for (int i = 0; i < record.getUserIds().size(); i++) {
                for (int j = 0; j < record.getDropCount(); j++) {
                    User dropUser = users.get(i);
                    if (collection.getType() == CollectionType.BLIND_BOX && !collection.isVariableType()) {
                        BlindBoxItem winItem = collectionService.draw(collection.getId());
                        givenAsset = assetService.createAsset(collection, winItem, dropUser, null, record.getDropPrice(), operation);
                    } else {
                        givenAsset = assetService.createAsset(collection, dropUser, null, record.getDropPrice(), operation);
                    }
                    //变更库存和销量,同时处理
                    collectionRepo.decreaseStockAndIncreaseSale(collection.getId(), 1);
                }
            }
            record.setAsset(givenAsset);
        }
        record.setDropCount(dropCount.get());
        if (!record.isNotSave()){
            return airDropRepo.save(record);
        }
        return record;
    }

    @Transactional
    public void createWithUpload(InputStream inputStream, Long collectionId) throws IOException {
        try (inputStream) {
            List<AirDropExcelDTO> excelDataList =
                    ExcelUtils.readExcel(inputStream, AirDropExcelDTO.class, 1, 1);
            List<Long> userIds = excelDataList.stream().map(AirDropExcelDTO::getUserId).collect(Collectors.toList());

            batchCreate(userIds, collectionId);
        }

    }

    @RedisLockable(prefix = RedisKeys.CREATE_AIR_DROP_LOCK, key = "null + '_' + #collectionId", expiration = 360)
    @Transactional
    public AirDrop batchCreate(List<Long> userIds, Long collectionId) {
        AirDrop record = new AirDrop();
        record.setType(AirDropType.asset);
        record.setCollectionId(collectionId);
        record.setName("批量空投");
        record.setProjectId(0);
        record.setRemark("批量空投");
        record.setUserIds(userIds);

        //执行空投
        return create(record);
    }


    /**
     * 开惊喜盒子
     *
     * @param user
     * @return
     */
    @RedisLockable(prefix = RedisKeys.OPEN_SURPRISE_BOX_LOCK, key = "#surpriseBoxId", expiration = 360)
    @Transactional
    public SurpriseBoxLog openSurpriseBox(User user,Long surpriseBoxId) {
        UserWareHouse box = userWareHouseRepo.findById(surpriseBoxId).orElseThrow(new BusinessException("盒子不存在"));
        if (!box.getItemName().equals(ItemType.SURPRISE_BOX)) throw new BusinessException("这个不是惊喜盒子");
        if (!box.isDisabled()) throw new BusinessException("盒子已经开过了");
        if (box.getOverdueTime()!= null && LocalDateTime.now().isAfter(box.getOverdueTime())){
            throw new BusinessException("当前盒子已过期");
        }
        if (box.getTargetCollectionId() == null) {
            box.setDisabled(false);
            userWareHouseRepo.save(box);
            throw new BusinessException("感谢您的参与！");
        }

        if (Objects.equals(box.getUserId(), user.getId())) {
            ActivitySurpriseBox activitySurpriseBox = activitySurpriseBoxRepo.findById(box.getTargetCollectionId()).orElseThrow(new BusinessException("盒子配置不存在"));
            int stock = activitySurpriseBox.getProperties().stream().mapToInt(SurpriseBoxContent::getStock).sum();
            SurpriseBoxContent boxConfig;//实际开奖的配置
            SurpriseBoxContent boxConfig0 = null;//本次开奖抽到的配置
            SurpriseBoxContent boxConfig1 = null;//空盒子配置（备用）
            if (stock > 0) {
                int next = random.nextInt(stock);
                for (SurpriseBoxContent boxContent : activitySurpriseBox.getProperties()) {
                    if (boxContent.getType() == 2) {
                        boxConfig1 = boxContent;
                    }
                    if (boxContent.getStock() <= 0) continue;
                    next = next - boxContent.getStock();
                    if (next <= 0 && boxConfig0 == null) {
                        boxConfig0 = boxContent;
                    }
                }
            }
            boxConfig = boxConfig0 == null ? boxConfig1 : boxConfig0;
            //没库存了 容错开个空盒子
            if (stock <= 0 || boxConfig == null) {
                box.setDisabled(false);
                userWareHouseRepo.save(box);
                return surpriseBoxLogRepo.save(SurpriseBoxLog.builder()
                        .name(activitySurpriseBox.getName())
                        .picUrl("")
                        .remarks("")
                        .phone(user.getPhone())
                        .shipments(true)
                        .type(2)
                        .projectId(box.getProjectId())
                        .user_id(user.getId())
                        .build());
            }
            Asset asset = null;
            //如果是藏品 或者盲盒
            if (boxConfig.getType() == 0) {
                Collection collection = collectionRepo.findById(boxConfig.getCollectionId()).orElseThrow(new BusinessException("藏品不存在"));
                if (collection.getStock() < 1) {
                    throw new BusinessException("藏品库存不足");
                }
                try {
                    if (collection.getType() == CollectionType.BLIND_BOX) {
                        BlindBoxItem winItem = collectionService.draw(collection.getId());
                        asset = assetService.createAsset(collection,winItem, user, null, null, OperationType.SURPRISE_BOX.getDescription());
                    } else {
                        asset = assetService.createAsset(collection, user, null, null, OperationType.SURPRISE_BOX.getDescription());
                    }
                    //变更库存和销量,同时处理
                    collectionRepo.decreaseStockAndIncreaseSale(collection.getId(), 1);
                    boxConfig.setPicUrl(collection.getPic().get(0).getUrl());
                }catch(RedisLockException err){
                    log.error("抽盲合失败  藏品ID：{}", collection.getId());
                    throw new BusinessException("用力过猛，请重试一次");
                }catch (Exception e1) {
                    log.error("开惊喜盒子出错, 用户ID:" + user.getId(), e1);
                    throw new BusinessException("请联系客服");
                }
            }
            //废掉盒子
            box.setDisabled(false);
            userWareHouseRepo.save(box);
            //扣除库存
            boxConfig.setStock(boxConfig.getStock() - 1);

            activitySurpriseBoxRepo.save(activitySurpriseBox);

            return surpriseBoxLogRepo.save(SurpriseBoxLog.builder()
                    .collectionId(boxConfig.getCollectionId())
                    .name(activitySurpriseBox.getName())
                    .picUrl(boxConfig.getPicUrl())
                    .remarks(boxConfig.getRemarks())
                    .phone(user.getPhone())
                    .shipments(asset != null)
                    .projectId(box.getProjectId())
                    .type(boxConfig.getType())
                    .user_id(user.getId()).build());
        } else {
            throw new BusinessException("盒子不是你的");
        }
    }
}
