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 SnIncService snIncService;

    @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<SnIncEntity>> 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())
        );

        saveSnIncEntityList(tuple2.get_1());
    }

    private Tuple2<List<SectionConfig>, List<SnIncEntity>> resolveConfig(List<HashMap<String, Object>> configList) {
        Assert.notEmpty(configList, () -> Exceptions.exception("编码配置不能为空"));
        List<SectionConfig> configs = new ArrayList<>(configList.size());
        List<SnIncEntity> snIncEntityList = new LinkedList<>();
        Set<String> snIncEntityCodeList = new HashSet<>();
        for (HashMap<String, Object> config : configList) {
            SectionConfig sectionConfig = SnUtil.resolve(config);
            if (sectionConfig instanceof IncSectionConfig incSectionConfig) {
                PadMode padMode = incSectionConfig.getPadMode();
                String code = incSectionConfig.getCode();
                Assert.isFalse(snIncEntityCodeList.contains(code), () -> Exceptions.exception("编码配置已存在：{}", code));
                snIncEntityCodeList.add(code);
                snIncEntityList.add(new SnIncEntity()
                    .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())
                    .setRollbackMode(incSectionConfig.getRollbackMode())
                    .setAllowOverflow(incSectionConfig.getAllowOverflow()));
            }
            configs.add(sectionConfig);
        }

        return Tuple2.of(configs, snIncEntityList);
    }

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

    /**
     * 修改
     */
    @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<SnIncEntity>> tuple2 = resolveConfig(newConfig);
        List<String> oldIncSectionConfigCodeList = oldConfig.stream()
            .filter(it -> it instanceof IncSectionConfig)
            .map(it -> ((IncSectionConfig) it).getCode())
            .collect(Collectors.toList());
        List<SnIncEntity> oldSnIncEntityList;
        if (CollUtil.isEmpty(oldIncSectionConfigCodeList)) {
            oldSnIncEntityList = Collections.emptyList();
        } else {
            oldSnIncEntityList = snIncService.list(Wrappers.lambdaQuery(SnIncEntity.class).in(SnIncEntity::getCode, oldIncSectionConfigCodeList));
        }
        modifySnIncSectionConfig(oldSnIncEntityList, 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 modifySnIncSectionConfig(List<SnIncEntity> oldSnIncEntityList, List<SnIncEntity> newSnIncEntityList) {
        if (oldSnIncEntityList.isEmpty() && newSnIncEntityList.isEmpty()) return;
        // 旧无 新有 --> 新增
        List<SnIncEntity> addList = difference(newSnIncEntityList, oldSnIncEntityList);
        if (CollUtil.isNotEmpty(addList)) saveSnIncEntityList(addList);
        // 旧有 新有 --> 修改
        List<SnIncEntity> modList = intersection(oldSnIncEntityList, newSnIncEntityList);
        if (CollUtil.isNotEmpty(modList)) snIncService.updateBatchById(modList);
        // 旧有 新无 --> 删除
        List<SnIncEntity> delList = difference(oldSnIncEntityList, newSnIncEntityList);
        if (CollUtil.isNotEmpty(delList)) snIncService.removeBatchByIds(delList);
    }

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

    private List<SnIncEntity> intersection(List<SnIncEntity> list1, List<SnIncEntity> list2) {
        Map<String, SnIncEntity> map = GroupUtil.k_o(list2, SnIncEntity::getCode);
        return list1.stream()
            .filter(it -> map.containsKey(it.getCode()))
            .map(it -> {
                SnIncEntity newData = map.get(it.getCode());
                PadMode padMode = newData.getPadMode();
                Integer padLen = newData.getPadLen();
                return new SnIncEntity()
                    .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))
                    .setRollbackMode(newData.getRollbackMode())
                    .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<SnIncEntity> snIncEntityList = snIncService
            .list(Wrappers.lambdaQuery(SnIncEntity.class).in(SnIncEntity::getCode, codes))
            .stream().map(it -> BeanUtil.copyProperties(it, SnIncEntity.class)
                .setVal(it.getInitialVal())
                .setLastTime(null)
                .setLastPici(null)
            )
            .collect(Collectors.toList());

        snIncService.remove(Wrappers.lambdaQuery(SnIncEntity.class).in(SnIncEntity::getCode, codes));

        snIncService.saveBatch(snIncEntityList);
    }
}
