package org.springblade.file.config;

import com.aliyun.oss.OSSClientBuilder;
import com.obs.services.ObsClient;
import io.minio.MinioClient;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.file.service.IFileService;
import org.springblade.file.service.impl.LocalFileServiceImpl;
import org.springblade.file.service.impl.MinioFileServiceImpl;
import org.springblade.file.service.impl.OssFileServiceImpl;
import org.springblade.file.service.impl.ObsFileServiceImpl;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 *  文件配置类
 * @author bfhuange
 * @date 2024/10/21
 */
@Configuration
public class FileConfig {

	private static final String STORAGE_AUTO = "auto";
	private static final String STORAGE_OBS = "obs";
	private static final String STORAGE_MINIO = "minio";
	private static final String STORAGE_OSS = "oss";
	private static final String STORAGE_LOCAL = "local";

	@Bean
	@ConfigurationProperties(prefix = "obs")
	public ObsProperties obsProperties() {
		return new ObsProperties();
	}

	@Bean
	@ConfigurationProperties(prefix = "file.storage")
	public FileStorageProperties fileStorageProperties() {
		return new FileStorageProperties();
	}

	@Bean
	public IFileService fileService(ObsProperties obsProperties, FileStorageProperties fileStorageProperties, Environment environment) {
		FileStorageProperties.StoreProperties minioProperties = mergeStoreProperties(fileStorageProperties.getMinio(), environment, STORAGE_MINIO);
		FileStorageProperties.StoreProperties ossProperties = mergeStoreProperties(fileStorageProperties.getOss(), environment, STORAGE_OSS);
		String storageType = resolveStorageType(fileStorageProperties.getType(), obsProperties, minioProperties, ossProperties);
		return switch (storageType) {
			case STORAGE_OBS -> buildObsFileService(obsProperties);
			case STORAGE_MINIO -> buildMinioFileService(minioProperties);
			case STORAGE_OSS -> buildOssFileService(ossProperties);
			case STORAGE_LOCAL -> new LocalFileServiceImpl(fileStorageProperties.getLocal());
			default -> throw new ServiceException("文件存储类型配置错误");
		};
	}

	private IFileService buildObsFileService(ObsProperties properties) {
		validateObsProperties(properties);
		ObsClient obsClient = new ObsClient(properties.getAccessKeyId(), properties.getAccessKeySecret(), properties.getEndpoint());
		return new ObsFileServiceImpl(properties, obsClient);
	}

	private IFileService buildMinioFileService(FileStorageProperties.StoreProperties properties) {
		validateStoreProperties(STORAGE_MINIO, properties);
		MinioClient minioClient = MinioClient.builder()
			.endpoint(properties.getEndpoint())
			.credentials(properties.getAccessKeyId(), properties.getAccessKeySecret())
			.build();
		return new MinioFileServiceImpl(properties, minioClient);
	}

	private IFileService buildOssFileService(FileStorageProperties.StoreProperties properties) {
		validateStoreProperties(STORAGE_OSS, properties);
		return new OssFileServiceImpl(properties, new OSSClientBuilder().build(properties.getEndpoint(), properties.getAccessKeyId(), properties.getAccessKeySecret()));
	}

	private String resolveStorageType(String type, ObsProperties obsProperties, FileStorageProperties.StoreProperties minioProperties, FileStorageProperties.StoreProperties ossProperties) {
		if (StringUtil.isNotBlank(type) && !STORAGE_AUTO.equalsIgnoreCase(type)) {
			return type.toLowerCase();
		}
		if (hasObsProperties(obsProperties)) {
			return STORAGE_OBS;
		}
		if (hasStoreProperties(minioProperties)) {
			return STORAGE_MINIO;
		}
		if (hasStoreProperties(ossProperties)) {
			return STORAGE_OSS;
		}
		return STORAGE_LOCAL;
	}

	private FileStorageProperties.StoreProperties mergeStoreProperties(FileStorageProperties.StoreProperties properties, Environment environment, String prefix) {
		FileStorageProperties.StoreProperties mergedProperties = new FileStorageProperties.StoreProperties();
		String fileStoragePrefix = "file.storage." + prefix;
		mergedProperties.setEndpoint(firstNotBlank(properties.getEndpoint(), environment.getProperty(fileStoragePrefix + ".endpoint"), environment.getProperty(prefix + ".endpoint")));
		mergedProperties.setTransformEndpoint(firstNotBlank(properties.getTransformEndpoint(), environment.getProperty(fileStoragePrefix + ".transform-endpoint"), environment.getProperty(prefix + ".transform-endpoint")));
		mergedProperties.setAccessKeyId(firstNotBlank(properties.getAccessKeyId(), environment.getProperty(fileStoragePrefix + ".access-key-id"), environment.getProperty(fileStoragePrefix + ".access-key"), environment.getProperty(prefix + ".access-key-id"), environment.getProperty(prefix + ".access-key")));
		mergedProperties.setAccessKeySecret(firstNotBlank(properties.getAccessKeySecret(), environment.getProperty(fileStoragePrefix + ".access-key-secret"), environment.getProperty(fileStoragePrefix + ".secret-key"), environment.getProperty(prefix + ".access-key-secret"), environment.getProperty(prefix + ".secret-key")));
		mergedProperties.setBucketName(firstNotBlank(properties.getBucketName(), environment.getProperty(fileStoragePrefix + ".bucket-name"), environment.getProperty(prefix + ".bucket-name")));
		mergedProperties.setRootDirectory(firstNotBlank(properties.getRootDirectory(), environment.getProperty(fileStoragePrefix + ".root-directory"), environment.getProperty(prefix + ".root-directory")));
		mergedProperties.setExpires(properties.getExpires());
		return mergedProperties;
	}

	private String firstNotBlank(String... values) {
		for (String value : values) {
			if (StringUtil.isNotBlank(value)) {
				return value;
			}
		}
		return null;
	}

	private boolean hasObsProperties(ObsProperties properties) {
		return StringUtil.isNotBlank(properties.getEndpoint())
			&& StringUtil.isNotBlank(properties.getAccessKeyId())
			&& StringUtil.isNotBlank(properties.getAccessKeySecret())
			&& StringUtil.isNotBlank(properties.getBucketName());
	}

	private boolean hasStoreProperties(FileStorageProperties.StoreProperties properties) {
		return StringUtil.isNotBlank(properties.getEndpoint())
			&& StringUtil.isNotBlank(properties.getAccessKeyId())
			&& StringUtil.isNotBlank(properties.getAccessKeySecret())
			&& StringUtil.isNotBlank(properties.getBucketName());
	}

	private void validateObsProperties(ObsProperties properties) {
		if (!hasObsProperties(properties)) {
			throw new ServiceException("OBS文件存储配置不完整");
		}
	}

	private void validateStoreProperties(String storageType, FileStorageProperties.StoreProperties properties) {
		if (!hasStoreProperties(properties)) {
			throw new ServiceException(storageType + "文件存储配置不完整");
		}
	}
}
