/**
 * 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.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.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.SysCache;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.transport.excel.CommonAddressExcel;
import org.springblade.transport.mapper.CommonAddressMapper;
import org.springblade.transport.pojo.entity.CommonAddress;
import org.springblade.transport.pojo.vo.CommonAddressRemoveResultVO;
import org.springblade.transport.pojo.vo.CommonAddressVO;
import org.springblade.transport.service.ICommonAddressService;
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.Objects;

/**
 * 常用地址 服务实现类
 *
 * @author Chill
 */
@Service
public class CommonAddressServiceImpl extends BaseServiceImpl<CommonAddressMapper, CommonAddress> implements ICommonAddressService {

	private static final String TYPE_NORMAL = "常规地址";
	private static final String TYPE_PORT = "港口/码头";
	private static final String TYPE_RAILWAY = "铁路车站";
	private static final String TYPE_AIRPORT = "空港机场";
	private static final String NORMAL_SITE_CODE = "/";
	private static final String ADDRESS_CODE_PREFIX = "DZ";
	private static final int ADDRESS_CODE_LENGTH = 5;
	private static final int NAME_MAX_LENGTH = 100;
	private static final int DETAIL_MAX_LENGTH = 255;
	private static final int REGION_CODE_MAX_LENGTH = 32;
	private static final int REGION_MAX_LENGTH = 100;
	private static final int CONTACT_MAX_LENGTH = 50;
	private static final int PHONE_MAX_LENGTH = 30;
	private static final int REMARK_MAX_LENGTH = 200;
	private static final BigDecimal MIN_LONGITUDE = new BigDecimal("-180");
	private static final BigDecimal MAX_LONGITUDE = new BigDecimal("180");
	private static final BigDecimal MIN_LATITUDE = new BigDecimal("-90");
	private static final BigDecimal MAX_LATITUDE = new BigDecimal("90");

	@Override
	public IPage<CommonAddressVO> selectCommonAddressPage(IPage<CommonAddressVO> page, CommonAddressVO address) {
		prepareQuery(address);
		IPage<CommonAddressVO> result = page.setRecords(baseMapper.selectCommonAddressPage(page, address));
		result.getRecords().forEach(this::fillDisplay);
		return result;
	}

