package com.ruoyi.system.service.impl;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.system.mapper.SysCodeRuleMapper;
import com.ruoyi.system.domain.SysCodeRule;
import com.ruoyi.system.service.ISysCodeRuleService;
import com.ruoyi.common.core.text.Convert;

/**
 * 编码规则Service业务层处理
 * 
 * @author Yangk
 * @date 2026-06-08
 */
@Service
public class SysCodeRuleServiceImpl implements ISysCodeRuleService 
{
    private static final String ENABLED_YES = "Y";

    private static final String ENABLED_NO = "N";

    private static final String DEL_FLAG_NORMAL = "0";

    private static final String RESET_CYCLE_DAY = "DAY";

    private static final String RESET_CYCLE_MONTH = "MONTH";

    private static final String RESET_CYCLE_YEAR = "YEAR";

    private static final String RESET_CYCLE_NONE = "NONE";

    private static final String DEFAULT_DATE_FORMAT = "yyyyMMdd";

    private static final Long DEFAULT_SERIAL_LENGTH = 4L;

    private static final int MAX_SERIAL_LENGTH = 10;

    private static final int MAX_BUSINESS_CODE_LENGTH = 64;

    @Autowired
    private SysCodeRuleMapper sysCodeRuleMapper;

    /**
     * 查询编码规则
     * 
     * @param ruleId 编码规则主键
     * @return 编码规则
     */
    @Override
    public SysCodeRule selectSysCodeRuleByRuleId(Long ruleId)
    {
        return sysCodeRuleMapper.selectSysCodeRuleByRuleId(ruleId);
    }

    /**
     * 查询编码规则列表
     * 
     * @param sysCodeRule 编码规则
     * @return 编码规则
     */
    @Override
    public List<SysCodeRule> selectSysCodeRuleList(SysCodeRule sysCodeRule)
    {
        return sysCodeRuleMapper.selectSysCodeRuleList(sysCodeRule);
    }

