package com.bonus.canteen.core.marketing.v2.service;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bonus.common.houqin.constant.LeConstants;
import com.bonus.canteen.core.allocation.api.GlobalMetadataApi;
import com.bonus.canteen.core.common.enums.MetadataModelTypeEnum;
import com.bonus.canteen.core.common.utils.LogUtil;
import com.bonus.canteen.core.marketing.util.EnumUtil;
import com.bonus.canteen.core.marketing.v2.constants.MarketEffectPeriodEnum;
import com.bonus.canteen.core.marketing.v2.constants.MarketEffectTimeEnum;
import com.bonus.canteen.core.marketing.v2.constants.MarketModuleTypeEnum;
import com.bonus.canteen.core.marketing.v2.constants.MarketRangeTypeEnum;
import com.bonus.canteen.core.marketing.v2.custom.MarketRuleCustomBusiness;
import com.bonus.canteen.core.marketing.v2.dto.MarketRuleExtDTO;
import com.bonus.canteen.core.marketing.v2.mapper.MarketRuleMapper;
import com.bonus.canteen.core.marketing.v2.model.MarketRule;
import com.bonus.canteen.core.marketing.v2.model.MarketRuleExt;
import com.bonus.canteen.core.marketing.v2.model.MarketRuleRange;
import com.bonus.canteen.core.marketing.v2.rule.param.RuleRangeParam;
import com.bonus.canteen.core.marketing.v2.util.MarketUtil;
import com.bonus.canteen.core.marketing.v2.vo.MarketRuleDetailVO;
import com.bonus.canteen.core.marketing.vo.MarketRuleVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.function.Consumer;
import java.util.function.Function;

@Service
public class MarketRuleService extends ServiceImpl<MarketRuleMapper, MarketRule> {
    private static final Logger log = LoggerFactory.getLogger(MarketRuleService.class);
    @Resource
    private MarketRuleRangeService marketRuleRangeService;
    @Resource
    private MarketRuleExtService marketRuleExtService;
    @Resource
    @Lazy
    private GlobalMetadataApi globalMetadataApi;
    @Resource
    private MarketRuleCustomBusiness marketRuleCustomBusiness;
    @Resource(
            name = "yunshitangTaskExecutor"
    )
    private AsyncTaskExecutor asyncTaskExecutor;

    public List<MarketRuleDetailVO> listRulesByModuleType(Integer moduleType) {
        List<MarketRule> rules = this.list(
                Wrappers.<MarketRule>lambdaQuery().eq(Objects.nonNull(moduleType), MarketRule::getModuleType, moduleType)
                        .eq(MarketRule::getDisableFlag, LeConstants.COMMON_NO).
                        eq(MarketRule::getDelFlag, LeConstants.COMMON_NO)
                        .orderByAsc(MarketRule::getSort)
                        .orderByDesc(MarketRule::getUptime));
        List<MarketRuleDetailVO> results = new ArrayList();
        rules.forEach((e) -> {
            results.add(this.detail(e.getRuleId()));
        });
        return results;
    }

    public MarketRuleDetailVO detail(Long ruleId) {
        MarketRule rule = (MarketRule) this.getById(ruleId);
        MarketRuleDetailVO detailVO = (MarketRuleDetailVO) BeanUtil.copyProperties(rule, MarketRuleDetailVO.class, new String[0]);
        List<MarketRuleRange> ruleRanges = this.marketRuleRangeService.list(Wrappers.<MarketRuleRange>lambdaQuery().eq(MarketRuleRange::getRuleId, ruleId));
        this.formatDetail(detailVO, ruleRanges);
        if (MarketModuleTypeEnum.RULE_MEAL.getKey().equals(rule.getModuleType())) {
            List<MarketRuleExtDTO> ruleExtInfo = this.getRuleExtInfo(ruleId);
            detailVO.setRuleExtInfo(ruleExtInfo);
        }
        return detailVO;
    }

    protected void formatDetail(MarketRuleDetailVO detailVO, List<MarketRuleRange> ruleRanges) {
        Map<Integer, List<MarketRuleRange>> map = ruleRanges.stream().collect(Collectors.groupingBy(MarketRuleRange::getRangeType));

        setDetailList(map, MarketRangeTypeEnum.RANGE_DEVICE.getKey(), detailVO::setDeviceList, MarketRuleRange::getCustomSn);
        setDetailList(map, MarketRangeTypeEnum.RANGE_MEALTIME.getKey(), detailVO::setMealtimeList, MarketRuleRange::getMealtimeType);
        setDetailList(map, MarketRangeTypeEnum.RANGE_ORGANIZATION.getKey(), detailVO::setOrgIdList, MarketRuleRange::getOrgId);
        setDetailList(map, MarketRangeTypeEnum.RANGE_PERSON.getKey(), detailVO::setPsnTypeList, MarketRuleRange::getPsnType);
        setDetailList(map, MarketRangeTypeEnum.RANGE_AGE.getKey(), detailVO::setAgeTypeList, MarketRuleRange::getAgeType);
        setDetailList(map, MarketRangeTypeEnum.RANGE_PAY_CHANNEL.getKey(), detailVO::setPayChannelList, MarketRuleRange::getPayChannel);
    }

