package org.springblade.modules.resource.template;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import org.springblade.core.oss.OssTemplate;
import org.springblade.core.oss.enums.PolicyType;
import org.springblade.core.oss.model.BladeFile;
import org.springblade.core.oss.model.OssFile;
import org.springblade.core.oss.props.OssProperties;
import org.springblade.core.oss.rule.OssRule;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class BladeS3Template implements OssTemplate {

	private final AmazonS3 client;
	private final OssRule ossRule;
	private final OssProperties ossProperties;

    public BladeS3Template(AmazonS3 client, OssRule ossRule, OssProperties ossProperties) {
        this.client = client;
        this.ossRule = ossRule;
        this.ossProperties = ossProperties;
    }

    public void makeBucket(String bucketName) {
		if (!this.client.doesBucketExistV2(this.getBucketName(bucketName))) {
			this.client.createBucket(this.getBucketName(bucketName));
			this.client.setBucketPolicy(new SetBucketPolicyRequest(this.getBucketName(bucketName), getPolicyType(this.getBucketName(bucketName), PolicyType.READ)));
		}

	}

	public Bucket getBucket() {
		try {
			return this.getBucket(this.getBucketName());
		} catch (Throwable var2) {
			throw var2;
		}
	}

	public Bucket getBucket(String bucketName) {
		try {
			Optional<Bucket> bucketOptional = this.client.listBuckets().stream().filter((bucket) -> {
				return bucket.getName().equals(this.getBucketName(bucketName));
			}).findFirst();
			return (Bucket)bucketOptional.orElse((Bucket) null);
		} catch (Throwable var3) {
			throw var3;
		}
	}

	public List<Bucket> listBuckets() {
		try {
			return this.client.listBuckets();
		} catch (Throwable var2) {
			throw var2;
		}
	}

	public void removeBucket(String bucketName) {
		this.client.deleteBucket(new DeleteBucketRequest(this.getBucketName(bucketName)));
	}

	public boolean bucketExists(String bucketName) {
		return this.client.doesBucketExistV2(this.getBucketName(bucketName));
	}

	public void copyFile(String bucketName, String fileName, String destBucketName) {
		this.copyFile(bucketName, fileName, destBucketName, fileName);
	}

	public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {
		this.client.copyObject(new CopyObjectRequest(this.getBucketName(bucketName), fileName, this.getBucketName(destBucketName), destFileName));
	}

	public OssFile statFile(String fileName) {
		return this.statFile(this.ossProperties.getBucketName(), fileName);
	}

	public OssFile statFile(String bucketName, String fileName) {
		S3Object stat = this.client.getObject(new GetObjectRequest(this.getBucketName(bucketName), fileName));
		OssFile ossFile = new OssFile();
		ossFile.setName(Func.isEmpty(stat.getKey()) ? fileName : stat.getKey());
		ossFile.setLink(this.fileLink(ossFile.getName()));
		ossFile.setHash(String.valueOf(stat.hashCode()));
		ossFile.setLength(stat.getObjectMetadata().getContentLength());
		ossFile.setPutTime(stat.getObjectMetadata().getLastModified());
		ossFile.setContentType(stat.getObjectMetadata().getContentType());
		return ossFile;
	}

	public InputStream statFileStream(String fileName) {
		return this.statFileStream(this.getBucketName(), fileName);
	}

	public InputStream statFileStream(String bucketName, String fileName) {
		return this.client.getObject(new GetObjectRequest(this.getBucketName(bucketName), fileName)).getObjectContent();
	}

	public String filePath(String fileName) {
		return this.getBucketName().concat("/").concat(fileName);
	}

	public String filePath(String bucketName, String fileName) {
		return this.getBucketName(bucketName).concat("/").concat(fileName);
	}

	public String fileLink(String fileName) {
		try {
			return this.getEndpoint().concat("/").concat(this.getBucketName()).concat("/").concat(fileName);
		} catch (Throwable var3) {
			throw var3;
		}
	}

	public String fileLink(String bucketName, String fileName) {
		try {
			return this.getEndpoint().concat("/").concat(this.getBucketName(bucketName)).concat("/").concat(fileName);
		} catch (Throwable var4) {
			throw var4;
		}
	}

	public BladeFile putFile(MultipartFile file) {
		try {
			return this.putFile(this.ossProperties.getBucketName(), file.getOriginalFilename(), file);
		} catch (Throwable var3) {
			throw var3;
		}
	}

	public BladeFile putFile(String fileName, MultipartFile file) {
		try {
			return this.putFile(this.ossProperties.getBucketName(), fileName, file);
		} catch (Throwable var4) {
			throw var4;
		}
	}

	public BladeFile putFile(String bucketName, String fileName, MultipartFile file) {
		try {
			return this.putFile(bucketName, file.getOriginalFilename(), file.getInputStream());
		} catch (Throwable var5) {
            try {
                throw var5;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
	}

	public BladeFile putFile(String fileName, InputStream stream) {
		return this.putFile(this.ossProperties.getBucketName(), fileName, stream);
	}

	public BladeFile putFile(String bucketName, String fileName, InputStream stream) {
		return this.putFile(bucketName, fileName, stream, "application/octet-stream");
	}

	public BladeFile putFile(String bucketName, String fileName, InputStream stream, String contentType) {
		try {
			this.makeBucket(bucketName);
			String originalName = fileName;
			ObjectMetadata objectMetadata = new ObjectMetadata();
			objectMetadata.setContentType(contentType);
			objectMetadata.setCacheControl("public, max-age=604800");
			fileName = this.getFileName(fileName);
			PutObjectRequest req = new PutObjectRequest(this.getBucketName(bucketName), fileName, stream, objectMetadata);
			req.withCannedAcl(CannedAccessControlList.PublicRead);
			this.client.putObject(req);
			BladeFile file = new BladeFile();
			file.setOriginalName(originalName);
			file.setName(fileName);
			file.setDomain(this.getOssHost(bucketName));
			file.setLink(this.fileLink(bucketName, fileName));
			return file;
		} catch (Throwable var8) {
			throw var8;
		}
	}

	public void removeFile(String fileName) {
		this.removeFile(this.ossProperties.getBucketName(), fileName);
	}

	public void removeFile(String bucketName, String fileName) {
		this.client.deleteObject(bucketName, fileName);
	}

	public void removeFiles(List<String> fileNames) {
		this.removeFiles(this.ossProperties.getBucketName(), fileNames);
	}

	public void removeFiles(String bucketName, List<String> fileNames) {
		List<DeleteObjectsRequest.KeyVersion> keyVersions = (List)fileNames.stream().map(DeleteObjectsRequest.KeyVersion::new).collect(Collectors.toList());
		DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(this.getBucketName());
		this.client.deleteObjects(deleteObjectsRequest.withKeys(keyVersions));
	}

	private String getBucketName() {
		return this.getBucketName(this.ossProperties.getBucketName());
	}

	private String getBucketName(String bucketName) {
		return this.ossRule.bucketName(bucketName);
	}

	private String getFileName(String originalFilename) {
		return this.ossRule.fileName(originalFilename);
	}

	public static String getPolicyType(String bucketName, PolicyType policyType) {
		StringBuilder builder = new StringBuilder();
		builder.append("{\n");
		builder.append("    \"Statement\": [\n");
		builder.append("        {\n");
		builder.append("            \"Action\": [\n");
		switch (policyType) {
			case WRITE:
				builder.append("                \"s3:GetBucketLocation\",\n");
				builder.append("                \"s3:ListBucketMultipartUploads\"\n");
				break;
			case READ_WRITE:
				builder.append("                \"s3:GetBucketLocation\",\n");
				builder.append("                \"s3:ListBucket\",\n");
				builder.append("                \"s3:ListBucketMultipartUploads\"\n");
				break;
			default:
				builder.append("                \"s3:GetBucketLocation\"\n");
		}

		builder.append("            ],\n");
		builder.append("            \"Effect\": \"Allow\",\n");
		builder.append("            \"Principal\": \"*\",\n");
		builder.append("            \"Resource\": \"arn:aws:s3:::");
		builder.append(bucketName);
		builder.append("\"\n");
		builder.append("        },\n");
		if (PolicyType.READ.equals(policyType)) {
			builder.append("        {\n");
			builder.append("            \"Action\": [\n");
			builder.append("                \"s3:ListBucket\"\n");
			builder.append("            ],\n");
			builder.append("            \"Effect\": \"Deny\",\n");
			builder.append("            \"Principal\": \"*\",\n");
			builder.append("            \"Resource\": \"arn:aws:s3:::");
			builder.append(bucketName);
			builder.append("\"\n");
			builder.append("        },\n");
		}

		builder.append("        {\n");
		builder.append("            \"Action\": ");
		switch (policyType) {
			case WRITE:
				builder.append("[\n");
				builder.append("                \"s3:AbortMultipartUpload\",\n");
				builder.append("                \"s3:DeleteObject\",\n");
				builder.append("                \"s3:ListMultipartUploadParts\",\n");
				builder.append("                \"s3:PutObject\"\n");
				builder.append("            ],\n");
				break;
			case READ_WRITE:
				builder.append("[\n");
				builder.append("                \"s3:AbortMultipartUpload\",\n");
				builder.append("                \"s3:DeleteObject\",\n");
				builder.append("                \"s3:GetObject\",\n");
				builder.append("                \"s3:ListMultipartUploadParts\",\n");
				builder.append("                \"s3:PutObject\"\n");
				builder.append("            ],\n");
				break;
			default:
				builder.append("\"s3:GetObject\",\n");
		}

		builder.append("            \"Effect\": \"Allow\",\n");
		builder.append("            \"Principal\": \"*\",\n");
		builder.append("            \"Resource\": \"arn:aws:s3:::");
		builder.append(bucketName);
		builder.append("/*\"\n");
		builder.append("        }\n");
		builder.append("    ],\n");
		builder.append("    \"Version\": \"2012-10-17\"\n");
		builder.append("}\n");
		return builder.toString();
	}

	public String getOssHost(String bucketName) {
		String var10000 = this.getEndpoint();
		return var10000 + "/" + this.getBucketName(bucketName);
	}

	public String getOssHost() {
		return this.getOssHost(this.ossProperties.getBucketName());
	}

	public String getEndpoint() {
		return StringUtil.isBlank(this.ossProperties.getTransformEndpoint()) ? this.ossProperties.getEndpoint() : this.ossProperties.getTransformEndpoint();
	}

}
