/**
 * 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>
 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
 * not liable for any claims arising from secondary or illegal development.
 * <p>
 * Author: Chill Zhuang (bladejava@qq.com)
 */
package org.springblade.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
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.Func;
import org.springblade.system.cache.UserCache;
import org.springblade.system.excel.CargoTypeExcel;
import org.springblade.system.excel.CargoTypeExportExcel;
import org.springblade.system.excel.CargoTypeImportFailureExcel;
import org.springblade.system.mapper.CargoTypeMapper;
import org.springblade.system.pojo.entity.CargoType;
import org.springblade.system.pojo.vo.CargoTypeVO;
import org.springblade.system.service.ICargoTypeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 货物类型 服务实现类
 *
 * @author Chill
 */
@Service
public class CargoTypeServiceImpl extends BaseServiceImpl<CargoTypeMapper, CargoType> implements ICargoTypeService {

	private static final int TYPE_LEVEL_ONE = 1;
	private static final int TYPE_LEVEL_TWO = 2;
	private static final int STATUS_ENABLED = 1;
	private static final int CARGO_NAME_MAX_LENGTH = 50;
	private static final int REMARK_MAX_LENGTH = 200;
	private static final String SOURCE_BATCH = "批量导入";
	private static final String SOURCE_MANUAL = "手工录入";
	private static final Pattern PARENT_CODE_PATTERN = Pattern.compile("^\\d{2}$");
	private static final Pattern CHILD_CODE_PATTERN = Pattern.compile("^\\d{4}$");

