/**
 * 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.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.UserCache;
import org.springblade.transport.excel.TemporaryCreditLimitExcel;
import org.springblade.transport.mapper.TemporaryCreditLimitMapper;
import org.springblade.transport.pojo.entity.TemporaryCreditLimit;
import org.springblade.transport.pojo.vo.TemporaryCreditLimitVO;
import org.springblade.transport.service.ITemporaryCreditLimitService;
import org.springblade.transport.support.TransportBusinessSupport;
import org.springblade.transport.wrapper.TemporaryCreditLimitWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * 临时额度申请 服务实现类
 *
 * @author Chill
 */
@Service
public class TemporaryCreditLimitServiceImpl extends BaseServiceImpl<TemporaryCreditLimitMapper, TemporaryCreditLimit> implements ITemporaryCreditLimitService {

	private static final String STATUS_DRAFT = "draft";
	private static final String STATUS_REVIEWING = "reviewing";
	private static final String STATUS_WITHDRAWN = "withdrawn";
	private static final String STATUS_APPROVED = "approved";
	private static final String STATUS_REJECTED = "rejected";

	@Override
	public IPage<TemporaryCreditLimitVO> selectTemporaryCreditLimitPage(IPage<TemporaryCreditLimit> page, TemporaryCreditLimitVO temporaryCreditLimit) {
		IPage<TemporaryCreditLimit> entityPage = page(page, buildQuery(temporaryCreditLimit));
		IPage<TemporaryCreditLimitVO> voPage = TemporaryCreditLimitWrapper.build().pageVO(entityPage);
		voPage.getRecords().forEach(this::fillReadonly);
		return voPage;
	}

