package com.xjrsoft.module.system.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.CodeRuleTypeEnum;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.page.ConventPage;
import com.xjrsoft.common.page.PageInput;
import com.xjrsoft.common.page.PageOutput;
import com.xjrsoft.common.utils.FormatUtil;
import com.xjrsoft.module.organization.entity.Department;
import com.xjrsoft.module.organization.entity.User;
import com.xjrsoft.module.organization.service.IDepartmentService;
import com.xjrsoft.module.system.constant.CodeRuleConstant;
import com.xjrsoft.module.system.entity.CodeRule;
import com.xjrsoft.module.system.entity.CodeRuleSeed;
import com.xjrsoft.module.system.mapper.CodeRuleMapper;
import com.xjrsoft.module.system.service.ICodeRuleSeedService;
import com.xjrsoft.module.system.service.ICodeRuleService;
import com.xjrsoft.module.system.vo.CodeRuleVo;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 编号规则表 服务实现类
 * </p>
 *
 * @author zlf
 * @since 2022-06-23
 */
@Service
@AllArgsConstructor
public class CodeRuleServiceImpl extends MPJBaseServiceImpl<CodeRuleMapper, CodeRule> implements ICodeRuleService {


    private final ICodeRuleSeedService codeRuleSeedService;

    private final IDepartmentService departmentService;

    @Override
    public PageOutput<CodeRuleVo> getCodeRulePageList(PageInput dto) {
        IPage<CodeRule> page = baseMapper.selectPage(ConventPage.getPage(dto), Wrappers.<CodeRule>query().lambda()
                .like(StrUtil.isNotBlank(dto.getKeyword()), CodeRule::getName, dto.getKeyword()).orderByDesc(CodeRule::getSortCode));
        return ConventPage.getPageOutput(page, CodeRuleVo.class);
    }

    @Override
    public String genEncode(String encode) {
        CodeRule codeRule = this.getCodeRuleByEncode(encode);
        if (codeRule == null) {
            return null;
        }
        CodeRuleSeed codeRuleSeed = null;
        SaSession tokenSession = StpUtil.getTokenSession();
        User loginUser = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        if (loginUser != null) {
            // 查询用户已存在的编码
            codeRuleSeed = codeRuleSeedService.getCodeRuleSeedBy(codeRule.getId(), loginUser.getId());
            if (codeRuleSeed != null) {
                // 返回用户已存在的编码
                return genEnCode(codeRule, codeRuleSeed, false);
            }
        }
        // 查询最新的编码
        List<CodeRuleSeed> list = codeRuleSeedService.list(Wrappers.<CodeRuleSeed>query().lambda()
                .eq(CodeRuleSeed::getRuleId, codeRule.getId()).isNull(CodeRuleSeed::getUserId));
        if (CollectionUtil.isNotEmpty(list)) {
            codeRuleSeed = list.get(0);
        } else {
            codeRuleSeed = new CodeRuleSeed();
            codeRuleSeed.setRuleId(codeRule.getId());
        }
        String userCode = genEnCode(codeRule, codeRuleSeed, true);
        // 保存下一个编码和种子
        if (this.updateById(codeRule)) {
            return codeRuleSeedService.saveOrUpdate(codeRuleSeed) ? userCode : null;
        }
        return null;
    }

    @Override
    public boolean useEncode(String encodes) {
        return useEncode(StrUtil.split(encodes, StringPool.COMMA));
    }

    @Override
    public boolean useEncode(List<String> encodeList) {
        if (CollectionUtils.isEmpty(encodeList)) {
            return false;
        }
        List<CodeRule> codeRuleList = this.list(Wrappers.<CodeRule>query().lambda().select(CodeRule::getId)
                .eq(CodeRule::getEnabledMark, EnabledMark.ENABLED.getCode())
                .in(encodeList.size() > 1, CodeRule::getCode, encodeList)
                .eq(encodeList.size() == 1, CodeRule::getCode, encodeList.get(0)));
        if (CollectionUtil.isEmpty(codeRuleList)) {
            return false;
        }
        List<Long> ruleIdList = codeRuleList.stream().map(CodeRule::getId).collect(Collectors.toList());
        Wrapper<CodeRuleSeed> deleteWrapper = Wrappers.<CodeRuleSeed>query().lambda()
                .in(ruleIdList.size() > 1, CodeRuleSeed::getRuleId, ruleIdList)
                .eq(ruleIdList.size() == 1, CodeRuleSeed::getRuleId, ruleIdList.get(0))
                .eq(CodeRuleSeed::getUserId, StpUtil.getLoginIdAsLong());
        return codeRuleSeedService.remove(deleteWrapper);
    }

