/**
 * 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.transport.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
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.transport.excel.ViolationRecordExcel;
import org.springblade.transport.excel.ViolationRecordImportExcel;
import org.springblade.transport.mapper.ViolationRecordMapper;
import org.springblade.transport.pojo.entity.ViolationRecord;
import org.springblade.transport.pojo.vo.ViolationRecordVO;
import org.springblade.transport.service.IViolationRecordService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 违章记录 服务实现类
 *
 * @author Chill
 */
@Service
public class ViolationRecordServiceImpl extends BaseServiceImpl<ViolationRecordMapper, ViolationRecord> implements IViolationRecordService {

	private static final int VEHICLE_NO_MAX_LENGTH = 30;
	private static final int DRIVER_NAME_MAX_LENGTH = 20;
	private static final int TYPE_MAX_LENGTH = 50;
	private static final int ITEM_MAX_LENGTH = 100;
	private static final int LOCATION_MAX_LENGTH = 100;
	private static final int PENALTY_UNIT_MAX_LENGTH = 50;
	private static final int DESCRIPTION_MAX_LENGTH = 500;
	private static final int RESULT_MAX_LENGTH = 500;
	private static final int ATTACHMENTS_MAX_LENGTH = 1000;
	private static final int MAX_DEDUCT_POINTS = 15;
	private static final String PROCESSED = "已处理";
	private static final String UNPROCESSED = "未处理";