	@Override
	public TemporaryCreditLimitVO detail(Long id) {
		TemporaryCreditLimitVO temporaryCreditLimitVO = TemporaryCreditLimitWrapper.build().entityVO(loadExists(id));
		fillReadonly(temporaryCreditLimitVO);
		return temporaryCreditLimitVO;
	}

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

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(TemporaryCreditLimit temporaryCreditLimit) {
		prepare(temporaryCreditLimit);
		validateSubmit(temporaryCreditLimit);
		if (Func.isNotEmpty(temporaryCreditLimit.getId())) {
			loadEditable(temporaryCreditLimit.getId());
		}
		prepareCreateOrUpdate(temporaryCreditLimit);
		temporaryCreditLimit.setApprovalStatus(STATUS_REVIEWING);
		temporaryCreditLimit.setCurrentNode("临时额度审批");
		temporaryCreditLimit.setCurrentProcessor("待处理");
		return saveOrUpdate(temporaryCreditLimit);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submitApproval(Long id) {
		TemporaryCreditLimit temporaryCreditLimit = loadEditable(id);
		validateSubmit(temporaryCreditLimit);
		temporaryCreditLimit.setApprovalStatus(STATUS_REVIEWING);
		temporaryCreditLimit.setCurrentNode("临时额度审批");
		temporaryCreditLimit.setCurrentProcessor("待处理");
		return updateById(temporaryCreditLimit);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean approve(Long id) {
		TemporaryCreditLimit temporaryCreditLimit = loadReviewing(id);
		temporaryCreditLimit.setApprovalStatus(STATUS_APPROVED);
		temporaryCreditLimit.setCurrentNode("审批通过");
		temporaryCreditLimit.setCurrentProcessor(AuthUtil.getUserName());
		temporaryCreditLimit.setApprovedTime(LocalDateTime.now());
		return updateById(temporaryCreditLimit);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean reject(Long id) {
		TemporaryCreditLimit temporaryCreditLimit = loadReviewing(id);
		temporaryCreditLimit.setApprovalStatus(STATUS_REJECTED);
		temporaryCreditLimit.setCurrentNode("审核不通过");
		temporaryCreditLimit.setCurrentProcessor(AuthUtil.getUserName());
		return updateById(temporaryCreditLimit);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean withdraw(Long id) {
		TemporaryCreditLimit temporaryCreditLimit = loadReviewing(id);
		temporaryCreditLimit.setApprovalStatus(STATUS_WITHDRAWN);
		temporaryCreditLimit.setCurrentNode("已撤回");
		temporaryCreditLimit.setCurrentProcessor(AuthUtil.getUserName());
		return updateById(temporaryCreditLimit);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean removeDraft(String ids) {
		List<Long> idList = Func.toLongList(ids);
		if (Func.isEmpty(idList)) {
			throw new ServiceException("请选择需要删除的数据");
		}
		for (TemporaryCreditLimit temporaryCreditLimit : listByIds(idList)) {
			if (!List.of(STATUS_DRAFT, STATUS_WITHDRAWN, STATUS_REJECTED).contains(temporaryCreditLimit.getApprovalStatus())) {
				throw new ServiceException("仅草稿、已撤回、已驳回单据允许删除");
			}
		}
		return deleteLogic(idList);
	}

	@Override
	public List<TemporaryCreditLimitExcel> exportTemporaryCreditLimit(TemporaryCreditLimitVO temporaryCreditLimit, String ids) {
		LambdaQueryWrapper<TemporaryCreditLimit> queryWrapper = buildQuery(temporaryCreditLimit);
		if (Func.isNotEmpty(ids)) {
			queryWrapper.in(TemporaryCreditLimit::getId, Func.toLongList(ids));
		}
		return list(queryWrapper).stream().map(record -> {
			TemporaryCreditLimitExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(record, TemporaryCreditLimitExcel.class));
			excel.setProjectFundLimit(scale(record.getProjectFundLimit()));
			excel.setUsedFundLimit(scale(record.getUsedFundLimit()));
			excel.setRemainingFundLimit(scale(record.getRemainingFundLimit()));
			excel.setApplyLimit(scale(record.getApplyLimit()));
			excel.setCreateUserName(UserCache.getUserRealName(record.getCreateUser()));
			excel.setUpdateUserName(UserCache.getUserRealName(record.getUpdateUser()));
			return excel;
		}).toList();
	}

	private LambdaQueryWrapper<TemporaryCreditLimit> buildQuery(TemporaryCreditLimitVO temporaryCreditLimit) {
		TransportBusinessSupport.validateAllDept(temporaryCreditLimit.getAllDept(), "临时额度管理");
		LambdaQueryWrapper<TemporaryCreditLimit> queryWrapper = Wrappers.<TemporaryCreditLimit>lambdaQuery().eq(TemporaryCreditLimit::getIsDeleted, 0);
		if (!Objects.equals(temporaryCreditLimit.getAllDept(), 1)) {
			queryWrapper.eq(TemporaryCreditLimit::getApplyDeptId, TransportBusinessSupport.currentDeptId("临时额度管理"));
		} else if (Func.isNotEmpty(temporaryCreditLimit.getApplyDeptId())) {
			queryWrapper.eq(TemporaryCreditLimit::getApplyDeptId, temporaryCreditLimit.getApplyDeptId());
		}
		queryWrapper.like(Func.isNotEmpty(temporaryCreditLimit.getApplicationNo()), TemporaryCreditLimit::getApplicationNo, temporaryCreditLimit.getApplicationNo())
			.like(Func.isNotEmpty(temporaryCreditLimit.getProjectName()), TemporaryCreditLimit::getProjectName, temporaryCreditLimit.getProjectName())
			.like(Func.isNotEmpty(temporaryCreditLimit.getApplicantName()), TemporaryCreditLimit::getApplicantName, temporaryCreditLimit.getApplicantName())
			.like(Func.isNotEmpty(temporaryCreditLimit.getApplyDeptName()), TemporaryCreditLimit::getApplyDeptName, temporaryCreditLimit.getApplyDeptName())
			.eq(Func.isNotEmpty(temporaryCreditLimit.getApprovalStatus()), TemporaryCreditLimit::getApprovalStatus, temporaryCreditLimit.getApprovalStatus())
			.orderByDesc(TemporaryCreditLimit::getCreateTime);
		return queryWrapper;
	}

	private void prepareCreateOrUpdate(TemporaryCreditLimit temporaryCreditLimit) {
		if (Func.isEmpty(temporaryCreditLimit.getId())) {
			if (Func.isEmpty(temporaryCreditLimit.getApplicationNo())) {
				temporaryCreditLimit.setApplicationNo("TQ" + LocalDate.now().toString().replace("-", "") + String.format("%06d", count() + 1));
			}
			if (Func.isEmpty(temporaryCreditLimit.getApplyDeptId())) {
				temporaryCreditLimit.setApplyDeptId(TransportBusinessSupport.currentDeptId("临时额度管理"));
				temporaryCreditLimit.setApplyDeptName(TransportBusinessSupport.currentDept("临时额度管理").getDeptName());
			}
			if (Func.isEmpty(temporaryCreditLimit.getApplicantId())) {
				temporaryCreditLimit.setApplicantId(AuthUtil.getUserId());
				temporaryCreditLimit.setApplicantName(AuthUtil.getUserName());
			}
		} else {
			TemporaryCreditLimit oldRecord = loadExists(temporaryCreditLimit.getId());
			temporaryCreditLimit.setApplicationNo(oldRecord.getApplicationNo());
			temporaryCreditLimit.setApplyDeptId(oldRecord.getApplyDeptId());
			temporaryCreditLimit.setApplyDeptName(oldRecord.getApplyDeptName());
			temporaryCreditLimit.setApplicantId(oldRecord.getApplicantId());
			temporaryCreditLimit.setApplicantName(oldRecord.getApplicantName());
		}
	}

	private void prepare(TemporaryCreditLimit temporaryCreditLimit) {
		temporaryCreditLimit.setProjectCode(TransportBusinessSupport.trimToNull(temporaryCreditLimit.getProjectCode()));
		temporaryCreditLimit.setProjectName(TransportBusinessSupport.trimToNull(temporaryCreditLimit.getProjectName()));
		temporaryCreditLimit.setApplyDeptName(TransportBusinessSupport.trimToNull(temporaryCreditLimit.getApplyDeptName()));
		temporaryCreditLimit.setApplicantName(TransportBusinessSupport.trimToNull(temporaryCreditLimit.getApplicantName()));
		temporaryCreditLimit.setRemark(TransportBusinessSupport.trimToNull(temporaryCreditLimit.getRemark()));
		temporaryCreditLimit.setAttachmentsJson(TransportBusinessSupport.trimToNull(temporaryCreditLimit.getAttachmentsJson()));
	}

	private void validateDraft(TemporaryCreditLimit temporaryCreditLimit) {
		TransportBusinessSupport.validateRequired(temporaryCreditLimit.getProjectName(), "请选择项目");
		validateApplyLimit(temporaryCreditLimit.getApplyLimit());
		validateRemark(temporaryCreditLimit.getRemark());
	}

	private void validateSubmit(TemporaryCreditLimit temporaryCreditLimit) {
		validateDraft(temporaryCreditLimit);
		TransportBusinessSupport.validateRequired(temporaryCreditLimit.getProjectCode(), "项目编号不能为空");
		TransportBusinessSupport.validateRequired(temporaryCreditLimit.getUndertakeDeptName(), "承办部门不能为空");
		TransportBusinessSupport.validateRequired(temporaryCreditLimit.getValidUntil() == null ? null : temporaryCreditLimit.getValidUntil().toString(), "请选择申请有效期至");
		if (temporaryCreditLimit.getValidUntil() != null && !temporaryCreditLimit.getValidUntil().isAfter(LocalDate.now())) {
			throw new ServiceException("申请有效期至必须晚于当前日期");
		}
		if (temporaryCreditLimit.getProjectFundLimit() == null || temporaryCreditLimit.getUsedFundLimit() == null || temporaryCreditLimit.getRemainingFundLimit() == null) {
			throw new ServiceException("项目额度数据异常，请重新选择项目");
		}
		if (temporaryCreditLimit.getRemainingFundLimit().compareTo(BigDecimal.ZERO) < 0) {
			throw new ServiceException("项目剩余额度不能小于0");
		}
	}

	private void validateApplyLimit(BigDecimal applyLimit) {
		if (applyLimit == null || applyLimit.compareTo(BigDecimal.ZERO) <= 0) {
			throw new ServiceException("申请临时额度必须大于0");
		}
		if (applyLimit.scale() > 2) {
			throw new ServiceException("申请临时额度最多保留2位小数");
		}
	}

	private void validateRemark(String remark) {
		TransportBusinessSupport.validateLength(remark, 500, "备注不能超过500字");
	}

	private TemporaryCreditLimit loadExists(Long id) {
		TemporaryCreditLimit temporaryCreditLimit = getById(id);
		if (Func.isEmpty(temporaryCreditLimit) || Objects.equals(temporaryCreditLimit.getIsDeleted(), 1)) {
			throw new ServiceException("临时额度申请不存在");
		}
		return temporaryCreditLimit;
	}

	private TemporaryCreditLimit loadEditable(Long id) {
		TemporaryCreditLimit temporaryCreditLimit = loadExists(id);
		if (!List.of(STATUS_DRAFT, STATUS_REJECTED, STATUS_WITHDRAWN).contains(temporaryCreditLimit.getApprovalStatus())) {
			throw new ServiceException("当前状态不允许编辑");
		}
		TransportBusinessSupport.assertCurrentDept(temporaryCreditLimit.getApplyDeptId(), "临时额度管理");
		return temporaryCreditLimit;
	}

	private TemporaryCreditLimit loadReviewing(Long id) {
		TemporaryCreditLimit temporaryCreditLimit = loadExists(id);
		if (!Objects.equals(temporaryCreditLimit.getApprovalStatus(), STATUS_REVIEWING)) {
			throw new ServiceException("当前状态不允许操作");
		}
		return temporaryCreditLimit;
	}

	private BigDecimal scale(BigDecimal value) {
		return value == null ? null : value.setScale(2, RoundingMode.HALF_UP);
	}

	private void fillReadonly(TemporaryCreditLimitVO temporaryCreditLimitVO) {
		temporaryCreditLimitVO.setReadonly(!Objects.equals(temporaryCreditLimitVO.getApplyDeptId(), TransportBusinessSupport.currentDeptId("临时额度管理")));
	}

}
