package com.izouma.nineth.service;

import com.izouma.nineth.domain.BonusGive;
import com.izouma.nineth.domain.BonusGiveItem;
import com.izouma.nineth.domain.DestroyRecord;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.enums.BalanceType;
import com.izouma.nineth.repo.BonusGiveItemRepo;
import com.izouma.nineth.repo.BonusGiveRepo;
import com.izouma.nineth.repo.DestroyRecordRepo;
import com.izouma.nineth.utils.JpaUtils;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class BonusGiveService {

    private BonusGiveRepo      bonusGiveRepo;
    private BonusGiveItemRepo  bonusGiveItemRepo;
    private DestroyRecordRepo  destroyRecordRepo;
    private UserBalanceService userBalanceService;

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

    public List<BonusGiveItem> preview(BonusGive bonusGive) {
        Objects.requireNonNull(bonusGive, "bonusGive is null");
        Objects.requireNonNull(bonusGive.getPattern(), "bonusGive.pattern is null");
        Objects.requireNonNull(bonusGive.getStartTime(), "bonusGive.startTime is null");
        Objects.requireNonNull(bonusGive.getEndTime(), "bonusGive.endTime is null");

        List<DestroyRecord> destroyRecords = destroyRecordRepo.findByCreatedAtBetween(bonusGive.getStartTime(), bonusGive.getEndTime());
        Pattern pattern = Pattern.compile(bonusGive.getPattern());
        List<Long> ids = new ArrayList<>();
        return destroyRecords.stream()
                .filter(destroyRecord -> {
                    if (ids.contains(destroyRecord.getAssetId())) {
                        return false;
                    }
                    ids.add(destroyRecord.getAssetId());
                    Matcher matcher = pattern.matcher(destroyRecord.getName());
                    return matcher.matches();
                })
                .map(destroyRecord -> BonusGiveItem.builder()
                        .bonusGiveId(bonusGive.getId())
                        .userId(destroyRecord.getUserId())
                        .assetId(destroyRecord.getAssetId())
                        .assetName(destroyRecord.getName())
                        .bonus(bonusGive.getBonus())
                        .destroyId(destroyRecord.getId())
                        .destroyTime(destroyRecord.getCreatedAt())
                        .build())
                .collect(Collectors.toList());
    }

    @Transactional
    public BonusGive save(BonusGive bonusGive) {
        List<BonusGiveItem> bonusGiveItems = preview(bonusGive);
        bonusGive.setAssetNum(bonusGiveItems.size());
        bonusGive.setTotalBonus(bonusGiveItems.stream().map(BonusGiveItem::getBonus)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        bonusGiveRepo.save(bonusGive);
        bonusGiveItems.forEach(bonusGiveItem -> bonusGiveItem.setBonusGiveId(bonusGive.getId()));
        bonusGiveItemRepo.saveAll(bonusGiveItems);

        for (BonusGiveItem bonusGiveItem : bonusGiveItems) {
            userBalanceService.modifyBalance(bonusGiveItem.getUserId(), bonusGiveItem.getBonus(), BalanceType.BONUS, null, false, null);
        }

        return bonusGive;
    }
}