	@Override
	public CommonAddressVO detail(Long id) {
		if (Func.isEmpty(id)) {
			throw new ServiceException("主键不能为空");
		}
		CommonAddress address = getById(id);
		if (Func.isEmpty(address) || Objects.equals(address.getIsDeleted(), 1)) {
			throw new ServiceException("常用地址不存在");
		}
		CommonAddressVO addressVO = Objects.requireNonNull(BeanUtil.copyProperties(address, CommonAddressVO.class));
		fillDisplay(addressVO);
		return addressVO;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(CommonAddress address) {
		boolean created = Func.isEmpty(address.getId());
		if (!created) {
			CommonAddress oldAddress = getEditableAddress(address.getId());
			address.setAddressCode(oldAddress.getAddressCode());
			address.setDeptId(oldAddress.getDeptId());
			address.setDeptName(oldAddress.getDeptName());
		}
		prepare(address);
		validate(address);
		if (created) {
			address.setAddressCode(nextAddressCode());
		}
		return saveOrUpdate(address);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public CommonAddressRemoveResultVO removeAddress(String ids) {
		List<Long> idList = Func.toLongList(ids);
		if (Func.isEmpty(idList)) {
			throw new ServiceException("请选择需要删除的数据");
		}
		CommonAddressRemoveResultVO result = new CommonAddressRemoveResultVO();
		List<Long> deleteIdList = new ArrayList<>();
		for (CommonAddress address : listByIds(idList)) {
			validateCurrentDept(address);
			Integer referenceCount = baseMapper.countBusinessReference(address.getId());
			if (referenceCount != null && referenceCount > 0) {
				result.setSkippedCount(result.getSkippedCount() + 1);
				result.getSkippedAddressCodes().add(address.getAddressCode());
				continue;
			}
			deleteIdList.add(address.getId());
		}
		if (Func.isNotEmpty(deleteIdList)) {
			deleteLogic(deleteIdList);
		}
		result.setSuccessCount(deleteIdList.size());
		return result;
	}

	@Override
	public List<CommonAddressVO> sourceOptions(String addressType, String keyword) {
		String type = trimToEmpty(addressType);
		if (TYPE_NORMAL.equals(type)) {
			return new ArrayList<>();
		}
		validateAddressType(type);
		List<CommonAddressVO> sourceList = baseMapper.selectSourceOptions(type, trimToNull(keyword));
		sourceList.forEach(source -> {
			source.setAddressType(type);
			source.setSiteCodeDisplay(source.getSiteCode());
		});
		return sourceList;
	}

	@Override
	public List<CommonAddressExcel> exportCommonAddress(Wrapper<CommonAddress> queryWrapper) {
		return list(queryWrapper).stream().map(address -> {
			CommonAddressExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(address, CommonAddressExcel.class));
			excel.setSiteCodeDisplay(TYPE_NORMAL.equals(address.getAddressType()) ? NORMAL_SITE_CODE : address.getSiteCode());
			return excel;
		}).toList();
	}

	private void prepareQuery(CommonAddressVO address) {
		if (Objects.equals(address.getAllDept(), 1)) {
			if (!AuthUtil.isAdministrator()) {
				throw new ServiceException("无权查看全部组织常用地址");
			}
		} else {
			address.setDeptId(currentDeptId());
		}
		address.setAddressName(trimToNull(address.getAddressName()));
		address.setAddressType(trimToNull(address.getAddressType()));
		address.setDetailAddress(trimToNull(address.getDetailAddress()));
		address.setRegionName(trimToNull(address.getRegionName()));
		address.setSiteCode(trimToNull(address.getSiteCode()));
		address.setContactName(trimToNull(address.getContactName()));
		address.setContactPhone(trimToNull(address.getContactPhone()));
		if (NORMAL_SITE_CODE.equals(address.getSiteCode())) {
			address.setSiteCode(null);
		}
	}

	private void prepare(CommonAddress address) {
		address.setAddressName(trimToEmpty(address.getAddressName()));
		address.setAddressType(trimToEmpty(address.getAddressType()));
		address.setDetailAddress(trimToEmpty(address.getDetailAddress()));
		address.setRegionCode(trimToNull(address.getRegionCode()));
		address.setRegionName(trimToNull(address.getRegionName()));
		address.setContactName(trimToNull(address.getContactName()));
		address.setContactPhone(trimToNull(address.getContactPhone()));
		address.setRemark(trimToNull(address.getRemark()));
		if (address.getStatus() == null) {
			address.setStatus(1);
		}
		if (Func.isEmpty(address.getDeptId())) {
			fillCurrentDept(address);
		}
		if (TYPE_NORMAL.equals(address.getAddressType())) {
			address.setSourceId(null);
			address.setSiteCode(NORMAL_SITE_CODE);
			address.setLongitude(scale(address.getLongitude()));
			address.setLatitude(scale(address.getLatitude()));
			return;
		}
		CommonAddressVO source = loadSource(address.getAddressType(), address.getSourceId());
		address.setSourceId(source.getSourceId());
		address.setSiteCode(source.getSiteCode());
		address.setDetailAddress(source.getDetailAddress());
		address.setRegionCode(source.getRegionCode());
		address.setRegionName(source.getRegionName());
		address.setLongitude(scale(source.getLongitude()));
		address.setLatitude(scale(source.getLatitude()));
		if (Func.isEmpty(address.getAddressName())) {
			address.setAddressName(source.getAddressName());
		}
	}

	private void fillCurrentDept(CommonAddress address) {
		Long deptId = currentDeptId();
		Dept dept = SysCache.getDept(deptId);
		if (Func.isEmpty(dept)) {
			throw new ServiceException("当前用户所属组织异常，请重新登录后再试");
		}
		address.setDeptId(deptId);
		address.setDeptName(dept.getDeptName());
	}

	private CommonAddressVO loadSource(String addressType, Long sourceId) {
		if (Func.isEmpty(sourceId)) {
			throw new ServiceException(addressType + "主数据不能为空");
		}
		CommonAddressVO source;
		if (TYPE_PORT.equals(addressType)) {
			source = baseMapper.selectPortTerminalSource(sourceId);
		} else if (TYPE_RAILWAY.equals(addressType)) {
			source = baseMapper.selectRailwayStationSource(sourceId);
		} else if (TYPE_AIRPORT.equals(addressType)) {
			source = baseMapper.selectAirportMasterSource(sourceId);
		} else {
			throw new ServiceException("地址类型不正确");
		}
		if (Func.isEmpty(source)) {
			throw new ServiceException(addressType + "主数据不存在或已停用");
		}
		return source;
	}

	private void validate(CommonAddress address) {
		validateAddressType(address.getAddressType());
		if (Func.isEmpty(address.getAddressName())) {
			throw new ServiceException("地址名称不能为空");
		}
		if (Func.isEmpty(address.getDetailAddress())) {
			throw new ServiceException("详细地址不能为空");
		}
		if (Func.isEmpty(address.getRegionName())) {
			throw new ServiceException("行政区划不能为空");
		}
		if (Func.isEmpty(address.getLongitude())) {
			throw new ServiceException("经度不能为空");
		}
		if (Func.isEmpty(address.getLatitude())) {
			throw new ServiceException("纬度不能为空");
		}
		validateLength(address.getAddressName(), NAME_MAX_LENGTH, "地址名称不能超过100字");
		validateLength(address.getDetailAddress(), DETAIL_MAX_LENGTH, "详细地址不能超过255字");
		validateLength(address.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编码不能超过32字");
		validateLength(address.getRegionName(), REGION_MAX_LENGTH, "行政区划不能超过100字");
		validateLength(address.getContactName(), CONTACT_MAX_LENGTH, "联系人不能超过50字");
		validateLength(address.getContactPhone(), PHONE_MAX_LENGTH, "联系方式不能超过30字");
		validateLength(address.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200字");
		validateRange(address.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度范围为 -180 到 180");
		validateRange(address.getLatitude(), MIN_LATITUDE, MAX_LATITUDE, "纬度范围为 -90 到 90");
		if (!TYPE_NORMAL.equals(address.getAddressType()) && Func.isEmpty(address.getSiteCode())) {
			throw new ServiceException("站点编码不能为空");
		}
	}

	private void validateAddressType(String addressType) {
		if (!TYPE_NORMAL.equals(addressType) && !TYPE_PORT.equals(addressType)
			&& !TYPE_RAILWAY.equals(addressType) && !TYPE_AIRPORT.equals(addressType)) {
			throw new ServiceException("地址类型不正确");
		}
	}

	private CommonAddress getEditableAddress(Long id) {
		CommonAddress address = getById(id);
		if (Func.isEmpty(address) || Objects.equals(address.getIsDeleted(), 1)) {
			throw new ServiceException("常用地址不存在");
		}
		validateCurrentDept(address);
		return address;
	}

	private void validateCurrentDept(CommonAddress address) {
		if (!Objects.equals(address.getDeptId(), currentDeptId())) {
			throw new ServiceException("无权操作其他组织常用地址");
		}
	}

	private void fillDisplay(CommonAddressVO address) {
		address.setSiteCodeDisplay(TYPE_NORMAL.equals(address.getAddressType()) ? NORMAL_SITE_CODE : address.getSiteCode());
		address.setReadonly(!Objects.equals(address.getDeptId(), currentDeptId()));
		address.setCreateUserName(UserCache.getUserRealName(address.getCreateUser()));
		address.setUpdateUserName(UserCache.getUserRealName(address.getUpdateUser()));
	}

	private Long currentDeptId() {
		Long deptId = Func.firstLong(AuthUtil.getDeptId());
		if (Func.isEmpty(deptId) || deptId <= 0) {
			throw new ServiceException("当前用户所属组织为空，无法查询常用地址");
		}
		return deptId;
	}

	private synchronized String nextAddressCode() {
		String maxAddressCode = baseMapper.selectMaxAddressCode();
		int next = 1;
		if (Func.isNotEmpty(maxAddressCode) && maxAddressCode.length() > ADDRESS_CODE_PREFIX.length()) {
			String number = maxAddressCode.substring(ADDRESS_CODE_PREFIX.length());
			if (number.chars().allMatch(Character::isDigit)) {
				next = Integer.parseInt(number) + 1;
			}
		}
		return ADDRESS_CODE_PREFIX + String.format("%0" + ADDRESS_CODE_LENGTH + "d", next);
	}

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

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

	private void validateRange(BigDecimal value, BigDecimal min, BigDecimal max, String message) {
		if (Func.isNotEmpty(value) && (value.compareTo(min) < 0 || value.compareTo(max) > 0)) {
			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;
	}

}
