/**
 * 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.secure.utils.AuthUtil;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.UserCache;
import org.springblade.transport.excel.ContractManageExcel;
import org.springblade.transport.mapper.ContractManageMapper;
import org.springblade.transport.pojo.entity.ContractManage;
import org.springblade.transport.pojo.vo.ContractManageVO;
import org.springblade.transport.service.IContractManageService;
import org.springblade.transport.support.TransportBusinessSupport;
import org.springblade.transport.wrapper.ContractManageWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 合同管理 服务实现类
 *
 * @author Chill
 */
@Service
public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMapper, ContractManage> implements IContractManageService {

	private static final String STAGE_DRAFT = "draft";
	private static final String STAGE_TEMPORARY = "temporary";
	private static final String STAGE_FORMAL = "formal";
	private static final String STAGE_TERMINATED = "terminated";
	private static final String STATUS_DRAFT = "draft";
	private static final String STATUS_REVIEWING = "reviewing";
	private static final String STATUS_REJECTED = "rejected";
	private static final String STATUS_APPROVED = "approved";
	private static final String STATUS_CHANGE_REVIEWING = "change_reviewing";
	private static final String STATUS_CHANGE_APPROVED = "change_approved";
	private static final String STATUS_CHANGE_REJECTED = "change_rejected";

	@Override
	public IPage<ContractManageVO> selectContractManagePage(IPage<ContractManage> page, ContractManageVO contractManage) {
		IPage<ContractManage> entityPage = page(page, buildQuery(contractManage));
		IPage<ContractManageVO> voPage = ContractManageWrapper.build().pageVO(entityPage);
		voPage.getRecords().forEach(this::fillReadonly);
		return voPage;
	}

	@Override
	public ContractManageVO detail(Long id) {
		ContractManageVO contractManageVO = ContractManageWrapper.build().entityVO(loadExists(id));
		fillReadonly(contractManageVO);
		return contractManageVO;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean saveDraft(ContractManage contractManage) {
		prepare(contractManage);
		validateDraft(contractManage);
		if (Func.isNotEmpty(contractManage.getId())) {
			loadEditable(contractManage.getId());
		}
		prepareCreateOrUpdate(contractManage);
		if (Func.isEmpty(contractManage.getApprovalStatus())) {
			contractManage.setApprovalStatus(STATUS_DRAFT);
		}
		if (Func.isEmpty(contractManage.getContractStage())) {
			contractManage.setContractStage(STAGE_DRAFT);
		}
		return saveOrUpdate(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(ContractManage contractManage) {
		prepare(contractManage);
		validateDraft(contractManage);
		validateTemporary(contractManage);
		if (Func.isNotEmpty(contractManage.getId())) {
			loadEditable(contractManage.getId());
		}
		prepareCreateOrUpdate(contractManage);
		contractManage.setContractStage(STAGE_TEMPORARY);
		contractManage.setApprovalStatus(STATUS_REVIEWING);
		appendCreateChangeRecordIfAbsent(contractManage);
		return saveOrUpdate(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean toTemporary(Long id) {
		ContractManage contractManage = loadEditable(id);
		if (!Objects.equals(contractManage.getContractStage(), STAGE_DRAFT) || !Objects.equals(contractManage.getApprovalStatus(), STATUS_DRAFT)) {
			throw new ServiceException("仅草稿合同允许转临时合同");
		}
		contractManage.setContractStage(STAGE_TEMPORARY);
		contractManage.setApprovalStatus(STATUS_REVIEWING);
		contractManage.setCurrentNode("临时合同审批");
		contractManage.setCurrentProcessor("待处理");
		return updateById(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submitFormal(Long id) {
		ContractManage contractManage = loadExists(id);
		if (!Objects.equals(contractManage.getContractStage(), STAGE_TEMPORARY) || !List.of(STATUS_REJECTED, STATUS_APPROVED, STATUS_DRAFT).contains(contractManage.getApprovalStatus())) {
			throw new ServiceException("仅临时合同允许转正式合同");
		}
		validateFormal(contractManage);
		contractManage.setContractStage(STAGE_FORMAL);
		contractManage.setApprovalStatus(STATUS_REVIEWING);
		contractManage.setCurrentNode("正式合同审批");
		contractManage.setCurrentProcessor("待处理");
		return updateById(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean approve(Long id) {
		ContractManage contractManage = loadReviewing(id);
		boolean changeReviewing = Objects.equals(contractManage.getApprovalStatus(), STATUS_CHANGE_REVIEWING);
		contractManage.setApprovalStatus(changeReviewing ? STATUS_CHANGE_APPROVED : STATUS_APPROVED);
		contractManage.setCurrentNode("审批通过");
		contractManage.setCurrentProcessor(AuthUtil.getUserName());
		contractManage.setApprovedTime(LocalDateTime.now());
		if (Objects.equals(contractManage.getContractStage(), STAGE_TEMPORARY) && contractManage.getTemporaryStartDate() == null) {
			contractManage.setTemporaryStartDate(LocalDate.now());
		}
		updateLatestReviewingChangeRecord(contractManage, contractManage.getApprovalStatus(), "审核通过");
		return updateById(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean reject(Long id) {
		ContractManage contractManage = loadReviewing(id);
		boolean changeReviewing = Objects.equals(contractManage.getApprovalStatus(), STATUS_CHANGE_REVIEWING);
		contractManage.setApprovalStatus(changeReviewing ? STATUS_CHANGE_REJECTED : STATUS_REJECTED);
		contractManage.setCurrentNode("已驳回");
		contractManage.setCurrentProcessor(AuthUtil.getUserName());
		updateLatestReviewingChangeRecord(contractManage, contractManage.getApprovalStatus(), "审核不通过");
		return updateById(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean withdraw(Long id) {
		ContractManage contractManage = loadReviewing(id);
		boolean changeReviewing = Objects.equals(contractManage.getApprovalStatus(), STATUS_CHANGE_REVIEWING);
		contractManage.setApprovalStatus(changeReviewing ? STATUS_APPROVED : STATUS_DRAFT);
		contractManage.setCurrentNode("已撤回");
		contractManage.setCurrentProcessor(AuthUtil.getUserName());
		updateLatestReviewingChangeRecord(contractManage, "withdrawn", "已撤回");
		return updateById(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean startChange(Long id, String changeContent, String changeReason) {
		ContractManage contractManage = loadExists(id);
		if (!Objects.equals(contractManage.getContractStage(), STAGE_FORMAL)
			|| !List.of(STATUS_APPROVED, STATUS_CHANGE_APPROVED, STATUS_CHANGE_REJECTED).contains(contractManage.getApprovalStatus())) {
			throw new ServiceException("仅正式合同允许发起变更");
		}
		contractManage.setApprovalStatus(STATUS_CHANGE_REVIEWING);
		contractManage.setChangeContent(TransportBusinessSupport.trimToNull(changeContent));
		contractManage.setChangeReason(TransportBusinessSupport.trimToNull(changeReason));
		contractManage.setCurrentNode("合同变更审批");
		contractManage.setCurrentProcessor("待处理");
		appendChangeRecord(contractManage, "合同信息变更", contractManage.getChangeReason(), STATUS_CHANGE_REVIEWING, "审核中");
		return updateById(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean updateAttachments(ContractManage contractManage) {
		ContractManage source = loadExists(contractManage.getId());
		if (!Objects.equals(source.getContractStage(), STAGE_FORMAL)
			|| !List.of(STATUS_APPROVED, STATUS_CHANGE_APPROVED).contains(source.getApprovalStatus())) {
			throw new ServiceException("仅审批通过的正式合同允许上传附件");
		}
		source.setContractFileJson(TransportBusinessSupport.trimToNull(contractManage.getContractFileJson()));
		source.setAttachmentsJson(TransportBusinessSupport.trimToNull(contractManage.getAttachmentsJson()));
		return updateById(source);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean terminate(Long id, String reason) {
		ContractManage contractManage = loadExists(id);
		if (!Objects.equals(contractManage.getApprovalStatus(), STATUS_APPROVED) || !Objects.equals(contractManage.getContractStage(), STAGE_FORMAL)) {
			throw new ServiceException("仅正式合同允许终止");
		}
		contractManage.setContractStage(STAGE_TERMINATED);
		contractManage.setTerminateReason(TransportBusinessSupport.trimToNull(reason));
		contractManage.setCurrentNode("已终止");
		contractManage.setCurrentProcessor(AuthUtil.getUserName());
		appendChangeRecord(contractManage, "终止", contractManage.getTerminateReason(), STATUS_APPROVED, "已终止");
		return updateById(contractManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean removeDraft(String ids) {
		List<Long> idList = Func.toLongList(ids);
		if (Func.isEmpty(idList)) {
			throw new ServiceException("请选择需要删除的数据");
		}
		for (ContractManage contractManage : listByIds(idList)) {
			if (!Objects.equals(contractManage.getContractStage(), STAGE_DRAFT) || !Objects.equals(contractManage.getApprovalStatus(), STATUS_DRAFT)) {
				throw new ServiceException("仅草稿合同允许删除");
			}
		}
		return deleteLogic(idList);
	}

	@Override
	public ContractManageVO copy(Long id) {
		ContractManage source = loadExists(id);
		ContractManage target = new ContractManage();
		BeanUtil.copyProperties(source, target);
		target.setId(null);
		target.setContractNo(null);
		target.setContractName(source.getContractName() + " - 副本");
		target.setContractStage(STAGE_DRAFT);
		target.setApprovalStatus(STATUS_DRAFT);
		target.setCurrentNode(null);
		target.setCurrentProcessor(null);
		target.setApprovedTime(null);
		target.setChangeContent(null);
		target.setChangeReason(null);
		target.setTerminateReason(null);
		saveDraft(target);
		return detail(target.getId());
	}

	@Override
	public Map<String, Long> expireStats(ContractManageVO contractManage) {
		List<ContractManage> contracts = list(buildQuery(contractManage));
		Map<String, Long> stats = new LinkedHashMap<>();
		stats.put("all", (long) contracts.size());
		stats.put("within30", 0L);
		stats.put("within90", 0L);
		stats.put("over90", 0L);
		stats.put("expired", 0L);
		contracts.stream()
			.filter(item -> item.getEndDate() != null)
			.map(item -> expireScope(item.getEndDate()))
			.forEach(scope -> stats.computeIfPresent(scope, (key, count) -> count + 1));
		return stats;
	}

	@Override
	public List<ContractManageExcel> exportContractManage(ContractManageVO contractManage, String ids) {
		LambdaQueryWrapper<ContractManage> queryWrapper = buildQuery(contractManage);
		if (Func.isNotEmpty(ids)) {
			queryWrapper.in(ContractManage::getId, Func.toLongList(ids));
		}
		return list(queryWrapper).stream().map(record -> {
			ContractManageExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(record, ContractManageExcel.class));
			excel.setCreateUserName(UserCache.getUserRealName(record.getCreateUser()));
			excel.setUpdateUserName(UserCache.getUserRealName(record.getUpdateUser()));
			return excel;
		}).toList();
	}

	private LambdaQueryWrapper<ContractManage> buildQuery(ContractManageVO contractManage) {
		TransportBusinessSupport.validateAllDept(contractManage.getAllDept(), "合同管理");
		LambdaQueryWrapper<ContractManage> queryWrapper = Wrappers.<ContractManage>lambdaQuery().eq(ContractManage::getIsDeleted, 0);
		if (!Objects.equals(contractManage.getAllDept(), 1)) {
			Long currentDeptId = TransportBusinessSupport.currentDeptId("合同管理");
			Long currentUserId = AuthUtil.getUserId();
			queryWrapper.and(wrapper -> wrapper.eq(ContractManage::getOrganizationId, currentDeptId)
				.or().eq(ContractManage::getCreateUser, currentUserId)
				.or().eq(ContractManage::getHandlerUserId, currentUserId));
		} else if (Func.isNotEmpty(contractManage.getOrganizationId())) {
			queryWrapper.eq(ContractManage::getOrganizationId, contractManage.getOrganizationId());
		}
		queryWrapper.like(Func.isNotEmpty(contractManage.getContractNo()), ContractManage::getContractNo, contractManage.getContractNo())
			.like(Func.isNotEmpty(contractManage.getContractName()), ContractManage::getContractName, contractManage.getContractName())
			.like(Func.isNotEmpty(contractManage.getProjectName()), ContractManage::getProjectName, contractManage.getProjectName())
			.like(Func.isNotEmpty(contractManage.getOrganizationName()), ContractManage::getOrganizationName, contractManage.getOrganizationName())
			.eq(Func.isNotEmpty(contractManage.getContractCategory()), ContractManage::getContractCategory, contractManage.getContractCategory())
			.eq(Func.isNotEmpty(contractManage.getSignType()), ContractManage::getSignType, contractManage.getSignType())
			.eq(Func.isNotEmpty(contractManage.getApprovalStatus()), ContractManage::getApprovalStatus, contractManage.getApprovalStatus())
			.eq(Func.isNotEmpty(contractManage.getContractStage()), ContractManage::getContractStage, contractManage.getContractStage())
			.orderByDesc(ContractManage::getCreateTime);
		applyEffectiveType(queryWrapper, contractManage.getEffectiveType());
		applyExpireScope(queryWrapper, contractManage.getExpireScope());
		return queryWrapper;
	}

	private void applyEffectiveType(LambdaQueryWrapper<ContractManage> queryWrapper, String effectiveType) {
		if (Func.isEmpty(effectiveType)) {
			return;
		}
		LocalDate today = LocalDate.now();
		switch (effectiveType) {
			case "not_effective" -> queryWrapper.and(wrapper -> wrapper
				.notIn(ContractManage::getApprovalStatus, STATUS_APPROVED, STATUS_CHANGE_APPROVED)
				.or(item -> item.eq(ContractManage::getContractStage, STAGE_TEMPORARY)
					.gt(ContractManage::getTemporaryStartDate, today))
				.or(item -> item.ne(ContractManage::getContractStage, STAGE_TEMPORARY)
					.gt(ContractManage::getStartDate, today)));
			case "temporary_effective" -> queryWrapper.eq(ContractManage::getContractStage, STAGE_TEMPORARY)
				.in(ContractManage::getApprovalStatus, STATUS_APPROVED, STATUS_CHANGE_APPROVED)
				.and(wrapper -> wrapper.isNull(ContractManage::getTemporaryStartDate)
					.or().le(ContractManage::getTemporaryStartDate, today))
				.and(wrapper -> wrapper.isNull(ContractManage::getTemporaryEndDate)
					.or().ge(ContractManage::getTemporaryEndDate, today));
			case "formal_effective" -> queryWrapper.eq(ContractManage::getContractStage, STAGE_FORMAL)
				.in(ContractManage::getApprovalStatus, STATUS_APPROVED, STATUS_CHANGE_APPROVED)
				.and(wrapper -> wrapper.isNull(ContractManage::getStartDate)
					.or().le(ContractManage::getStartDate, today))
				.and(wrapper -> wrapper.isNull(ContractManage::getEndDate)
					.or().ge(ContractManage::getEndDate, today));
			case "expired" -> queryWrapper.in(ContractManage::getApprovalStatus, STATUS_APPROVED, STATUS_CHANGE_APPROVED)
				.and(wrapper -> wrapper.eq(ContractManage::getContractStage, STAGE_TEMPORARY)
					.lt(ContractManage::getTemporaryEndDate, today)
					.or(wrapper2 -> wrapper2.ne(ContractManage::getContractStage, STAGE_TEMPORARY)
						.lt(ContractManage::getEndDate, today)));
			default -> throw new ServiceException("无效的生效类型筛选条件");
		}
	}

	private void applyExpireScope(LambdaQueryWrapper<ContractManage> queryWrapper, String expireScope) {
		if (Func.isEmpty(expireScope)) {
			return;
		}
		LocalDate today = LocalDate.now();
		switch (expireScope) {
			case "within30" -> queryWrapper.ge(ContractManage::getEndDate, today).le(ContractManage::getEndDate, today.plusDays(30));
			case "within90" -> queryWrapper.gt(ContractManage::getEndDate, today.plusDays(30)).le(ContractManage::getEndDate, today.plusDays(90));
			case "over90" -> queryWrapper.gt(ContractManage::getEndDate, today.plusDays(90));
			case "expired" -> queryWrapper.lt(ContractManage::getEndDate, today);
			default -> throw new ServiceException("无效的到期筛选条件");
		}
	}

	private void prepareCreateOrUpdate(ContractManage contractManage) {
		if (Func.isEmpty(contractManage.getId())) {
			if (Func.isEmpty(contractManage.getContractNo())) {
				contractManage.setContractNo("HT" + LocalDate.now().toString().replace("-", "") + String.format("%06d", count() + 1));
			}
			if (Func.isEmpty(contractManage.getOrganizationId())) {
				contractManage.setOrganizationId(TransportBusinessSupport.currentDeptId("合同管理"));
				contractManage.setOrganizationName(TransportBusinessSupport.currentDept("合同管理").getDeptName());
			}
			if (Func.isEmpty(contractManage.getHandlerUserId())) {
				contractManage.setHandlerUserId(AuthUtil.getUserId());
				contractManage.setHandlerUserName(AuthUtil.getUserName());
			}
		} else {
			ContractManage oldRecord = loadExists(contractManage.getId());
			contractManage.setContractNo(oldRecord.getContractNo());
			if (Func.isEmpty(contractManage.getOrganizationId())) {
				contractManage.setOrganizationId(oldRecord.getOrganizationId());
				contractManage.setOrganizationName(oldRecord.getOrganizationName());
			}
			contractManage.setHandlerUserId(oldRecord.getHandlerUserId());
			contractManage.setHandlerUserName(oldRecord.getHandlerUserName());
		}
		if (Func.isEmpty(contractManage.getContractStage())) {
			contractManage.setContractStage(STAGE_DRAFT);
		}
	}

	private void prepare(ContractManage contractManage) {
		contractManage.setContractName(TransportBusinessSupport.trimToNull(contractManage.getContractName()));
		contractManage.setProjectName(TransportBusinessSupport.trimToNull(contractManage.getProjectName()));
		contractManage.setOrganizationName(TransportBusinessSupport.trimToNull(contractManage.getOrganizationName()));
		contractManage.setContractCategory(TransportBusinessSupport.trimToNull(contractManage.getContractCategory()));
		contractManage.setSignType(TransportBusinessSupport.trimToNull(contractManage.getSignType()));
		contractManage.setPartyA(TransportBusinessSupport.trimToNull(contractManage.getPartyA()));
		contractManage.setPartyB(TransportBusinessSupport.trimToNull(contractManage.getPartyB()));
		contractManage.setSettlementMode(TransportBusinessSupport.trimToNull(contractManage.getSettlementMode()));
		contractManage.setContractFormat(TransportBusinessSupport.trimToNull(contractManage.getContractFormat()));
		contractManage.setRemark(TransportBusinessSupport.trimToNull(contractManage.getRemark()));
		contractManage.setContractFileJson(TransportBusinessSupport.trimToNull(contractManage.getContractFileJson()));
		contractManage.setAttachmentsJson(TransportBusinessSupport.trimToNull(contractManage.getAttachmentsJson()));
		contractManage.setBillingPlanJson(TransportBusinessSupport.trimToNull(contractManage.getBillingPlanJson()));
		contractManage.setSettlementRuleJson(TransportBusinessSupport.trimToNull(contractManage.getSettlementRuleJson()));
		contractManage.setReconciliationJson(TransportBusinessSupport.trimToNull(contractManage.getReconciliationJson()));
		contractManage.setChangeRecordJson(TransportBusinessSupport.trimToNull(contractManage.getChangeRecordJson()));
		normalizeOptionalIntegerFields(contractManage);
	}

	private void normalizeOptionalIntegerFields(ContractManage contractManage) {
		if (contractManage.getCopyCount() != null && contractManage.getCopyCount() < 0) {
			contractManage.setCopyCount(null);
		}
		if (contractManage.getPaymentDays() != null && contractManage.getPaymentDays() < 0) {
			contractManage.setPaymentDays(null);
		}
	}

	private void validateDraft(ContractManage contractManage) {
		TransportBusinessSupport.validateRequired(contractManage.getContractName(), "请输入合同名称");
		TransportBusinessSupport.validateRequired(contractManage.getContractCategory(), "请选择合同类别");
		validateContractNameUnique(contractManage);
		TransportBusinessSupport.validateLength(contractManage.getRemark(), 2000, "备注不能超过2000字");
	}

	private void validateContractNameUnique(ContractManage contractManage) {
		Long count = count(Wrappers.<ContractManage>lambdaQuery()
			.eq(ContractManage::getIsDeleted, 0)
			.eq(ContractManage::getContractName, contractManage.getContractName())
			.ne(Func.isNotEmpty(contractManage.getId()), ContractManage::getId, contractManage.getId()));
		if (count > 0) {
			throw new ServiceException("合同名称已存在");
		}
	}

	private void validateTemporary(ContractManage contractManage) {
		TransportBusinessSupport.validateRequired(contractManage.getSignType(), "请选择签约类型");
	}

	private void validateFormal(ContractManage contractManage) {
		TransportBusinessSupport.validateRequired(contractManage.getPartyA(), "请输入甲方");
		TransportBusinessSupport.validateRequired(contractManage.getPartyB(), "请输入乙方");
		TransportBusinessSupport.validateRequired(contractManage.getStartDate() == null ? null : contractManage.getStartDate().toString(), "请选择合同期限");
		TransportBusinessSupport.validateRequired(contractManage.getSignDate() == null ? null : contractManage.getSignDate().toString(), "请选择签订日期");
		TransportBusinessSupport.validateDateRange(contractManage.getStartDate(), contractManage.getEndDate(), "合同期限开始日期不能晚于结束日期");
		TransportBusinessSupport.validateLength(contractManage.getRemark(), 2000, "备注不能超过2000字");
	}

	private ContractManage loadExists(Long id) {
		ContractManage contractManage = getById(id);
		if (Func.isEmpty(contractManage) || Objects.equals(contractManage.getIsDeleted(), 1)) {
			throw new ServiceException("合同不存在");
		}
		return contractManage;
	}

	private ContractManage loadEditable(Long id) {
		ContractManage contractManage = loadExists(id);
		if (!Objects.equals(contractManage.getApprovalStatus(), STATUS_DRAFT)) {
			throw new ServiceException("当前状态不允许编辑");
		}
		if (!canOperateInCurrentScope(contractManage)) {
			throw new ServiceException("无权操作其他组织合同管理");
		}
		return contractManage;
	}

	private ContractManage loadReviewing(Long id) {
		ContractManage contractManage = loadExists(id);
		if (!List.of(STATUS_REVIEWING, STATUS_CHANGE_REVIEWING).contains(contractManage.getApprovalStatus())) {
			throw new ServiceException("当前状态不允许操作");
		}
		return contractManage;
	}

	private void appendCreateChangeRecordIfAbsent(ContractManage contractManage) {
		List<Map<String, Object>> records = parseChangeRecords(contractManage.getChangeRecordJson());
		boolean exists = records.stream().anyMatch(record -> Objects.equals(record.get("changeType"), "创建"));
		if (!exists) {
			appendChangeRecord(contractManage, "创建", null, STATUS_REVIEWING, "审核中");
		}
	}

	private void appendChangeRecord(ContractManage contractManage, String changeType, String changeReason, String status, String statusName) {
		List<Map<String, Object>> records = parseChangeRecords(contractManage.getChangeRecordJson());
		Map<String, Object> record = new LinkedHashMap<>();
		record.put("changeDate", LocalDate.now().toString());
		record.put("handlerUserId", AuthUtil.getUserId());
		record.put("handlerUserName", AuthUtil.getUserName());
		record.put("changeType", changeType);
		record.put("changeReason", TransportBusinessSupport.trimToNull(changeReason));
		record.put("status", status);
		record.put("statusName", statusName);
		records.add(record);
		contractManage.setChangeRecordJson(JsonUtil.toJson(records));
	}

	private void updateLatestReviewingChangeRecord(ContractManage contractManage, String status, String statusName) {
		List<Map<String, Object>> records = parseChangeRecords(contractManage.getChangeRecordJson());
		for (int index = records.size() - 1; index >= 0; index--) {
			Map<String, Object> record = records.get(index);
			if (Objects.equals(record.get("status"), STATUS_REVIEWING) || Objects.equals(record.get("status"), STATUS_CHANGE_REVIEWING)) {
				record.put("status", status);
				record.put("statusName", statusName);
				contractManage.setChangeRecordJson(JsonUtil.toJson(records));
				return;
			}
		}
	}

	@SuppressWarnings("unchecked")
	private List<Map<String, Object>> parseChangeRecords(String value) {
		if (Func.isEmpty(value)) {
			return new ArrayList<>();
		}
		try {
			Object records = JsonUtil.parse(value, List.class);
			if (records instanceof List<?> list) {
				return (List<Map<String, Object>>) (List<?>) list;
			}
		} catch (Exception ignored) {
		}
		return new ArrayList<>();
	}

	private String expireScope(LocalDate endDate) {
		long days = java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), endDate);
		if (days < 0) {
			return "expired";
		}
		if (days <= 30) {
			return "within30";
		}
		if (days <= 90) {
			return "within90";
		}
		return "over90";
	}

	private void fillReadonly(ContractManageVO contractManageVO) {
		normalizeOptionalIntegerFields(contractManageVO);
		contractManageVO.setReadonly(!canOperateInCurrentScope(contractManageVO));
	}

	private boolean canOperateInCurrentScope(ContractManage contractManage) {
		Long currentDeptId = TransportBusinessSupport.currentDeptId("合同管理");
		Long currentUserId = AuthUtil.getUserId();
		return Objects.equals(contractManage.getOrganizationId(), currentDeptId)
			|| Objects.equals(contractManage.getCreateUser(), currentUserId)
			|| Objects.equals(contractManage.getHandlerUserId(), currentUserId);
	}

}