	@Override
	public IPage<CargoTypeVO> selectCargoTypePage(IPage<CargoTypeVO> page, CargoTypeVO cargoType) {
		return page.setRecords(baseMapper.selectCargoTypePage(page, cargoType));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(CargoType cargoType) {
		prepare(cargoType, SOURCE_MANUAL);
		validate(cargoType);
		return saveOrUpdate(cargoType);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean deleteCargoTypes(List<Long> ids) {
		if (Func.isEmpty(ids)) {
			throw new ServiceException("请选择至少一条数据");
		}
		long childCount = count(Wrappers.<CargoType>lambdaQuery()
			.in(CargoType::getParentId, ids)
			.eq(CargoType::getIsDeleted, 0));
		if (childCount > 0L) {
			throw new ServiceException("存在下级货物类型，不能删除");
		}
		return deleteLogic(ids);
	}

	@Override
	public List<CargoTypeVO> parentOptions(String keyword) {
		String trimKeyword = trimToEmpty(keyword);
		LambdaQueryWrapper<CargoType> queryWrapper = Wrappers.<CargoType>lambdaQuery()
			.eq(CargoType::getTypeLevel, TYPE_LEVEL_ONE)
			.eq(CargoType::getIsDeleted, 0)
			.orderByAsc(CargoType::getCargoCode);
		if (Func.isNotEmpty(trimKeyword)) {
			queryWrapper.and(wrapper -> wrapper.like(CargoType::getCargoName, trimKeyword)
				.or()
				.like(CargoType::getCargoCode, trimKeyword));
		}
		return list(queryWrapper).stream().map(this::toParentOption).toList();
	}

	@Override
	public String nextChildCode(String parentCargoCode) {
		CargoType parent = findParentByCode(trimToEmpty(parentCargoCode));
		if (parent == null) {
			throw new ServiceException("请选择上级货物类型");
		}
		String parentCode = parent.getCargoCode();
		String maxCode = list(Wrappers.<CargoType>lambdaQuery()
			.eq(CargoType::getTypeLevel, TYPE_LEVEL_TWO)
			.eq(CargoType::getParentCargoCode, parentCode)
			.eq(CargoType::getIsDeleted, 0)
			.orderByDesc(CargoType::getCargoCode))
			.stream()
			.map(CargoType::getCargoCode)
			.filter(code -> code != null && CHILD_CODE_PATTERN.matcher(code).matches())
			.findFirst()
			.orElse(null);
		int nextSerial = maxCode == null ? 1 : Integer.parseInt(maxCode.substring(2)) + 1;
		if (nextSerial > 99) {
			throw new ServiceException("二级编码序号已超过99");
		}
		return parentCode + String.format("%02d", nextSerial);
	}

	@Override
	public List<CargoTypeImportFailureExcel> importCargoType(List<CargoTypeExcel> data) {
		if (Func.isEmpty(data)) {
			throw new ServiceException("导入数据不能为空");
		}
		List<CargoTypeImportFailureExcel> failureList = new ArrayList<>();
		for (int index = 0; index < data.size(); index++) {
			CargoTypeExcel excel = data.get(index);
			try {
				CargoType cargoType = buildImportCargoType(excel);
				prepare(cargoType, SOURCE_BATCH);
				validate(cargoType);
				save(cargoType);
			} catch (Exception exception) {
				String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
				excel.setErrorMessage(message);
				failureList.add(toImportFailureExcel(excel, "第" + (index + 2) + "行：" + message));
			}
		}
		return failureList;
	}

	@Override
	public List<CargoTypeExportExcel> exportCargoType(Wrapper<CargoType> queryWrapper) {
		return list(queryWrapper).stream().map(this::toExportExcel).toList();
	}

	private CargoType buildImportCargoType(CargoTypeExcel excel) {
		CargoType cargoType = new CargoType();
		cargoType.setTypeLevel(parseTypeLevel(excel.getTypeLevelName()));
		cargoType.setParentCargoCode(trimToNull(excel.getParentCargoCode()));
		cargoType.setCargoName(trimToEmpty(excel.getCargoName()));
		cargoType.setCargoCode(trimToEmpty(excel.getCargoCode()));
		cargoType.setRemark(trimToNull(excel.getRemark()));
		if (Objects.equals(cargoType.getTypeLevel(), TYPE_LEVEL_TWO)) {
			CargoType parent = resolveImportParent(excel);
			cargoType.setParentId(parent.getId());
			cargoType.setParentCargoCode(parent.getCargoCode());
		}
		return cargoType;
	}

	private Integer parseTypeLevel(String typeLevelName) {
		String value = trimToEmpty(typeLevelName);
		if ("一级货物类型".equals(value)) {
			return TYPE_LEVEL_ONE;
		}
		if ("二级货物类型".equals(value)) {
			return TYPE_LEVEL_TWO;
		}
		throw new ServiceException("请选择货物类型级别");
	}

	private CargoType resolveImportParent(CargoTypeExcel excel) {
		String parentCode = trimToEmpty(excel.getParentCargoCode());
		String parentName = trimToEmpty(excel.getParentCargoName());
		if (Func.isEmpty(parentCode) && Func.isEmpty(parentName)) {
			throw new ServiceException("请选择上级货物类型");
		}
		CargoType parent = Func.isNotEmpty(parentCode) ? findParentByCode(parentCode) : findParentByName(parentName);
		if (parent == null) {
			throw new ServiceException("请选择上级货物类型");
		}
		if (Func.isNotEmpty(parentName) && !Objects.equals(parent.getCargoName(), parentName)) {
			throw new ServiceException("请选择上级货物类型");
		}
		return parent;
	}

	private void prepare(CargoType cargoType, String defaultDataSource) {
		cargoType.setCargoName(trimToEmpty(cargoType.getCargoName()));
		cargoType.setCargoCode(trimToEmpty(cargoType.getCargoCode()));
		cargoType.setParentCargoCode(trimToNull(cargoType.getParentCargoCode()));
		cargoType.setDataSource(Func.toStrWithEmpty(cargoType.getDataSource(), defaultDataSource));
		cargoType.setRemark(trimToNull(cargoType.getRemark()));
		if (Objects.equals(cargoType.getTypeLevel(), TYPE_LEVEL_ONE)) {
			cargoType.setParentId(null);
			cargoType.setParentCargoCode(null);
		}
		if (Func.isEmpty(cargoType.getStatus())) {
			cargoType.setStatus(STATUS_ENABLED);
		}
	}

	private void validate(CargoType cargoType) {
		if (!Objects.equals(cargoType.getTypeLevel(), TYPE_LEVEL_ONE) && !Objects.equals(cargoType.getTypeLevel(), TYPE_LEVEL_TWO)) {
			throw new ServiceException("请选择货物类型级别");
		}
		if (Func.isEmpty(cargoType.getCargoName())) {
			throw new ServiceException("请输入货物类型名称");
		}
		if (cargoType.getCargoName().length() > CARGO_NAME_MAX_LENGTH) {
			throw new ServiceException("货物类型名称不能超过50字符");
		}
		if (Func.isEmpty(cargoType.getCargoCode())) {
			throw new ServiceException("货物类型编码格式不正确");
		}
		if (Objects.equals(cargoType.getTypeLevel(), TYPE_LEVEL_ONE)) {
			validateParentCargoType(cargoType);
		} else {
			validateChildCargoType(cargoType);
		}
		if (Func.isNotEmpty(cargoType.getRemark()) && cargoType.getRemark().length() > REMARK_MAX_LENGTH) {
			throw new ServiceException("备注不能超过200字");
		}
		validateUniqueCode(cargoType);
		validateUniqueName(cargoType);
	}

	private void validateParentCargoType(CargoType cargoType) {
		if (!PARENT_CODE_PATTERN.matcher(cargoType.getCargoCode()).matches()) {
			throw new ServiceException("货物类型编码格式不正确");
		}
	}

	private void validateChildCargoType(CargoType cargoType) {
		CargoType parent = resolveParent(cargoType);
		if (parent == null) {
			throw new ServiceException("请选择上级货物类型");
		}
		if (!CHILD_CODE_PATTERN.matcher(cargoType.getCargoCode()).matches()) {
			throw new ServiceException("货物类型编码格式不正确");
		}
		if (!cargoType.getCargoCode().startsWith(parent.getCargoCode())) {
			throw new ServiceException("货物类型编码前2位必须与上级货物类型编码一致");
		}
		if (Objects.equals(cargoType.getId(), parent.getId())) {
			throw new ServiceException("请选择上级货物类型");
		}
		cargoType.setParentId(parent.getId());
		cargoType.setParentCargoCode(parent.getCargoCode());
	}

	private CargoType resolveParent(CargoType cargoType) {
		if (Func.isNotEmpty(cargoType.getParentId())) {
			CargoType parent = getById(cargoType.getParentId());
			if (parent != null && Objects.equals(parent.getTypeLevel(), TYPE_LEVEL_ONE) && Objects.equals(parent.getIsDeleted(), 0)) {
				return parent;
			}
		}
		if (Func.isNotEmpty(cargoType.getParentCargoCode())) {
			return findParentByCode(cargoType.getParentCargoCode());
		}
		return null;
	}

	private CargoType findParentByCode(String cargoCode) {
		String code = trimToEmpty(cargoCode);
		if (Func.isEmpty(code)) {
			return null;
		}
		return getOne(Wrappers.<CargoType>lambdaQuery()
			.eq(CargoType::getTypeLevel, TYPE_LEVEL_ONE)
			.eq(CargoType::getCargoCode, code)
			.eq(CargoType::getIsDeleted, 0)
			.last("limit 1"));
	}

	private CargoType findParentByName(String cargoName) {
		String name = trimToEmpty(cargoName);
		if (Func.isEmpty(name)) {
			return null;
		}
		return getOne(Wrappers.<CargoType>lambdaQuery()
			.eq(CargoType::getTypeLevel, TYPE_LEVEL_ONE)
			.eq(CargoType::getCargoName, name)
			.eq(CargoType::getIsDeleted, 0)
			.last("limit 1"));
	}

	private void validateUniqueCode(CargoType cargoType) {
		LambdaQueryWrapper<CargoType> queryWrapper = Wrappers.<CargoType>lambdaQuery()
			.eq(CargoType::getCargoCode, cargoType.getCargoCode())
			.eq(CargoType::getIsDeleted, 0);
		if (Func.isNotEmpty(cargoType.getId())) {
			queryWrapper.ne(CargoType::getId, cargoType.getId());
		}
		if (count(queryWrapper) > 0L) {
			throw new ServiceException("该货物类型编码已存在");
		}
	}

	private void validateUniqueName(CargoType cargoType) {
		LambdaQueryWrapper<CargoType> queryWrapper = Wrappers.<CargoType>lambdaQuery()
			.eq(CargoType::getCargoName, cargoType.getCargoName())
			.eq(CargoType::getTypeLevel, cargoType.getTypeLevel())
			.eq(CargoType::getIsDeleted, 0);
		if (Objects.equals(cargoType.getTypeLevel(), TYPE_LEVEL_TWO)) {
			queryWrapper.eq(CargoType::getParentCargoCode, cargoType.getParentCargoCode());
		}
		if (Func.isNotEmpty(cargoType.getId())) {
			queryWrapper.ne(CargoType::getId, cargoType.getId());
		}
		if (count(queryWrapper) > 0L) {
			throw new ServiceException("该上级下已存在同名货物类型");
		}
	}

	private CargoTypeVO toParentOption(CargoType cargoType) {
		CargoTypeVO cargoTypeVO = new CargoTypeVO();
		cargoTypeVO.setId(cargoType.getId());
		cargoTypeVO.setCargoName(cargoType.getCargoName());
		cargoTypeVO.setCargoCode(cargoType.getCargoCode());
		cargoTypeVO.setTypeLevel(cargoType.getTypeLevel());
		cargoTypeVO.setTypeLevelName("一级货物类型");
		return cargoTypeVO;
	}

	private CargoTypeExportExcel toExportExcel(CargoType cargoType) {
		CargoTypeExportExcel excel = new CargoTypeExportExcel();
		excel.setCargoName(cargoType.getCargoName());
		excel.setCargoCode(cargoType.getCargoCode());
		excel.setParentCargoName("/");
		excel.setParentCargoCode("/");
		if (Objects.equals(cargoType.getTypeLevel(), TYPE_LEVEL_TWO)) {
			CargoType parent = resolveParent(cargoType);
			excel.setParentCargoName(parent == null ? "" : parent.getCargoName());
			excel.setParentCargoCode(cargoType.getParentCargoCode());
		}
		excel.setCreateUserName(Func.isEmpty(cargoType.getCreateUser()) ? "" : UserCache.getUserRealName(cargoType.getCreateUser()));
		excel.setRemark(cargoType.getRemark());
		excel.setUpdateTime(cargoType.getUpdateTime());
		excel.setCreateTime(cargoType.getCreateTime());
		return excel;
	}

	private CargoTypeImportFailureExcel toImportFailureExcel(CargoTypeExcel excel, String failureReason) {
		CargoTypeImportFailureExcel failureExcel = new CargoTypeImportFailureExcel();
		failureExcel.setTypeLevelName(excel.getTypeLevelName());
		failureExcel.setParentCargoName(excel.getParentCargoName());
		failureExcel.setParentCargoCode(excel.getParentCargoCode());
		failureExcel.setCargoName(excel.getCargoName());
		failureExcel.setCargoCode(excel.getCargoCode());
		failureExcel.setRemark(excel.getRemark());
		failureExcel.setFailureReason(failureReason);
		return failureExcel;
	}

	private String trimToEmpty(String value) {
		return value == null ? "" : value.trim();
	}

	private String trimToNull(String value) {
		String trimValue = trimToEmpty(value);
		return trimValue.isEmpty() ? null : trimValue;
	}

}
