package com.huigou.topsun.base.coderule.application.impl;

import com.huigou.data.domain.model.CommonDomainConstants;
import com.huigou.data.query.model.QueryDescriptor;
import com.huigou.data.query.model.QueryModel;
import com.huigou.exception.ApplicationException;
import com.huigou.topsun.base.coderule.application.CodeRuleApplication;
import com.huigou.topsun.base.coderule.domain.model.CodeRule;
import com.huigou.topsun.base.coderule.domain.model.CodeRuleKind;
import com.huigou.topsun.base.coderule.domain.query.CodeRuleQueryRequest;
import com.huigou.topsun.base.coderule.repository.CodeRuleRepository;
import com.huigou.uasp.bmp.common.application.BaseApplication;
import com.huigou.uasp.bmp.opm.domain.model.org.Org;
import com.huigou.uasp.bmp.opm.proxy.OrgApplicationProxy;
import com.huigou.util.ApplicationContextWrapper;
import com.huigou.util.DateUtil;
import com.huigou.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 采购编号及合同编号编码规则
 * 
 * @ClassName: CodeRuleApplicationImpl
 * @author
 * @date 2018-06-20 15:20
 * @version V1.0
 */
@Service("codeRuleApplication")
public class CodeRuleApplicationImpl extends BaseApplication implements CodeRuleApplication {
    @Autowired
    private CodeRuleRepository codeRuleRepository;

    @Autowired
    protected OrgApplicationProxy orgApplication;

    /**
     * @Transactional(propagation=Propagation.REQUIRES_NEW) ：不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public CodeRule getRuleValueAsKind(String orgId, CodeRuleKind kind) {
        Assert.hasText(orgId, "系统ID不能为空");
        int step = 1;
        CodeRule codeRule = null;
        String id = this.sqlExecutorDao.queryToString("select t.id from code_rule t where t.org_id = ? and t.rule_kind= ?", orgId, kind.getId());
        if (StringUtil.isNotBlank(id)) {
            String sql = "select t.* from code_rule t where t.id = ?  for update";
            codeRule = this.sqlExecutorDao.queryToObject(sql, CodeRule.class, id);
        } else {
            codeRule = new CodeRule();
            codeRule.setRuleKind(kind.getId());
            codeRule.setOrgId(orgId);
            codeRule.setName(kind.getName());
            codeRule.setCode(kind.getId());
            codeRule.setPrefix(kind.getPrefix());
            codeRule.setRule("{yyyy}{5}");
        }
        Integer currentValue = codeRule.getCurrentValue();
        if (currentValue == null) {
            currentValue = 0;
        }
        Pattern p = Pattern.compile(RULE_REG, Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(codeRule.getRule());
        if (m.find()) {
            String dateFormat = m.group(1);
            try {
                // 判断是否已到循环周期
                if (isPeriodEnd(dateFormat, codeRule.getLastModifiedDate())) {
                    currentValue = 0;
                }
            } catch (ParseException e) {
                throw new ApplicationException(String.format("解析单据编号规则“%s出错”。", dateFormat));
            }
        } else {
            throw new ApplicationException(String.format("解析单据编号规则“%s出错”。", codeRule.getRule()));
        }
        codeRule.setCurrentValue(currentValue + step);// 流水号+步长
        codeRule.setLastModifiedDate(DateUtil.getTimestamp());
        codeRule = codeRuleRepository.save(codeRule);
        codeRule.setLastValue(currentValue);
        return codeRule;
    }

    /**
     * 获取单据编号
     * 
     * @return
     */
    @Override
    public String getNextCode(String orgId, String purchaseKind, CodeRuleKind kind) {
        StringBuffer code = new StringBuffer();
        Org org = orgApplication.loadOrg(orgId);
        Assert.notNull(org, "编号获取失败，未找到对应组织!");
        try {
            StringBuffer sb = new StringBuffer();
            // 通过代理调用
            CodeRuleApplication application = ApplicationContextWrapper.getBean("codeRuleApplication", CodeRuleApplication.class);
            CodeRule codeRule = application.getRuleValueAsKind(orgId, kind);
            code.append(codeRule.getPrefix()).append("-");
            code.append(org.getCode()).append("-");
            if (StringUtil.isNotBlank(purchaseKind)) {
                code.append(purchaseKind).append("-");
            }
            String rule = codeRule.getRule();
            Integer nextValue = codeRule.getCurrentValue();
            Pattern p = Pattern.compile(RULE_REG, Pattern.CASE_INSENSITIVE);
            Matcher m = p.matcher(rule);
            if (m.find()) {
                String dateFormat = m.group(1);
                int serialLength = Integer.parseInt(m.group(3));
                m.appendReplacement(sb, formatDate(dateFormat, DateUtil.getDateTime()) + m.group(2) + formatSerialNumber(nextValue, serialLength));
            }
            code.append(sb);
        } catch (Exception e) {
            throw new ApplicationException(e);
        }
        return code.toString();
    }