    /**
     * 校验规则编码是否唯一
     *
     * @param sysCodeRule 编码规则
     * @return 结果
     */
    @Override
    public boolean checkRuleCodeUnique(SysCodeRule sysCodeRule)
    {
        if (StringUtils.isNull(sysCodeRule) || StringUtils.isEmpty(sysCodeRule.getRuleCode()))
        {
            return UserConstants.UNIQUE;
        }
        Long ruleId = StringUtils.isNull(sysCodeRule.getRuleId()) ? -1L : sysCodeRule.getRuleId();
        SysCodeRule info = sysCodeRuleMapper.selectSysCodeRuleByRuleCode(StringUtils.trim(sysCodeRule.getRuleCode()));
        if (StringUtils.isNotNull(info) && info.getRuleId().longValue() != ruleId.longValue())
        {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 生成下一个业务编号
     *
     * @param ruleCode 规则编码
     * @return 业务编号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String nextCode(String ruleCode)
    {
        String normalizedRuleCode = StringUtils.trim(ruleCode);
        if (StringUtils.isEmpty(normalizedRuleCode))
        {
            throw new ServiceException("编码规则编码不能为空");
        }

        SysCodeRule rule = sysCodeRuleMapper.selectSysCodeRuleByRuleCodeForUpdate(normalizedRuleCode);
        if (StringUtils.isNull(rule))
        {
            throw new ServiceException(String.format("编码规则【%1$s】不存在或已删除", normalizedRuleCode));
        }
        if (!StringUtils.equals(ENABLED_YES, rule.getEnabled()))
        {
            throw new ServiceException(String.format("编码规则【%1$s】已停用", normalizedRuleCode));
        }
        validateRule(rule);

        Date now = DateUtils.getNowDate();
        String serialKey = buildSerialKey(rule, now);
        long currentSerial = StringUtils.nvl(rule.getCurrentSerial(), 0L);
        long nextSerial = shouldReset(rule, serialKey) ? 1L : currentSerial + 1L;
        long maxSerial = maxSerial(rule.getSerialLength().intValue());
        if (nextSerial > maxSerial)
        {
            throw new ServiceException(String.format("编码规则【%1$s】流水号已超出%2$s位长度",
                    rule.getRuleCode(), rule.getSerialLength()));
        }

        String datePart = formatDate(rule.getDateFormat(), now);
        String serialPart = String.format("%0" + rule.getSerialLength().intValue() + "d", nextSerial);
        String nextCode = rule.getCodePrefix() + datePart + serialPart;
        if (nextCode.length() > MAX_BUSINESS_CODE_LENGTH)
        {
            throw new ServiceException(String.format("编码规则【%1$s】生成的编号长度超过%2$s位",
                    rule.getRuleCode(), MAX_BUSINESS_CODE_LENGTH));
        }

        int rows = sysCodeRuleMapper.updateSerialState(rule.getRuleId(), nextSerial, serialKey, now);
        if (rows != 1)
        {
            throw new ServiceException(String.format("编码规则【%1$s】流水号更新失败", rule.getRuleCode()));
        }
        return nextCode;
    }

    /**
     * 新增编码规则
     * 
     * @param sysCodeRule 编码规则
     * @return 结果
     */
    @Override
    public int insertSysCodeRule(SysCodeRule sysCodeRule)
    {
        normalizeRule(sysCodeRule);
        sysCodeRule.setCurrentSerial(0L);
        sysCodeRule.setLastSerialKey(null);
        sysCodeRule.setDelFlag(DEL_FLAG_NORMAL);
        sysCodeRule.setUpdateBy(null);
        sysCodeRule.setUpdateTime(null);
        validateRule(sysCodeRule);
        if (!checkRuleCodeUnique(sysCodeRule))
        {
            throw new ServiceException("规则编码已存在");
        }
        sysCodeRule.setCreateTime(DateUtils.getNowDate());
        return sysCodeRuleMapper.insertSysCodeRule(sysCodeRule);
    }

    /**
     * 修改编码规则
     * 
     * @param sysCodeRule 编码规则
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateSysCodeRule(SysCodeRule sysCodeRule)
    {
        if (StringUtils.isNull(sysCodeRule) || StringUtils.isNull(sysCodeRule.getRuleId()))
        {
            throw new ServiceException("编码规则ID不能为空");
        }
        SysCodeRule current = selectExistingRule(sysCodeRule.getRuleId());
        normalizeRule(sysCodeRule);
        if (isUsed(current) && !StringUtils.equals(current.getRuleCode(), sysCodeRule.getRuleCode()))
        {
            // 规则编码是业务服务调用的稳定标识，已生成过编号后修改会导致后续单据找不到原规则。
            throw new ServiceException("已使用的编码规则不允许修改规则编码");
        }
        sysCodeRule.setCurrentSerial(current.getCurrentSerial());
        sysCodeRule.setLastSerialKey(current.getLastSerialKey());
        sysCodeRule.setDelFlag(current.getDelFlag());
        validateRule(sysCodeRule);
        if (!checkRuleCodeUnique(sysCodeRule))
        {
            throw new ServiceException("规则编码已存在");
        }
        sysCodeRule.setUpdateTime(DateUtils.getNowDate());
        return sysCodeRuleMapper.updateSysCodeRule(sysCodeRule);
    }

    /**
     * 批量删除编码规则
     * 
     * @param ruleIds 需要删除的编码规则主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteSysCodeRuleByRuleIds(String ruleIds)
    {
        String[] ids = Convert.toStrArray(ruleIds);
        for (String id : ids)
        {
            checkDeleteAllowed(Long.valueOf(id));
        }
        return sysCodeRuleMapper.deleteSysCodeRuleByRuleIds(ids);
    }

    /**
     * 删除编码规则信息
     * 
     * @param ruleId 编码规则主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteSysCodeRuleByRuleId(Long ruleId)
    {
        checkDeleteAllowed(ruleId);
        return sysCodeRuleMapper.deleteSysCodeRuleByRuleId(ruleId);
    }

    private SysCodeRule selectExistingRule(Long ruleId)
    {
        SysCodeRule rule = sysCodeRuleMapper.selectSysCodeRuleByRuleIdForUpdate(ruleId);
        if (StringUtils.isNull(rule))
        {
            throw new ServiceException("编码规则不存在或已删除");
        }
        return rule;
    }

    private void normalizeRule(SysCodeRule rule)
    {
        if (StringUtils.isNull(rule))
        {
            throw new ServiceException("编码规则不能为空");
        }
        rule.setRuleCode(StringUtils.trim(rule.getRuleCode()));
        rule.setRuleName(StringUtils.trim(rule.getRuleName()));
        rule.setCodePrefix(StringUtils.isNull(rule.getCodePrefix()) ? "" : StringUtils.trim(rule.getCodePrefix()));
        rule.setDateFormat(StringUtils.isEmpty(rule.getDateFormat())
                ? DEFAULT_DATE_FORMAT : StringUtils.trim(rule.getDateFormat()));
        rule.setSerialLength(StringUtils.isNull(rule.getSerialLength())
                ? DEFAULT_SERIAL_LENGTH : rule.getSerialLength());
        rule.setResetCycle(StringUtils.isEmpty(rule.getResetCycle())
                ? RESET_CYCLE_DAY : StringUtils.trim(rule.getResetCycle()));
        rule.setEnabled(StringUtils.isEmpty(rule.getEnabled())
                ? ENABLED_YES : StringUtils.trim(rule.getEnabled()));
    }

    private void validateRule(SysCodeRule rule)
    {
        if (StringUtils.isEmpty(rule.getRuleCode()))
        {
            throw new ServiceException("规则编码不能为空");
        }
        if (rule.getRuleCode().length() > 64)
        {
            throw new ServiceException("规则编码长度不能超过64个字符");
        }
        if (StringUtils.isEmpty(rule.getRuleName()))
        {
            throw new ServiceException("规则名称不能为空");
        }
        if (rule.getRuleName().length() > 100)
        {
            throw new ServiceException("规则名称长度不能超过100个字符");
        }
        if (rule.getCodePrefix().length() > 20)
        {
            throw new ServiceException("编码前缀长度不能超过20个字符");
        }
        if (StringUtils.isEmpty(rule.getDateFormat()) || rule.getDateFormat().length() > 20)
        {
            throw new ServiceException("日期格式不能为空且长度不能超过20个字符");
        }
        if (StringUtils.isNull(rule.getSerialLength())
                || rule.getSerialLength() <= 0
                || rule.getSerialLength() > MAX_SERIAL_LENGTH)
        {
            throw new ServiceException("流水号长度必须在1到10之间");
        }
        if (StringUtils.isNull(rule.getCurrentSerial()) || rule.getCurrentSerial() < 0)
        {
            throw new ServiceException("当前流水号不能小于0");
        }
        if (rule.getCurrentSerial() > maxSerial(rule.getSerialLength().intValue()))
        {
            throw new ServiceException("当前流水号已超出配置的流水号长度");
        }
        if (!StringUtils.equalsAny(rule.getResetCycle(), RESET_CYCLE_DAY, RESET_CYCLE_MONTH,
                RESET_CYCLE_YEAR, RESET_CYCLE_NONE))
        {
            throw new ServiceException("重置周期不合法");
        }
        if (!StringUtils.equalsAny(rule.getEnabled(), ENABLED_YES, ENABLED_NO))
        {
            throw new ServiceException("启用状态不合法");
        }
        if (StringUtils.isNotEmpty(rule.getLastSerialKey()) && rule.getLastSerialKey().length() > 20)
        {
            throw new ServiceException("上次流水键长度不能超过20个字符");
        }

        String datePart = formatDate(rule.getDateFormat(), DateUtils.getNowDate());
        if (rule.getCodePrefix().length() + datePart.length() + rule.getSerialLength() > MAX_BUSINESS_CODE_LENGTH)
        {
            throw new ServiceException("编码前缀、日期和流水号组成的编号长度不能超过64位");
        }
    }

    private String formatDate(String pattern, Date date)
    {
        try
        {
            return new SimpleDateFormat(pattern).format(date);
        }
        catch (IllegalArgumentException e)
        {
            throw new ServiceException("日期格式不合法");
        }
    }

    private String buildSerialKey(SysCodeRule rule, Date date)
    {
        if (StringUtils.equals(RESET_CYCLE_DAY, rule.getResetCycle()))
        {
            return DateUtils.parseDateToStr("yyyyMMdd", date);
        }
        if (StringUtils.equals(RESET_CYCLE_MONTH, rule.getResetCycle()))
        {
            return DateUtils.parseDateToStr("yyyyMM", date);
        }
        if (StringUtils.equals(RESET_CYCLE_YEAR, rule.getResetCycle()))
        {
            return DateUtils.parseDateToStr("yyyy", date);
        }
        return rule.getLastSerialKey();
    }

    private boolean shouldReset(SysCodeRule rule, String serialKey)
    {
        return !StringUtils.equals(RESET_CYCLE_NONE, rule.getResetCycle())
                && !StringUtils.equals(serialKey, rule.getLastSerialKey());
    }

    private long maxSerial(int serialLength)
    {
        if (serialLength >= MAX_SERIAL_LENGTH)
        {
            return Integer.MAX_VALUE;
        }
        long max = 1L;
        for (int i = 0; i < serialLength; i++)
        {
            max *= 10L;
        }
        return max - 1L;
    }

    private void checkDeleteAllowed(Long ruleId)
    {
        SysCodeRule rule = sysCodeRuleMapper.selectSysCodeRuleByRuleIdForUpdate(ruleId);
        if (StringUtils.isNull(rule))
        {
            return;
        }
        if (isUsed(rule))
        {
            throw new ServiceException(String.format("编码规则【%1$s】已生成过业务编号，不允许删除，可停用保留",
                    rule.getRuleName()));
        }
    }

    private boolean isUsed(SysCodeRule rule)
    {
        return StringUtils.nvl(rule.getCurrentSerial(), 0L) > 0L
                || StringUtils.isNotEmpty(rule.getLastSerialKey());
    }
}
