package com.njzscloud.common.sn;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.core.utils.GroupUtil;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import com.njzscloud.common.sn.contant.PadMode;
import com.njzscloud.common.sn.support.IncSectionConfig;
import com.njzscloud.common.sn.support.SectionConfig;
import com.njzscloud.common.sn.support.Sn;
import com.njzscloud.common.sn.support.SnUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 编码配置表
 */
@Slf4j
@RequiredArgsConstructor
public class SnConfigService extends ServiceImpl<SnConfigMapper, SnConfigEntity> implements IService<SnConfigEntity> {
    private final IncSnService incSnService;

    @Transactional(rollbackFor = Exception.class)
    public Sn getSn(String sncode) {
        SnConfigEntity configEntity = this.getOne(Wrappers.<SnConfigEntity>lambdaQuery().eq(SnConfigEntity::getSncode, sncode));
        if (configEntity == null) return new Sn();
        List<SectionConfig> configs = configEntity.getConfig();
        return new Sn(configs);
    }

    /**
     * 新增
     */
    @Transactional(rollbackFor = Exception.class)
    public void add(AddSnConfigParam addSnConfigParam) {
        String sncode = addSnConfigParam.getSncode();
        boolean exists = this.exists(Wrappers.<SnConfigEntity>lambdaQuery().eq(SnConfigEntity::getSncode, sncode));
        Assert.isFalse(exists, () -> Exceptions.exception("编码配置已存在：{}", sncode));
        List<HashMap<String, Object>> configList = addSnConfigParam.getConfig();
        Tuple2<List<SectionConfig>, List<IncSnEntity>> tuple2 = resolveConfig(configList);

        List<SectionConfig> configs = tuple2.get_0();
        String example = configs.stream().map(SectionConfig::genExample).collect(Collectors.joining());
        this.save(new SnConfigEntity()
                .setSnname(addSnConfigParam.getSnname())
                .setSncode(sncode)
                .setExample(example)
                .setConfig(configs)
                .setMemo(addSnConfigParam.getMemo())
        );

        saveIncSnEntityList(tuple2.get_1());
    }

    private Tuple2<List<SectionConfig>, List<IncSnEntity>> resolveConfig(List<HashMap<String, Object>> configList) {
        Assert.notEmpty(configList, () -> Exceptions.exception("编码配置不能为空"));
        List<SectionConfig> configs = new ArrayList<>(configList.size());
        List<IncSnEntity> incSnEntityList = new LinkedList<>();
        Set<String> incSnEntityCodeList = new HashSet<>();
        for (HashMap<String, Object> config : configList) {
            SectionConfig sectionConfig = SnUtil.resolve(config);
            if (sectionConfig instanceof IncSectionConfig) {
                IncSectionConfig incSectionConfig = (IncSectionConfig) sectionConfig;
                PadMode padMode = incSectionConfig.getPadMode();
                String code = incSectionConfig.getCode();
                Assert.isFalse(incSnEntityCodeList.contains(code), () -> Exceptions.exception("编码配置已存在：{}", code));
                incSnEntityCodeList.add(code);
                incSnEntityList.add(new IncSnEntity()
                        .setCode(code)
                        .setVal((long) incSectionConfig.getInitialVal())
                        .setStep(incSectionConfig.getStep())
                        .setInitialVal((long) incSectionConfig.getInitialVal())
                        .setMaxVal(padMode == PadMode.Wu ? Long.MAX_VALUE : (long) (Math.pow(10, incSectionConfig.getPadLen()) - 1))
                        .setPadMode(padMode)
                        .setPadVal(incSectionConfig.getPadVal())
                        .setPadLen(incSectionConfig.getPadLen())
                        .setRollback(incSectionConfig.getRollback())
                        .setAllowOverflow(incSectionConfig.getAllowOverflow()));
            }
            configs.add(sectionConfig);
        }

        return Tuple2.create(configs, incSnEntityList);
    }

    private void saveIncSnEntityList(List<IncSnEntity> incSnEntityList) {
        if (incSnEntityList.isEmpty()) return;
        List<String> incSnCodes = incSnEntityList.stream().map(IncSnEntity::getCode).collect(Collectors.toList());
        boolean exists = incSnService.exists(Wrappers.<IncSnEntity>lambdaQuery().in(IncSnEntity::getCode, incSnCodes));
        Assert.isFalse(exists, () -> Exceptions.exception("编码配置已存在：{}", incSnCodes));
        incSnService.saveBatch(incSnEntityList);
    }

    /**
     * 修改
     */
    @Transactional(rollbackFor = Exception.class)
    public void modify(ModifySnConfigParam modifySnConfigParam) {
        Long id = modifySnConfigParam.getId();
        SnConfigEntity oldData = this.getById(id);
        Assert.notNull(oldData, () -> Exceptions.exception("编码配置不存在"));
        List<SectionConfig> oldConfig = oldData.getConfig();
        List<HashMap<String, Object>> newConfig = modifySnConfigParam.getConfig();
        Tuple2<List<SectionConfig>, List<IncSnEntity>> tuple2 = resolveConfig(newConfig);
        List<String> oldIncSectionConfigCodeList = oldConfig.stream()
                .filter(it -> it instanceof IncSectionConfig)
                .map(it -> ((IncSectionConfig) it).getCode())
                .collect(Collectors.toList());
        List<IncSnEntity> oldIncSnEntityList;
        if (CollUtil.isEmpty(oldIncSectionConfigCodeList)) {
            oldIncSnEntityList = Collections.emptyList();
        } else {
            oldIncSnEntityList = incSnService.list(Wrappers.lambdaQuery(IncSnEntity.class).in(IncSnEntity::getCode, oldIncSectionConfigCodeList));
        }
        modifyIncSnSectionConfig(oldIncSnEntityList, tuple2.get_1());
        List<SectionConfig> configs = tuple2.get_0();
        String example = configs.stream().map(SectionConfig::genExample).collect(Collectors.joining());
        this.updateById(new SnConfigEntity()
                .setId(id)
                .setSnname(modifySnConfigParam.getSnname())
                .setSncode(modifySnConfigParam.getSncode())
                .setExample(example)
                .setConfig(tuple2.get_0())
                .setMemo(modifySnConfigParam.getMemo())
        );
    }

