package com.ruoyi.project.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SpecialCharacterEscaper;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.project.business.domain.TbContractCurrency;
import com.ruoyi.project.business.domain.TqQuotation;
import com.ruoyi.project.business.domain.vo.currency.*;
import com.ruoyi.project.business.enums.DelFlagEnum;
import com.ruoyi.project.business.mapper.TbContractCurrencyMapper;
import com.ruoyi.project.business.service.ITbContractCurrencyService;
import com.ruoyi.project.business.service.quotation.ITqQuotationService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 币种汇率Service业务层处理
 *
 * @author ruoyi
 * @date 2024-05-14
 */
@Service
@RequiredArgsConstructor
public class TbContractCurrencyServiceImpl extends ServiceImpl<TbContractCurrencyMapper, TbContractCurrency> implements ITbContractCurrencyService {

    private final ITqQuotationService tqQuotationService;

    /**
     * 查询币种汇率列表
     *
     * @param currencyReq 币种汇率
     * @return 币种汇率
     */
    @Override
    public List<TbContractCurrency> selectTbContractCurrencyList(ContractCurrencyReq currencyReq) {
        //// 构建查询条件
        //Wrapper<TbContractCurrency> wrapper = Wrappers.<TbContractCurrency>lambdaQuery()
        //        .like(StringUtils.isNotBlank(currencyReq.getCurrencyName()), TbContractCurrency::getCurrencyName, currencyReq.getCurrencyName())
        //        .eq(currencyReq.getExchangeRate() != null, TbContractCurrency::getExchangeRate, currencyReq.getExchangeRate())
        //        .between(currencyReq.getBeginTime() != null && currencyReq.getEndTime() != null, TbContractCurrency::getBeginTime, currencyReq.getBeginTime(), currencyReq.getEndTime())
        //        .like(StringUtils.isNotBlank(currencyReq.getRemark()), TbContractCurrency::getRemark, currencyReq.getRemark());
        //wrapper.getExpression()
        //        .add(Wrappers.<SysUser>lambdaQuery()
        //                .like(StringUtils.isNotBlank(currencyReq.getCreateUserNick()), SysUser::getNickName, currencyReq.getCreateUserNick()));
        //
        //return baseMapper.selectTbContractCurrencyList2(wrapper);
        SpecialCharacterEscaper.escapeSpecialCharacters(currencyReq, ContractCurrencyReq::getCreateUserNick);
        return baseMapper.selectTbContractCurrencyList(currencyReq);

    }

    @Override
    public boolean add(ContractCurrencySaveReq tbCurrency) {
        if (tbCurrency.getDateRange().size() == 2) {
            tbCurrency.setEffectiveDate(tbCurrency.getDateRange().get(0).atStartOfDay());
            tbCurrency.setExpirationDate(LocalDateTime.of(tbCurrency.getDateRange().get(1), LocalTime.MAX));
        }

        // 校验有效时间范围不能重叠
        validOverlap(tbCurrency);

        return save(tbCurrency);

    }

    @Override
    public List<TbContractCurrency> getUsableCurrencyRate() {
        return this.list(Wrappers.<TbContractCurrency>lambdaQuery()
            .le(TbContractCurrency::getEffectiveDate, DateUtils.getNowDate())
            .ge(TbContractCurrency::getExpirationDate, DateUtils.getNowDate())
            .eq(TbContractCurrency::getDelFlag, DelFlagEnum.NORMAL.getValue())
        );
    }

    @Override
    public ContractCurrencyRes getInfo(Long currencyId) {
        TbContractCurrency currency = getById(currencyId);
        ContractCurrencyRes currencyRes = new ContractCurrencyRes();
        BeanUtils.copyProperties(currency, currencyRes);
        currencyRes.setDateRange(
            Arrays.asList(currency.getEffectiveDate().toLocalDate(), currency.getExpirationDate().toLocalDate()));
        return currencyRes;
    }

    @Override
    public List<TbContractCurrency> listAll() {
        return this.list();
    }

