/**
 * BladeX Commercial License Agreement
 * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
 * <p>
 * Use of this software is governed by the Commercial License Agreement
 * obtained after purchasing a license from BladeX.
 * <p>
 * 1. This software is for development use only under a valid license
 * from BladeX.
 * <p>
 * 2. Redistribution of this software's source code to any third party
 * without a commercial license is strictly prohibited.
 * <p>
 * 3. Licensees may copyright their own code but cannot use segments
 * from this software for such purposes. Copyright of this software
 * remains with BladeX.
 * <p>
 * Using this software signifies agreement to this License, and the software
 * must not be used for illegal purposes.
 * <p>
 * Author: Chill Zhuang (bladejava@qq.com)
 */
package org.springblade.transport.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.transport.excel.ProcessConfigExcel;
import org.springblade.transport.mapper.ProcessConfigMapper;
import org.springblade.transport.pojo.entity.ProcessConfig;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.ProcessConfigVO;
import org.springblade.transport.service.IProcessConfigService;
import org.springblade.transport.support.TransportBusinessSupport;
import org.springblade.transport.wrapper.ProcessConfigWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 过程配置 服务实现类
 *
 * @author Chill
 */
@Service
public class ProcessConfigServiceImpl extends BaseServiceImpl<ProcessConfigMapper, ProcessConfig> implements IProcessConfigService {

	@Override
	public IPage<ProcessConfigVO> selectProcessConfigPage(IPage<ProcessConfig> page, ProcessConfigVO processConfig) {
		IPage<ProcessConfig> entityPage = page(page, buildQuery(processConfig));
		return ProcessConfigWrapper.build().pageVO(entityPage);
	}