    private <T> void setDetailList(Map<Integer, List<MarketRuleRange>> map, Integer key, Consumer<List<T>> setter, Function<MarketRuleRange, T> mapper) {
        Optional.ofNullable(map.get(key)).ifPresent(e -> setter.accept(e.stream().map(mapper).collect(Collectors.toList())));
    }

    protected List<MarketRuleExtDTO> getRuleExtInfo(Long ruleId) {
        List<MarketRuleExt> marketRuleExts = this.marketRuleExtService.list((Wrapper) Wrappers.<MarketRuleExt>lambdaQuery().eq(MarketRuleExt::getRuleId, ruleId));
        Map<String, List<MarketRuleExt>> map = (Map) marketRuleExts.stream().collect(Collectors.groupingBy((a) -> {
            String var10000 = String.valueOf(a.getStartTime());
            return var10000 + "_" + String.valueOf(a.getEndTime());
        }, Collectors.toList()));
        List<MarketRuleExtDTO> ruleExtInfo = new ArrayList();
        map.forEach((k, v) -> {
            MarketRuleExtDTO temp = new MarketRuleExtDTO();
            String[] split = k.split("_");
            temp.setStartTime(LocalTime.parse(split[0]));
            temp.setEndTime(LocalTime.parse(split[1]));
            temp.setPsnTypeList(v.stream().map(MarketRuleExt::getPsnType).filter(Objects::nonNull).collect(Collectors.toList()));
            temp.setOrgIdList(v.stream().map(MarketRuleExt::getOrgId).filter(Objects::nonNull).collect(Collectors.toList()));
            ruleExtInfo.add(temp);
        });
        return ruleExtInfo;
    }

    public List<MarketRuleVO> getSuitableRules(RuleRangeParam ruleRangeParam) {
        List<MarketRuleVO> suitableRules = MarketUtil.listCacheSuitableRulesByRangeParam(ruleRangeParam);
        if (Objects.nonNull(suitableRules)) {
            return this.marketRuleCustomBusiness.afterGetSuitableRules(suitableRules);
        } else {
            this.setRuleFlag(ruleRangeParam.getModuleType());
            MarketModuleTypeEnum type = EnumUtil.getEnumByKey(MarketModuleTypeEnum.class, ruleRangeParam.getModuleType());
            if (Objects.isNull(type)) {
                return null;
            } else {
                switch (type) {
                    case RULE_MEAL:
                        suitableRules = ((MarketRuleMapper)this.baseMapper).listSuitableMealRule(ruleRangeParam);
                        break;
                    case RULE_PRICE:
                        suitableRules = ((MarketRuleMapper)this.baseMapper).listSuitablePriceRule(ruleRangeParam);
                        break;
                    case RULE_PAY:
                        suitableRules = ((MarketRuleMapper)this.baseMapper).listSuitablePayRule(ruleRangeParam);
                        break;
                    case RULE_RECHARGE:
                        suitableRules = ((MarketRuleMapper)this.baseMapper).listSuitableRechargeRule(ruleRangeParam);
                        break;
                    default:
                        suitableRules = new ArrayList();
                }

                suitableRules = suitableRules.stream()
                        .filter(e -> MarketEffectPeriodEnum.operate(e, ruleRangeParam.getTradeDate()) &&
                                MarketEffectTimeEnum.operate(ruleRangeParam, BeanUtil.copyProperties(e, MarketRuleDetailVO.class)))
                        .collect(Collectors.toList());
                return this.marketRuleCustomBusiness.afterGetSuitableRules(suitableRules);
            }
        }
    }
    public void setRuleFlag(Integer moduleType) {
        CompletableFuture.runAsync(() -> {
            LogUtil.generateNewLogTraceId();
            log.info("规则重建缓存，模块类型：{}", moduleType);
            String metadataFileKey = MarketModuleTypeEnum.getMetadataFileKey(moduleType);
            if (!CharSequenceUtil.isBlank(metadataFileKey)) {
                List<MarketRuleDetailVO> marketRuleDetails = MarketUtil.saveOrUpdateRuleCache(moduleType);
                this.globalMetadataApi.saveValue(MetadataModelTypeEnum.MERCHANT, metadataFileKey, CollUtil.isNotEmpty(marketRuleDetails) ? LeConstants.COMMON_YES.toString() : LeConstants.COMMON_NO.toString(), (Long)null, (Long)null);
            }
        }, this.asyncTaskExecutor);
    }

    public List<MarketRule> listRuleByType(Integer moduleType, Integer ruleType) {
        return (List)Optional.ofNullable(this.list((Wrapper<MarketRule>)((LambdaQueryWrapper<MarketRule>)((LambdaQueryWrapper<MarketRule>)((LambdaQueryWrapper<MarketRule>)Wrappers.lambdaQuery(MarketRule.class).eq(MarketRule::getModuleType, moduleType)).eq(MarketRule::getRuleType, ruleType)).eq(MarketRule::getDisableFlag, LeConstants.COMMON_NO)).eq(MarketRule::getDelFlag, LeConstants.COMMON_NO))).orElse(new ArrayList());
    }

}