    /**
     * 格式化序列号
     * 
     * @param serialNumber
     *            序列号
     * @param length
     *            长度
     * @return
     */
    private String formatSerialNumber(Integer serialNumber, int length) {
        String result = String.valueOf(serialNumber);
        Assert.isTrue(result.length() <= length, String.format("流水号长度超出限制，设置长度：%s，实际长度：%s。", length, result.length()));
        while (result.length() < length) {
            result = "0" + result;
        }
        return result;
    }

    /**
     * 更具单据编号规则格式化日期
     * 
     * @param dateFormat
     *            日期格式
     * @param date
     *            日期
     * @return
     */
    private String formatDate(String dateFormat, Date date) {
        SimpleDateFormat df = new SimpleDateFormat(dateFormat);
        return df.format(date);
    }

    /**
     * 判断是否已到循环周期
     * 
     * @param dateFormat
     *            日期格式
     * @param lastupdate
     *            最后更新日期
     * @return
     * @throws ParseException
     */
    private boolean isPeriodEnd(String dateFormat, Date lastupdate) throws ParseException {
        lastupdate = lastupdate != null ? lastupdate : DateUtil.getDateTime();
        SimpleDateFormat df = new SimpleDateFormat(dateFormat);
        Date now = DateUtil.getDateTime();
        Date formattedCurrentDate = df.parse(df.format(now));
        Date formattedLastUpdateDate = df.parse(df.format(lastupdate));
        return formattedCurrentDate.compareTo(formattedLastUpdateDate) > 0;
    }

    @Override
    @Transactional
    public String saveCodeRule(CodeRule codeRule) {
        Assert.notNull(codeRule, CommonDomainConstants.OBJECT_NOT_NULL);
        codeRule = (CodeRule) this.commonDomainService.loadAndFillinProperties(codeRule);
        codeRule = codeRuleRepository.save(codeRule);
        return codeRule.getId();
    }

    @Override
    public CodeRule loadCodeRule(String id) {
        Assert.hasText(id, CommonDomainConstants.ID_NOT_BLANK);
        CodeRule codeRule = codeRuleRepository.findOne(id);
        StringBuffer code = new StringBuffer();
        Org org = orgApplication.loadOrg(codeRule.getOrgId());
        Assert.notNull(org, "编号获取失败，未找到对应组织!");
        code.append(org.getCode());
        code.append(codeRule.getRule());
        codeRule.setRuleView(code.toString());
        return codeRule;
    }

    @Override
    public Map<String, Object> slicedQueryCodeRule(CodeRuleQueryRequest queryRequest) {
        QueryDescriptor queryDescriptor = this.sqlExecutorDao.getQuery(QUERY_XML_FILE_PATH, "codeRule");
        QueryModel model = this.sqlExecutorDao.getQueryModel(queryDescriptor, queryRequest);
        model.putDictionary("ruleKind", CodeRuleKind.getMap());
        return this.sqlExecutorDao.executeSlicedQuery(model);
    }

}