    private String genEnCode(CodeRule codeRule, CodeRuleSeed currentSeed, boolean isGenNext) {
        String ruleFormat = codeRule.getFormatJson();
        JSONArray ruleFormatJson = JSONArray.parseArray(ruleFormat);
        StringBuilder currentCode = new StringBuilder();
        StringBuilder nextCode = new StringBuilder();
        // 下一个种子值
        BigDecimal nextSeedValue = null;

        SaSession tokenSession = StpUtil.getTokenSession();
        User loginUser = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        for (int i = 0; i < ruleFormatJson.size(); i++) {
            JSONObject jsonObject = ruleFormatJson.getJSONObject(i);
            CodeRuleTypeEnum itemType = EnumUtil.getEnumAt(CodeRuleTypeEnum.class, jsonObject.getInteger(CodeRuleConstant.ITEM_TYPE));
            String formatStr = jsonObject.getString(CodeRuleConstant.FORMAT_JSON_STR);
            switch (itemType) {
                case CUSTOM:
                    currentCode.append(formatStr);
                    nextCode.append(formatStr);
                    break;
                case DATE:
                    // 转换成java的时间格式
                    String dateStr = DateUtil.format(new Date(), formatStr);
                    currentCode.append(dateStr);
                    nextCode.append(dateStr);
                    break;
                case SERIAL:
                    BigDecimal seedValue = null;
                    Integer seedValueInt = currentSeed.getSeedValue();
                    if (seedValueInt != null) {
                        seedValue = new BigDecimal(seedValueInt);
                    }
                    if (seedValue == null) {
                        seedValue = new BigDecimal(jsonObject.getString(CodeRuleConstant.INIT_VALUE));
                        currentSeed.setSeedValue(seedValue.intValue());
                    }
                    // 计算下一个种子值
                    BigDecimal stepValue = new BigDecimal(jsonObject.getString(CodeRuleConstant.STEP_VALUE));
                    nextSeedValue = seedValue.add(stepValue);
                    // 流水号到达最大值，重置
                    BigDecimal maxValue = new BigDecimal(StrUtil.replace(formatStr, StringPool.ZERO, "9"));
                    if (nextSeedValue.compareTo(maxValue) > 0) {
                        nextSeedValue = new BigDecimal(jsonObject.getString(CodeRuleConstant.INIT_VALUE));
                    }
                    // 生成当前流水号
                    currentCode.append(FormatUtil.formatNumber(seedValue.toString(), formatStr));
                    // 生成下一个流水号
                    nextCode.append(FormatUtil.formatNumber(nextSeedValue.toString(), formatStr));
//                    currentSeed.setSeedValue(nextSeedValue.intValue());
                    break;
                case DEPARTMENT:

                    Department department = departmentService.getById(loginUser.getDepartmentId());
                    if (department == null) {
                        break;
                    }
                    if (StrUtil.equalsIgnoreCase(CodeRuleConstant.FORMAT_JSON_STR_NAME, formatStr)) {
                        String name = department.getName();
                        currentCode.append(name);
                        nextCode.append(name);
                    } else {
                        String code = department.getCode();
                        currentCode.append(code);
                        nextCode.append(code);
                    }
                    break;
                case USER:
                    if (StrUtil.equalsIgnoreCase(CodeRuleConstant.FORMAT_JSON_STR_NAME, formatStr)) {
                        String realName = loginUser.getName();
                        if (StrUtil.isEmpty(realName)) {
                            break;
                        }
                        currentCode.append(realName);
                        nextCode.append(realName);
                    } else {
                        String userEncode = loginUser.getCode();
                        if (StrUtil.isEmpty(userEncode)) {
                            break;
                        }
                        currentCode.append(userEncode);
                        nextCode.append(userEncode);
                    }
                    break;
                case COMPANY:
                default:
                    break;
            }
        }

        if (!isGenNext) {
            // 不生成下一个编码，只返回当前生成的编码
            return currentCode.toString();
        }
        // 保存分配给用户的编码
        CodeRuleSeed userRuleSeed = BeanUtil.toBean(currentSeed, CodeRuleSeed.class);
        userRuleSeed.setId(IdWorker.getId());
        userRuleSeed.setUserId(loginUser.getId());
        codeRuleSeedService.save(userRuleSeed);
        codeRule.setCurrentNumber(nextCode.toString());
        if (nextSeedValue != null) currentSeed.setSeedValue(nextSeedValue.intValue());
        return currentCode.toString();
    }

    public CodeRule getCodeRuleByEncode(String encode) {
        return this.getOne(Wrappers.<CodeRule>query().lambda()
                .eq(CodeRule::getCode, encode).eq(CodeRule::getEnabledMark, EnabledMark.ENABLED.getCode()));
    }
}