    private void modifyIncSnSectionConfig(List<IncSnEntity> oldIncSnEntityList, List<IncSnEntity> newIncSnEntityList) {
        if (oldIncSnEntityList.isEmpty() && newIncSnEntityList.isEmpty()) return;
        // 旧无 新有 --> 新增
        List<IncSnEntity> addList = difference(newIncSnEntityList, oldIncSnEntityList);
        if (CollUtil.isNotEmpty(addList)) saveIncSnEntityList(addList);
        // 旧有 新有 --> 修改
        List<IncSnEntity> modList = intersection(oldIncSnEntityList, newIncSnEntityList);
        if (CollUtil.isNotEmpty(modList)) incSnService.updateBatchById(modList);
        // 旧有 新无 --> 删除
        List<IncSnEntity> delList = difference(oldIncSnEntityList, newIncSnEntityList);
        if (CollUtil.isNotEmpty(delList)) incSnService.removeBatchByIds(delList);
    }

    private List<IncSnEntity> difference(List<IncSnEntity> list1, List<IncSnEntity> list2) {
        List<String> codes = list2.stream().map(IncSnEntity::getCode).collect(Collectors.toList());
        return list1.stream()
                .filter(it -> !codes.contains(it.getCode()))
                .collect(Collectors.toList());
    }

    private List<IncSnEntity> intersection(List<IncSnEntity> list1, List<IncSnEntity> list2) {
        Map<String, IncSnEntity> map = GroupUtil.k_o(list2, IncSnEntity::getCode);
        return list1.stream()
                .filter(it -> map.containsKey(it.getCode()))
                .map(it -> {
                    IncSnEntity newData = map.get(it.getCode());
                    PadMode padMode = newData.getPadMode();
                    Integer padLen = newData.getPadLen();
                    return new IncSnEntity()
                            .setId(it.getId())
                            .setStep(newData.getStep())
                            .setInitialVal(newData.getInitialVal())
                            .setPadMode(padMode)
                            .setPadVal(newData.getPadVal())
                            .setPadLen(padLen)
                            .setMaxVal(padMode == PadMode.Wu ? Long.MAX_VALUE : (long) (Math.pow(10, padLen) - 1))
                            .setRollback(newData.getRollback())
                            .setAllowOverflow(newData.getAllowOverflow());
                })
                .collect(Collectors.toList());
    }

    /**
     * 删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void del(List<Long> ids) {
        this.removeBatchByIds(ids);
    }

    /**
     * 详情
     */
    public SnConfigEntity detail(Long id) {
        return this.getById(id);
    }

    /**
     * 分页查询
     */
    public PageResult<SearchSnConfigResult> paging(PageParam pageParam, SnConfigEntity snConfigEntity) {
        String sncode = snConfigEntity.getSncode();
        String snname = snConfigEntity.getSnname();
        LambdaQueryWrapper<SnConfigEntity> ew = Wrappers.lambdaQuery(SnConfigEntity.class)
                .like(StrUtil.isNotBlank(sncode), SnConfigEntity::getSncode, sncode)
                .like(StrUtil.isNotBlank(snname), SnConfigEntity::getSnname, snname);
        IPage<SnConfigEntity> page = this.page(pageParam.toPage(), ew);
        return PageResult.of(page.convert(it -> BeanUtil.copyProperties(it, SearchSnConfigResult.class)));
    }

    @Transactional(rollbackFor = Exception.class)
    public void reset(String sncode) {
        SnConfigEntity configEntity = this.getOne(Wrappers.lambdaQuery(SnConfigEntity.class).eq(SnConfigEntity::getSncode, sncode));
        Assert.notNull(configEntity, () -> Exceptions.clierr("规则不存在"));
        List<SectionConfig> config = configEntity.getConfig();
        List<String> codes = config.stream().filter(it -> it instanceof IncSectionConfig)
                .map(it -> ((IncSectionConfig) it).getCode())
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(codes)) return;

        List<IncSnEntity> incSnEntityList = incSnService
                .list(Wrappers.lambdaQuery(IncSnEntity.class).in(IncSnEntity::getCode, codes))
                .stream().map(it -> BeanUtil.copyProperties(it, IncSnEntity.class)
                        .setVal(it.getInitialVal())
                        .setLastTime(null)
                        .setLastPici(null)
                )
                .collect(Collectors.toList());

        incSnService.remove(Wrappers.lambdaQuery(IncSnEntity.class).in(IncSnEntity::getCode, codes));

        incSnService.saveBatch(incSnEntityList);
    }
}