    @Override
    public boolean edit(ContractCurrencySaveReq tbCurrency) {
        if (tbCurrency.getDateRange().size() == 2) {
            tbCurrency.setEffectiveDate(tbCurrency.getDateRange().get(0).atStartOfDay());
            // tbCurrency.setExpirationDate(LocalDateTime.of(tbCurrency.getDateRange().get(1), LocalTime.MAX));
            // 使用上面的方式会导致数据库保存的时间四舍五入变成第二天的起始
            // 手动添加 23:59:59
            LocalDate date = tbCurrency.getDateRange().get(1);
            tbCurrency.setExpirationDate(
                LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(), 23, 59, 59));
        }

        // 校验有效时间范围不能重叠
        validOverlap(tbCurrency);

        return updateById(tbCurrency);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean remove(List<Long> ids) {
        // 校验 tqQuotationService 中是否已经使用该币种
        List<Long> currencyIds = tqQuotationService.list(
                Wrappers.<TqQuotation>lambdaQuery().in(TqQuotation::getCurrencyId, ids))
            .stream().map(TqQuotation::getCurrencyId).distinct().collect(Collectors.toList());
        if (!currencyIds.isEmpty()) {
            // 抛异常：哪些币种已经被使用
            List<TbContractCurrency> tbCurrencies = listByIds(currencyIds);
            throw new RuntimeException(MessageUtils.message("currency.error.currencyUsed",
                tbCurrencies.stream().map(TbContractCurrency::getCurrencyName).toArray()));
        }
        return removeBatchByIds(ids);
    }

    /**
     * 校验有效时间范围不能重叠 更新时排除当前记录
     *
     * @param tbCurrency 币种信息
     */
    private void validOverlap(ContractCurrencySaveReq tbCurrency) {
        // 根据币种名称查询币种信息，校验有效时间范围不能重叠 , 排除当前记录
        LocalDateTime effectiveDate = tbCurrency.getEffectiveDate();
        LocalDateTime expirationDate = tbCurrency.getExpirationDate();

        LambdaQueryWrapper<TbContractCurrency> wrapper = Wrappers.lambdaQuery(TbContractCurrency.class).last(" limit 1")
            .eq(TbContractCurrency::getCurrencyName, tbCurrency.getCurrencyName())
            .and(item ->
                item.ge(TbContractCurrency::getEffectiveDate, effectiveDate).le(TbContractCurrency::getEffectiveDate, expirationDate)
                    .or(k -> k.le(TbContractCurrency::getEffectiveDate, effectiveDate)
                        .ge(TbContractCurrency::getExpirationDate, expirationDate))
                    .or(m -> m.ge(TbContractCurrency::getExpirationDate, effectiveDate)
                        .le(TbContractCurrency::getExpirationDate, expirationDate))
            )
            .ne(tbCurrency.getCurrencyId() != null, TbContractCurrency::getCurrencyId, tbCurrency.getCurrencyId());

        // 存在重叠
        boolean existOverlap = baseMapper.exists(wrapper);
        if (existOverlap) {
            throw new RuntimeException(MessageUtils.message("currency.error.dateRangeOverlap"));
        }
    }

    @Override
    @Transactional
    public Map<Long, BigDecimal> selectExchangeRateByCurrencyId(List<ContractCurrencyTime> contractCurrencyTimeList){
        Map<Long, BigDecimal> tbContractCurrencyMap = new HashMap<>();
        if (contractCurrencyTimeList.stream().anyMatch(contractCurrencyTime -> contractCurrencyTime.getFindTime() == null)) {
            return tbContractCurrencyMap;
        }
        List<CurrencyRes> tbContractCurrencyList = baseMapper.selectExchangeRateByCurrencyId(contractCurrencyTimeList);
        if(CollectionUtils.isNotEmpty(tbContractCurrencyList)){
            tbContractCurrencyMap = tbContractCurrencyList.stream().collect(Collectors.toMap(CurrencyRes::getCurrencyId, CurrencyRes::getExchangeRate));
        }
        return  tbContractCurrencyMap;
    }
}