	@Override
	public ProcessConfigVO detail(Long id) {
		return ProcessConfigWrapper.build().entityVO(loadEditable(id, false));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(ProcessConfig processConfig) {
		boolean created = Func.isEmpty(processConfig.getId());
		if (!created) {
			ProcessConfig oldRecord = loadEditable(processConfig.getId(), true);
			processConfig.setConfigCode(oldRecord.getConfigCode());
			processConfig.setDeptId(oldRecord.getDeptId());
			processConfig.setDeptName(oldRecord.getDeptName());
		}
		prepare(processConfig);
		if (created && Func.isEmpty(processConfig.getConfigCode())) {
			processConfig.setConfigCode(nextCode());
		}
		validate(processConfig);
		return saveOrUpdate(processConfig);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public BusinessRemoveResultVO removeProcessConfig(String ids) {
		List<Long> idList = Func.toLongList(ids);
		if (Func.isEmpty(idList)) {
			throw new ServiceException("请选择需要删除的数据");
		}
		BusinessRemoveResultVO result = new BusinessRemoveResultVO();
		List<Long> deleteIdList = new ArrayList<>();
		for (ProcessConfig processConfig : listByIds(idList)) {
			TransportBusinessSupport.assertCurrentDept(processConfig.getDeptId(), "过程配置");
			if (shouldSkipDelete(processConfig)) {
				result.setSkippedCount(result.getSkippedCount() + 1);
				result.getSkippedCodes().add(processConfig.getConfigCode());
				continue;
			}
			deleteIdList.add(processConfig.getId());
		}
		if (Func.isNotEmpty(deleteIdList)) {
			deleteLogic(deleteIdList);
		}
		result.setSuccessCount(deleteIdList.size());
		return result;
	}

	@Override
	public List<ProcessConfigExcel> exportProcessConfig(ProcessConfigVO processConfig, String ids) {
		LambdaQueryWrapper<ProcessConfig> queryWrapper = buildQuery(processConfig);
		if (Func.isNotEmpty(ids)) {
			queryWrapper.in(ProcessConfig::getId, Func.toLongList(ids));
		}
		return list(queryWrapper).stream().map(record -> {
			ProcessConfigExcel excel = new ProcessConfigExcel();
			BeanUtil.copyProperties(record, excel);
			excel.setCreateUserName(UserCache.getUserRealName(record.getCreateUser()));
			excel.setUpdateUserName(UserCache.getUserRealName(record.getUpdateUser()));
			return excel;
		}).toList();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public ProcessConfigVO copy(Long id) {
		ProcessConfig source = loadEditable(id, true);
		ProcessConfig target = new ProcessConfig();
		target.setConfigName(source.getConfigName());
		target.setProjectIds(source.getProjectIds());
		target.setProjectNames(source.getProjectNames());
		target.setIncludedNodes(source.getIncludedNodes());
		target.setDefaultFinishDays(source.getDefaultFinishDays());
		target.setNodeConfigJson(source.getNodeConfigJson());
		target.setRemark(source.getRemark());
		target.setConfigName(source.getConfigName() + " - 副本");
		target.setStatus(0);
		target.setConfigCode(nextCode());
		prepare(target);
		validate(target);
		save(target);
		return detail(target.getId());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean enable(Long id) {
		ProcessConfig processConfig = loadEditable(id, true);
		validateEnableUnique(processConfig);
		processConfig.setStatus(1);
		return updateById(processConfig);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean disable(Long id) {
		ProcessConfig processConfig = loadEditable(id, true);
		processConfig.setStatus(2);
		return updateById(processConfig);
	}

	private void validateEnableUnique(ProcessConfig processConfig) {
		if (Func.isEmpty(processConfig.getProjectIds())) {
			return;
		}
		for (String projectId : processConfig.getProjectIds().split(",")) {
			String trimProjectId = projectId == null ? "" : projectId.trim();
			if (Func.isEmpty(trimProjectId)) {
				continue;
			}
			if (count(Wrappers.<ProcessConfig>lambdaQuery().eq(ProcessConfig::getStatus, 1).ne(ProcessConfig::getId, processConfig.getId()).eq(ProcessConfig::getIsDeleted, 0).like(ProcessConfig::getProjectIds, trimProjectId)) > 0) {
				throw new ServiceException("该项目已有关联的启用配置，请先停用原配置");
			}
		}
	}

	private LambdaQueryWrapper<ProcessConfig> buildQuery(ProcessConfigVO processConfig) {
		TransportBusinessSupport.validateAllDept(processConfig.getAllDept(), "过程配置");
		LambdaQueryWrapper<ProcessConfig> queryWrapper = Wrappers.<ProcessConfig>lambdaQuery().eq(ProcessConfig::getIsDeleted, 0);
		if (!Objects.equals(processConfig.getAllDept(), 1)) {
			queryWrapper.eq(ProcessConfig::getDeptId, TransportBusinessSupport.currentDeptId("过程配置"));
		} else if (Func.isNotEmpty(processConfig.getDeptId())) {
			queryWrapper.eq(ProcessConfig::getDeptId, processConfig.getDeptId());
		}
		if (Func.isNotEmpty(processConfig.getConfigCode())) {
			queryWrapper.eq(ProcessConfig::getConfigCode, processConfig.getConfigCode());
		}
		if (Func.isNotEmpty(processConfig.getConfigName())) {
			queryWrapper.like(ProcessConfig::getConfigName, processConfig.getConfigName());
		}
		if (Func.isNotEmpty(processConfig.getProjectIds())) {
			queryWrapper.like(ProcessConfig::getProjectIds, processConfig.getProjectIds());
		}
		if (Func.isNotEmpty(processConfig.getProjectNames())) {
			queryWrapper.like(ProcessConfig::getProjectNames, processConfig.getProjectNames());
		}
		if (Func.isNotEmpty(processConfig.getIncludedNodes())) {
			queryWrapper.like(ProcessConfig::getIncludedNodes, processConfig.getIncludedNodes());
		}
		if (Func.isNotEmpty(processConfig.getRemark())) {
			queryWrapper.like(ProcessConfig::getRemark, processConfig.getRemark());
		}
		if (Func.isNotEmpty(processConfig.getStatus())) {
			queryWrapper.eq(ProcessConfig::getStatus, processConfig.getStatus());
		}
		queryWrapper.orderByDesc(ProcessConfig::getCreateTime);
		return queryWrapper;
	}

	private void prepare(ProcessConfig processConfig) {
		processConfig.setConfigCode(TransportBusinessSupport.trimToNull(processConfig.getConfigCode()));
		processConfig.setConfigName(TransportBusinessSupport.trimToNull(processConfig.getConfigName()));
		processConfig.setProjectIds(TransportBusinessSupport.trimToNull(processConfig.getProjectIds()));
		processConfig.setProjectNames(TransportBusinessSupport.trimToNull(processConfig.getProjectNames()));
		processConfig.setIncludedNodes(TransportBusinessSupport.trimToNull(processConfig.getIncludedNodes()));
		processConfig.setNodeConfigJson(TransportBusinessSupport.trimToNull(processConfig.getNodeConfigJson()));
		processConfig.setDeptName(TransportBusinessSupport.trimToNull(processConfig.getDeptName()));
		processConfig.setRemark(TransportBusinessSupport.trimToNull(processConfig.getRemark()));
		if (Func.isEmpty(processConfig.getDeptId())) {
			Dept dept = TransportBusinessSupport.currentDept("过程配置");
			processConfig.setDeptId(dept.getId());
			processConfig.setDeptName(dept.getDeptName());
		}
		if (processConfig.getStatus() == null) { processConfig.setStatus(0); }
	}

	private void validate(ProcessConfig processConfig) {
		TransportBusinessSupport.validateRequired(processConfig.getConfigName(), "配置名称不能为空");
		TransportBusinessSupport.validateRequired(processConfig.getProjectNames(), "项目不能为空");
		TransportBusinessSupport.validateLength(processConfig.getConfigCode(), 255, "配置编号不能超过255字");
		TransportBusinessSupport.validateLength(processConfig.getConfigName(), 255, "配置名称不能超过255字");
		TransportBusinessSupport.validateLength(processConfig.getProjectIds(), 255, "项目ID集合不能超过255字");
		TransportBusinessSupport.validateLength(processConfig.getProjectNames(), 255, "项目不能超过255字");
		TransportBusinessSupport.validateLength(processConfig.getIncludedNodes(), 255, "包含过程节点不能超过255字");
		TransportBusinessSupport.validateLength(processConfig.getNodeConfigJson(), 8000, "过程节点配置不能超过8000字");
		TransportBusinessSupport.validateLength(processConfig.getDeptName(), 255, "所属组织不能超过255字");
		TransportBusinessSupport.validateLength(processConfig.getRemark(), 500, "备注不能超过500字");
		if (processConfig.getDefaultFinishDays() != null && (processConfig.getDefaultFinishDays() <= 0 || processConfig.getDefaultFinishDays() > 999)) { throw new ServiceException("请输入正整数"); }
		if (count(Wrappers.<ProcessConfig>lambdaQuery().eq(ProcessConfig::getDeptId, processConfig.getDeptId()).eq(ProcessConfig::getConfigName, processConfig.getConfigName()).ne(Func.isNotEmpty(processConfig.getId()), ProcessConfig::getId, processConfig.getId()).eq(ProcessConfig::getIsDeleted, 0)) > 0) { throw new ServiceException("配置名称已存在"); }
	}

	private ProcessConfig loadEditable(Long id, boolean checkDept) {
		if (Func.isEmpty(id)) {
			throw new ServiceException("主键不能为空");
		}
		ProcessConfig processConfig = getById(id);
		if (Func.isEmpty(processConfig) || Objects.equals(processConfig.getIsDeleted(), 1)) {
			throw new ServiceException("过程配置不存在");
		}
		if (checkDept) {
			TransportBusinessSupport.assertCurrentDept(processConfig.getDeptId(), "过程配置");
		}
		return processConfig;
	}

	private boolean shouldSkipDelete(ProcessConfig processConfig) {
		return Objects.equals(processConfig.getStatus(), 1);
	}

	private synchronized String nextCode() {
		String prefix = "GC";
		List<ProcessConfig> latestList = list(Wrappers.<ProcessConfig>lambdaQuery()
			.select(ProcessConfig::getConfigCode)
			.likeRight(ProcessConfig::getConfigCode, prefix)
			.orderByDesc(ProcessConfig::getConfigCode)
			.last("LIMIT 1"));
		int next = 1;
		if (Func.isNotEmpty(latestList) && Func.isNotEmpty(latestList.get(0).getConfigCode())) {
			String serial = latestList.get(0).getConfigCode().substring(prefix.length());
			if (serial.chars().allMatch(Character::isDigit)) {
				next = Integer.parseInt(serial) + 1;
			}
		}
		return prefix + String.format("%05d", next);
	}

}
