/**
 * 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 lombok.AllArgsConstructor;
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.excel.AirportMasterExcel;
import org.springblade.system.mapper.AirportMasterMapper;
import org.springblade.system.pojo.entity.AirportMaster;
import org.springblade.system.pojo.entity.Region;
import org.springblade.system.pojo.vo.AirportMasterVO;
import org.springblade.system.service.IAirportMasterService;
import org.springblade.system.service.IRegionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * 空港机场主数据 服务实现类
 *
 * @author Chill
 */
@Service
@AllArgsConstructor
public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMapper, AirportMaster> implements IAirportMasterService {

	private static final String CODE_PREFIX = "JC-";
	private static final String SOURCE_INITIAL = "初始导入";
	private static final String SOURCE_BATCH = "批量导入";
	private static final String SOURCE_MANUAL = "手动录入";
	private static final String SOURCE_MANUAL_OLD = "手工导入";
	private static final String DEFAULT_COUNTRY_CODE = "+86";
	private static final int STATUS_ENABLED = 1;
	private static final int STATUS_DISABLED = 2;
	private static final int CODE_MAX_LENGTH = 20;
	private static final int NAME_MAX_LENGTH = 100;
	private static final int SHORT_NAME_MAX_LENGTH = 100;
	private static final int REGION_NAME_MAX_LENGTH = 128;
	private static final int REGION_CODE_MAX_LENGTH = 32;
	private static final int DETAIL_ADDRESS_MAX_LENGTH = 255;
	private static final int REMARK_MAX_LENGTH = 200;
	private static final Pattern IATA_CODE_PATTERN = Pattern.compile("^[A-Z]{3}$");
	private static final Pattern ICAO_CODE_PATTERN = Pattern.compile("^[A-Z]{4}$");
	private static final BigDecimal MIN_LONGITUDE = BigDecimal.valueOf(-180);
	private static final BigDecimal MAX_LONGITUDE = BigDecimal.valueOf(180);
	private static final BigDecimal MIN_LATITUDE = BigDecimal.valueOf(-90);
	private static final BigDecimal MAX_LATITUDE = BigDecimal.valueOf(90);

	private final IRegionService regionService;

	@Override
	public IPage<AirportMasterVO> selectAirportMasterPage(IPage<AirportMasterVO> page, AirportMasterVO airportMaster) {
		return page.setRecords(baseMapper.selectAirportMasterPage(page, airportMaster));
	}

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

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean changeStatus(Long id, Integer status) {
		if (Func.isEmpty(id)) {
			throw new ServiceException("主键不能为空");
		}
		AirportMaster airportMaster = getById(id);
		if (Func.isEmpty(airportMaster)) {
			throw new ServiceException("空港机场不存在");
		}
		if (!Objects.equals(status, STATUS_ENABLED) && !Objects.equals(status, STATUS_DISABLED)) {
			throw new ServiceException("启停状态不正确");
		}
		AirportMaster update = new AirportMaster();
		update.setId(id);
		update.setStatus(status);
		return updateById(update);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public List<AirportMasterExcel> importAirportMaster(List<AirportMasterExcel> data) {
		if (Func.isEmpty(data)) {
			throw new ServiceException("导入数据不能为空");
		}
		List<AirportMasterExcel> errorList = new ArrayList<>();
		for (int index = 0; index < data.size(); index++) {
			AirportMasterExcel excel = data.get(index);
			try {
				AirportMaster airportMaster = Objects.requireNonNull(BeanUtil.copyProperties(excel, AirportMaster.class));
				airportMaster.setDataSource(SOURCE_BATCH);
				airportMaster.setStatus(STATUS_ENABLED);
				prepare(airportMaster, SOURCE_BATCH);
				validate(airportMaster);
				save(airportMaster);
			} catch (Exception exception) {
				String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
				excel.setErrorMessage("第" + (index + 2) + "行：" + message);
				errorList.add(excel);
			}
		}
		return errorList;
	}

	@Override
	public List<AirportMasterExcel> exportAirportMaster(Wrapper<AirportMaster> queryWrapper) {
		List<AirportMaster> airportMasterList = list(queryWrapper);
		return airportMasterList.stream().map(airportMaster -> {
			AirportMasterExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(airportMaster, AirportMasterExcel.class));
			excel.setLongitude(scaleCoordinate(airportMaster.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE));
			excel.setLatitude(scaleCoordinate(airportMaster.getLatitude(), MIN_LATITUDE, MAX_LATITUDE));
			excel.setDataSource(normalizeDataSource(airportMaster.getDataSource()));
			excel.setStatusName(Objects.equals(airportMaster.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
			return excel;
		}).toList();
	}

	private BigDecimal scaleCoordinate(BigDecimal value, BigDecimal min, BigDecimal max) {
		if (Func.isEmpty(value)) {
			return null;
		}
		return validRange(value, min, max) ? value.setScale(6, RoundingMode.HALF_UP) : null;
	}

	private void prepare(AirportMaster airportMaster, String defaultDataSource) {
		airportMaster.setIataCode(trimToEmpty(airportMaster.getIataCode()).toUpperCase(Locale.ROOT));
		airportMaster.setCode(CODE_PREFIX + airportMaster.getIataCode());
		airportMaster.setIcaoCode(trimToEmpty(airportMaster.getIcaoCode()).toUpperCase(Locale.ROOT));
		airportMaster.setName(trimToEmpty(airportMaster.getName()));
		airportMaster.setShortName(trimToNull(airportMaster.getShortName()));
		airportMaster.setProvinceCode(trimToNull(airportMaster.getProvinceCode()));
		airportMaster.setProvinceName(trimToNull(airportMaster.getProvinceName()));
		airportMaster.setCityCode(trimToNull(airportMaster.getCityCode()));
		airportMaster.setCityName(trimToNull(airportMaster.getCityName()));
		airportMaster.setDistrictCode(trimToNull(airportMaster.getDistrictCode()));
		airportMaster.setDistrictName(trimToNull(airportMaster.getDistrictName()));
		airportMaster.setRegionCode(trimToNull(airportMaster.getRegionCode()));
		airportMaster.setRegionName(trimToNull(airportMaster.getRegionName()));
		if (Func.isEmpty(airportMaster.getDistrictCode()) && Func.isNotEmpty(airportMaster.getRegionCode())) {
			airportMaster.setDistrictCode(airportMaster.getRegionCode());
		}
		if (Func.isEmpty(airportMaster.getDistrictName()) && Func.isNotEmpty(airportMaster.getRegionName())) {
			airportMaster.setDistrictName(airportMaster.getRegionName());
		}
		airportMaster.setDetailAddress(trimToNull(airportMaster.getDetailAddress()));
		airportMaster.setRemark(trimToNull(airportMaster.getRemark()));
		airportMaster.setDataSource(normalizeDataSource(Func.toStrWithEmpty(airportMaster.getDataSource(), defaultDataSource)));
		if (Func.isEmpty(airportMaster.getStatus())) {
			airportMaster.setStatus(STATUS_ENABLED);
		}
		fillRegion(airportMaster);
	}

	private void validate(AirportMaster airportMaster) {
		if (Func.isEmpty(airportMaster.getIataCode())) {
			throw new ServiceException("IATA编码不能为空");
		}
		if (!IATA_CODE_PATTERN.matcher(airportMaster.getIataCode()).matches()) {
			throw new ServiceException("IATA编码为3位大写字母");
		}
		if (Func.isEmpty(airportMaster.getIcaoCode())) {
			throw new ServiceException("ICAO代码不能为空");
		}
		if (!ICAO_CODE_PATTERN.matcher(airportMaster.getIcaoCode()).matches()) {
			throw new ServiceException("ICAO代码为4位大写字母");
		}
		if (Func.isEmpty(airportMaster.getName())) {
			throw new ServiceException("机场标准名称不能为空");
		}
		validateLength(airportMaster.getCode(), CODE_MAX_LENGTH, "编码不能超过20字");
		validateLength(airportMaster.getName(), NAME_MAX_LENGTH, "机场标准名称不能超过100字");
		validateLength(airportMaster.getShortName(), SHORT_NAME_MAX_LENGTH, "机场简称不能超过100字");
		validateLength(airportMaster.getProvinceName(), REGION_NAME_MAX_LENGTH, "所属省份不能超过128字");
		validateLength(airportMaster.getCityName(), REGION_NAME_MAX_LENGTH, "所属城市不能超过128字");
		validateLength(airportMaster.getDistrictName(), REGION_NAME_MAX_LENGTH, "所属区县不能超过128字");
		validateLength(airportMaster.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编码不能超过32字");
		validateLength(airportMaster.getRegionName(), REGION_NAME_MAX_LENGTH, "行政区划不能超过128字");
		validateLength(airportMaster.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字");
		validateLength(airportMaster.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200字");
		if (Func.isEmpty(airportMaster.getProvinceCode()) || Func.isEmpty(airportMaster.getCityCode()) || Func.isEmpty(airportMaster.getDistrictCode())) {
			throw new ServiceException("请选择省份、城市和区县");
		}
		validateCoordinate(airportMaster.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度");
		validateCoordinate(airportMaster.getLatitude(), MIN_LATITUDE, MAX_LATITUDE, "纬度");
		validateDataSource(airportMaster.getDataSource());
		validateStatus(airportMaster.getStatus());
		validateUnique(airportMaster, AirportMaster::getIataCode, airportMaster.getIataCode(), "该IATA编码已存在");
		validateUnique(airportMaster, AirportMaster::getIcaoCode, airportMaster.getIcaoCode(), "该ICAO代码已存在");
		validateUnique(airportMaster, AirportMaster::getCode, airportMaster.getCode(), "该编码已存在");
	}

	private void fillRegion(AirportMaster airportMaster) {
		Region province = null;
		if (Func.isNotEmpty(airportMaster.getProvinceCode())) {
			province = regionService.getById(airportMaster.getProvinceCode());
		}
		if (Func.isEmpty(province) && Func.isNotEmpty(airportMaster.getProvinceName())) {
			province = regionService.getOne(Wrappers.<Region>lambdaQuery()
				.eq(Region::getParentCode, DEFAULT_COUNTRY_CODE)
				.eq(Region::getName, airportMaster.getProvinceName()), false);
		}
		if (Func.isEmpty(province)) {
			throw new ServiceException("请选择省份");
		}

		Region city = null;
		if (Func.isNotEmpty(airportMaster.getCityCode())) {
			city = regionService.getById(airportMaster.getCityCode());
		}
		if (Func.isEmpty(city) && Func.isNotEmpty(airportMaster.getCityName())) {
			city = regionService.getOne(Wrappers.<Region>lambdaQuery()
				.eq(Region::getParentCode, province.getCode())
				.eq(Region::getName, airportMaster.getCityName()), false);
		}
		if (Func.isEmpty(city)) {
			throw new ServiceException("请选择城市");
		}
		if (!Objects.equals(city.getParentCode(), province.getCode())) {
			throw new ServiceException("所属城市与所属省份不匹配");
		}
		airportMaster.setProvinceCode(province.getCode());
		airportMaster.setProvinceName(province.getName());
		airportMaster.setCityCode(city.getCode());
		airportMaster.setCityName(city.getName());

		Region district = null;
		if (Func.isNotEmpty(airportMaster.getDistrictCode())) {
			district = regionService.getById(airportMaster.getDistrictCode());
		}
		if (Func.isEmpty(district) && Func.isNotEmpty(airportMaster.getDistrictName())) {
			district = regionService.getOne(Wrappers.<Region>lambdaQuery()
				.eq(Region::getParentCode, city.getCode())
				.eq(Region::getName, airportMaster.getDistrictName()), false);
		}
		if (Func.isEmpty(district)) {
			throw new ServiceException("请选择区县");
		}
		if (!Objects.equals(district.getParentCode(), city.getCode())) {
			throw new ServiceException("所属区县与所属城市不匹配");
		}
		airportMaster.setDistrictCode(district.getCode());
		airportMaster.setDistrictName(district.getName());
		airportMaster.setRegionCode(district.getCode());
		airportMaster.setRegionName(district.getName());
	}

	private void validateCoordinate(BigDecimal value, BigDecimal min, BigDecimal max, String name) {
		if (value == null) {
			return;
		}
		if (value.compareTo(min) < 0 || value.compareTo(max) > 0) {
			throw new ServiceException(name + ("经度".equals(name) ? "范围为 -180 到 180" : "范围为 -90 到 90"));
		}
	}

	private boolean validRange(BigDecimal value, BigDecimal min, BigDecimal max) {
		return Func.isEmpty(value) || (value.compareTo(min) >= 0 && value.compareTo(max) <= 0);
	}

	private void validateDataSource(String dataSource) {
		if (!SOURCE_INITIAL.equals(dataSource) && !SOURCE_BATCH.equals(dataSource) && !SOURCE_MANUAL.equals(dataSource)) {
			throw new ServiceException("数据来源不正确");
		}
	}

	private void validateStatus(Integer status) {
		if (!Objects.equals(status, STATUS_ENABLED) && !Objects.equals(status, STATUS_DISABLED)) {
			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 validateUnique(AirportMaster airportMaster, com.baomidou.mybatisplus.core.toolkit.support.SFunction<AirportMaster, ?> column, String value, String message) {
		LambdaQueryWrapper<AirportMaster> queryWrapper = Wrappers.<AirportMaster>lambdaQuery()
			.eq(column, value)
			.eq(AirportMaster::getIsDeleted, 0);
		if (Func.isNotEmpty(airportMaster.getId())) {
			queryWrapper.ne(AirportMaster::getId, airportMaster.getId());
		}
		if (count(queryWrapper) > 0L) {
			throw new ServiceException(message);
		}
	}

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

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

	private String normalizeDataSource(String dataSource) {
		String value = trimToEmpty(dataSource);
		return SOURCE_MANUAL_OLD.equals(value) ? SOURCE_MANUAL : value;
	}

}