	@Override
	public IPage<ViolationRecordVO> selectViolationRecordPage(IPage<ViolationRecordVO> page, ViolationRecordVO violationRecord) {
		List<ViolationRecordVO> records = baseMapper.selectViolationRecordPage(page, violationRecord);
		records.forEach(record -> record.setUpdateUserName(UserCache.getUserRealName(record.getUpdateUser())));
		return page.setRecords(records);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(ViolationRecord violationRecord) {
		prepare(violationRecord);
		validate(violationRecord);
		validateVehicleTypeImmutable(violationRecord);
		return saveOrUpdate(violationRecord);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public List<ViolationRecordImportExcel> importViolationRecord(List<ViolationRecordImportExcel> data) {
		if (Func.isEmpty(data)) {
			throw new ServiceException("导入数据不能为空");
		}
		List<ViolationRecordImportExcel> errorList = new ArrayList<>();
		for (int index = 0; index < data.size(); index++) {
			ViolationRecordImportExcel excel = data.get(index);
			try {
				ViolationRecord violationRecord = Objects.requireNonNull(BeanUtil.copyProperties(excel, ViolationRecord.class));
				if ("船舶".equals(trimToEmpty(excel.getVehicleType()))) {
					violationRecord.setViolationItem(excel.getViolationTypeOrItem());
				} else {
					violationRecord.setViolationType(excel.getViolationTypeOrItem());
				}
				submit(violationRecord);
			} catch (Exception exception) {
				excel.setErrorMessage("第" + (index + 2) + "行：" + exception.getMessage());
				errorList.add(excel);
			}
		}
		return errorList;
	}

	@Override
	public List<ViolationRecordExcel> exportViolationRecord(Wrapper<ViolationRecord> queryWrapper) {
		return list(queryWrapper).stream().map(violationRecord -> {
			ViolationRecordExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(violationRecord, ViolationRecordExcel.class));
			excel.setFineAmount(nonNegative(violationRecord.getFineAmount()));
			excel.setDeductPoints(validDeductPoints(violationRecord.getDeductPoints()));
			excel.setUpdateUserName(UserCache.getUserRealName(violationRecord.getUpdateUser()));
			return excel;
		}).toList();
	}

	private void prepare(ViolationRecord violationRecord) {
		violationRecord.setVehicleType(normalizeVehicleType(violationRecord.getVehicleType()));
		violationRecord.setVehicleNo(trimToEmpty(violationRecord.getVehicleNo()).toUpperCase());
		violationRecord.setDriverName(trimToEmpty(violationRecord.getDriverName()));
		violationRecord.setViolationType(trimToNull(violationRecord.getViolationType()));
		violationRecord.setViolationItem(trimToNull(violationRecord.getViolationItem()));
		violationRecord.setLocation(trimToEmpty(violationRecord.getLocation()));
		violationRecord.setPenaltyUnit(trimToNull(violationRecord.getPenaltyUnit()));
		violationRecord.setProcessStatus(normalizeProcessStatus(violationRecord.getProcessStatus()));
		violationRecord.setProcessDescription(trimToEmpty(violationRecord.getProcessDescription()));
		violationRecord.setProcessResult(trimToNull(violationRecord.getProcessResult()));
		violationRecord.setAttachments(trimToNull(violationRecord.getAttachments()));
		if ("车辆".equals(violationRecord.getVehicleType())) {
			violationRecord.setViolationItem(null);
		} else {
			violationRecord.setViolationType(null);
		}
		if (UNPROCESSED.equals(violationRecord.getProcessStatus())) {
			violationRecord.setProcessResult(null);
		}
	}

	private void validate(ViolationRecord violationRecord) {
		if (Func.isEmpty(violationRecord.getVehicleType())) {
			throw new ServiceException("车船类型不能为空");
		}
		if (!"车辆".equals(violationRecord.getVehicleType()) && !"船舶".equals(violationRecord.getVehicleType())) {
			throw new ServiceException("车船类型不正确");
		}
		if (Func.isEmpty(violationRecord.getVehicleNo())) {
			throw new ServiceException("车牌号/船号不能为空");
		}
		if (Func.isEmpty(violationRecord.getDriverName())) {
			throw new ServiceException("驾驶人/船长不能为空");
		}
		if ("车辆".equals(violationRecord.getVehicleType()) && Func.isEmpty(violationRecord.getViolationType())) {
			throw new ServiceException("类型不能为空");
		}
		if ("船舶".equals(violationRecord.getVehicleType()) && Func.isEmpty(violationRecord.getViolationItem())) {
			throw new ServiceException("事项不能为空");
		}
		if (Func.isEmpty(violationRecord.getViolationTime())) {
			throw new ServiceException("时间不能为空");
		}
		if (violationRecord.getViolationTime().isAfter(LocalDateTime.now())) {
			throw new ServiceException("时间不能超过当前时间");
		}
		if (Func.isEmpty(violationRecord.getLocation())) {
			throw new ServiceException("地址不能为空");
		}
		if (Func.isEmpty(violationRecord.getProcessStatus())) {
			throw new ServiceException("状态不能为空");
		}
		if (!PROCESSED.equals(violationRecord.getProcessStatus()) && !UNPROCESSED.equals(violationRecord.getProcessStatus())) {
			throw new ServiceException("状态值不正确");
		}
		if (Func.isEmpty(violationRecord.getProcessDescription())) {
			throw new ServiceException("过程描述不能为空");
		}
		validateNonNegative(violationRecord.getFineAmount(), "被罚金额不能小于0");
		validateDeductPoints(violationRecord.getDeductPoints());
		validateLength(violationRecord.getVehicleNo(), VEHICLE_NO_MAX_LENGTH, "车牌号/船号不能超过30字");
		validateLength(violationRecord.getDriverName(), DRIVER_NAME_MAX_LENGTH, "驾驶人/船长不能超过20字");
		validateLength(violationRecord.getViolationType(), TYPE_MAX_LENGTH, "类型不能超过50字");
		validateLength(violationRecord.getViolationItem(), ITEM_MAX_LENGTH, "事项不能超过100字");
		validateLength(violationRecord.getLocation(), LOCATION_MAX_LENGTH, "地址不能超过100字");
		validateLength(violationRecord.getPenaltyUnit(), PENALTY_UNIT_MAX_LENGTH, "被罚单位不能超过50字");
		validateLength(violationRecord.getProcessDescription(), DESCRIPTION_MAX_LENGTH, "过程描述不能超过500字");
		validateLength(violationRecord.getProcessResult(), RESULT_MAX_LENGTH, "处理结果不能超过500字");
		validateLength(violationRecord.getAttachments(), ATTACHMENTS_MAX_LENGTH, "附件不能超过1000字");
	}

	private void validateVehicleTypeImmutable(ViolationRecord violationRecord) {
		if (Func.isEmpty(violationRecord.getId())) {
			return;
		}
		ViolationRecord oldRecord = getById(violationRecord.getId());
		if (oldRecord != null && Func.isNotEmpty(oldRecord.getVehicleType()) && !oldRecord.getVehicleType().equals(violationRecord.getVehicleType())) {
			throw new ServiceException("车船类型保存后不可修改");
		}
	}

	private void validateLength(String value, int maxLength, String message) {
		if (Func.isNotEmpty(value) && value.length() > maxLength) {
			throw new ServiceException(message);
		}
	}

	private void validateNonNegative(BigDecimal value, String message) {
		if (Func.isNotEmpty(value) && value.compareTo(BigDecimal.ZERO) < 0) {
			throw new ServiceException(message);
		}
	}

	private void validateDeductPoints(Integer value) {
		if (Func.isNotEmpty(value) && (value < 0 || value > MAX_DEDUCT_POINTS)) {
			throw new ServiceException("被扣分数范围为0-15分");
		}
	}

	private BigDecimal nonNegative(BigDecimal value) {
		if (Func.isEmpty(value) || value.compareTo(BigDecimal.ZERO) >= 0) {
			return value;
		}
		return BigDecimal.ZERO;
	}

	private Integer validDeductPoints(Integer value) {
		if (Func.isEmpty(value)) {
			return value;
		}
		if (value < 0) {
			return 0;
		}
		return Math.min(value, MAX_DEDUCT_POINTS);
	}

	private String normalizeVehicleType(String vehicleType) {
		String value = trimToEmpty(vehicleType);
		return value.isEmpty() ? "车辆" : value;
	}

	private String normalizeProcessStatus(String processStatus) {
		String value = trimToEmpty(processStatus);
		return value.isEmpty() ? UNPROCESSED : value;
	}

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

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

}
