/**
 * 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.RailwayStationExcel;
import org.springblade.system.mapper.RailwayStationMapper;
import org.springblade.system.pojo.entity.RailwayStation;
import org.springblade.system.pojo.entity.Region;
import org.springblade.system.pojo.vo.RailwayStationVO;
import org.springblade.system.service.IRailwayStationService;
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 RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMapper, RailwayStation> implements IRailwayStationService {

	private static final String CODE_PREFIX = "TL";
	private static final String SOURCE_INITIAL = "初始化导入";
	private static final String SOURCE_INITIAL_OLD = "初始导入";
	private static final String SOURCE_BATCH = "批量";
	private static final String SOURCE_BATCH_OLD = "批量导入";
	private static final String SOURCE_MANUAL = "手动";
	private static final String SOURCE_MANUAL_RECORD = "手动录入";
	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 = 50;
	private static final int RAILWAY_LINE_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 TMIS_CODE_PATTERN = Pattern.compile("^\\d{5}$");
	private static final Pattern TELEGRAPH_CODE_PATTERN = Pattern.compile("^[A-Z]{3}$");
	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<RailwayStationVO> selectRailwayStationPage(IPage<RailwayStationVO> page, RailwayStationVO railwayStation) {
		return page.setRecords(baseMapper.selectRailwayStationPage(page, railwayStation));
	}

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

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

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

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

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

	private BigDecimal parseCoordinate(String value, String name) {
		String trimValue = trimToEmpty(value);
		if (trimValue.isEmpty()) {
			return null;
		}
		try {
			return new BigDecimal(trimValue);
		} catch (NumberFormatException exception) {
			throw new ServiceException(name + "范围不正确");
		}
	}

	private void prepare(RailwayStation railwayStation, String defaultDataSource) {
		railwayStation.setTmisCode(trimToEmpty(railwayStation.getTmisCode()));
		railwayStation.setCode(CODE_PREFIX + railwayStation.getTmisCode());
		railwayStation.setTelegraphCode(trimToEmpty(railwayStation.getTelegraphCode()).toUpperCase(Locale.ROOT));
		railwayStation.setName(trimToEmpty(railwayStation.getName()));
		railwayStation.setRailwayLine(trimToNull(railwayStation.getRailwayLine()));
		railwayStation.setProvinceCode(trimToNull(railwayStation.getProvinceCode()));
		railwayStation.setProvinceName(trimToNull(railwayStation.getProvinceName()));
		railwayStation.setCityCode(trimToNull(railwayStation.getCityCode()));
		railwayStation.setCityName(trimToNull(railwayStation.getCityName()));
		railwayStation.setDistrictCode(trimToNull(railwayStation.getDistrictCode()));
		railwayStation.setDistrictName(trimToNull(railwayStation.getDistrictName()));
		railwayStation.setRegionCode(trimToNull(railwayStation.getRegionCode()));
		railwayStation.setRegionName(trimToNull(railwayStation.getRegionName()));
		if (Func.isEmpty(railwayStation.getDistrictCode()) && Func.isNotEmpty(railwayStation.getRegionCode())) {
			railwayStation.setDistrictCode(railwayStation.getRegionCode());
		}
		if (Func.isEmpty(railwayStation.getDistrictName()) && Func.isNotEmpty(railwayStation.getRegionName())) {
			railwayStation.setDistrictName(railwayStation.getRegionName());
		}
		railwayStation.setDetailAddress(trimToNull(railwayStation.getDetailAddress()));
		railwayStation.setRemark(trimToNull(railwayStation.getRemark()));
		railwayStation.setDataSource(normalizeDataSource(Func.toStrWithEmpty(railwayStation.getDataSource(), defaultDataSource)));
		if (Func.isEmpty(railwayStation.getStatus())) {
			railwayStation.setStatus(STATUS_ENABLED);
		}
		fillRegion(railwayStation);
	}

	private void validate(RailwayStation railwayStation) {
		if (Func.isEmpty(railwayStation.getTmisCode())) {
			throw new ServiceException("TMIS国标编码不能为空");
		}
		if (!TMIS_CODE_PATTERN.matcher(railwayStation.getTmisCode()).matches()) {
			throw new ServiceException("TMIS国标编码为5位数字");
		}
		if (Func.isEmpty(railwayStation.getTelegraphCode())) {
			throw new ServiceException("电报码格式不正确或已存在");
		}
		if (!TELEGRAPH_CODE_PATTERN.matcher(railwayStation.getTelegraphCode()).matches()) {
			throw new ServiceException("电报码格式不正确或已存在");
		}
		if (Func.isEmpty(railwayStation.getName())) {
			throw new ServiceException("请输入车站名称");
		}
		validateLength(railwayStation.getCode(), CODE_MAX_LENGTH, "编码不能超过20字");
		validateLength(railwayStation.getName(), NAME_MAX_LENGTH, "请输入车站名称");
//		validateLength(railwayStation.getRailwayLine(), RAILWAY_LINE_MAX_LENGTH, "所属铁路线路不能超过100字");
		validateLength(railwayStation.getProvinceName(), REGION_NAME_MAX_LENGTH, "所属省份不能超过128字");
		validateLength(railwayStation.getCityName(), REGION_NAME_MAX_LENGTH, "所属城市不能超过128字");
		validateLength(railwayStation.getDistrictName(), REGION_NAME_MAX_LENGTH, "所属区县不能超过128字");
		validateLength(railwayStation.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编码不能超过32字");
		validateLength(railwayStation.getRegionName(), REGION_NAME_MAX_LENGTH, "行政区划不能超过128字");
		validateLength(railwayStation.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字");
		validateLength(railwayStation.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200字");
		if (Func.isEmpty(railwayStation.getProvinceCode())) {
			throw new ServiceException("请选择所属省份");
		}
		if (Func.isEmpty(railwayStation.getCityCode())) {
			throw new ServiceException("请选择所属城市");
		}
		if (Func.isEmpty(railwayStation.getDistrictCode())) {
			throw new ServiceException("请选择所属区县");
		}
		validateCoordinate(railwayStation.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度");
		validateCoordinate(railwayStation.getLatitude(), MIN_LATITUDE, MAX_LATITUDE, "纬度");
		validateDataSource(railwayStation.getDataSource());
		validateStatus(railwayStation.getStatus());
		validateUnique(railwayStation, RailwayStation::getTmisCode, railwayStation.getTmisCode(), "该TMIS编码已存在");
		validateUnique(railwayStation, RailwayStation::getTelegraphCode, railwayStation.getTelegraphCode(), "电报码格式不正确或已存在");
		validateUnique(railwayStation, RailwayStation::getCode, railwayStation.getCode(), "该编码已存在");
	}

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

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

		Region district = null;
		if (Func.isNotEmpty(railwayStation.getDistrictCode())) {
			district = regionService.getById(railwayStation.getDistrictCode());
		}
		if (Func.isEmpty(district) && Func.isNotEmpty(railwayStation.getDistrictName())) {
			district = regionService.getOne(Wrappers.<Region>lambdaQuery()
				.eq(Region::getParentCode, city.getCode())
				.eq(Region::getName, railwayStation.getDistrictName()), false);
		}
		if (Func.isEmpty(district)) {
			throw new ServiceException("请选择所属区县");
		}
		if (!Objects.equals(district.getParentCode(), city.getCode())) {
			throw new ServiceException("所属区县与所属城市不匹配");
		}
		railwayStation.setDistrictCode(district.getCode());
		railwayStation.setDistrictName(district.getName());
		railwayStation.setRegionCode(district.getCode());
		railwayStation.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 + "范围不正确");
		}
	}

	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(RailwayStation railwayStation, com.baomidou.mybatisplus.core.toolkit.support.SFunction<RailwayStation, ?> column, String value, String message) {
		LambdaQueryWrapper<RailwayStation> queryWrapper = Wrappers.<RailwayStation>lambdaQuery()
			.eq(column, value)
			.eq(RailwayStation::getIsDeleted, 0);
		if (Func.isNotEmpty(railwayStation.getId())) {
			queryWrapper.ne(RailwayStation::getId, railwayStation.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);
		if (SOURCE_INITIAL_OLD.equals(value)) {
			return SOURCE_INITIAL;
		}
		if (SOURCE_BATCH_OLD.equals(value)) {
			return SOURCE_BATCH;
		}
		if (SOURCE_MANUAL_OLD.equals(value) || SOURCE_MANUAL_RECORD.equals(value)) {
			return SOURCE_MANUAL;
		}
		return value;
	}

}
