package com.writeoff.module.file.service;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import com.aliyun.oss.HttpMethod;

@Service
public class OssService {
    @Value("${app.oss.endpoint}")
    private String endpoint;
    @Value("${app.oss.bucket}")
    private String bucket;
    @Value("${app.oss.access-key-id}")
    private String accessKeyId;
    @Value("${app.oss.access-key-secret}")
    private String accessKeySecret;
    @Value("${app.oss.sign-expire-seconds:600}")
    private int signExpireSeconds;

    public String generateDownloadUrl(String objectKey) {
        return generateDownloadUrl(objectKey, null);
    }

    public String generateDownloadUrl(String objectKey, String downloadFileName) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            Date expiration = new Date(System.currentTimeMillis() + signExpireSeconds * 1000L);
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucket, objectKey, HttpMethod.GET);
            req.setExpiration(expiration);
            String responseDisposition = buildAttachmentDisposition(downloadFileName);
            if (responseDisposition != null && !responseDisposition.isEmpty()) {
                req.addQueryParameter("response-content-disposition", responseDisposition);
            }
            URL url = ossClient.generatePresignedUrl(req);
            return url.toString();
        } finally {
            ossClient.shutdown();
        }
    }

    public String generateUploadUrl(String objectKey) {
        return generateUploadUrl(objectKey, "application/octet-stream");
    }

    public String generateUploadUrl(String objectKey, String contentType) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            Date expiration = new Date(System.currentTimeMillis() + signExpireSeconds * 1000L);
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucket, objectKey, HttpMethod.PUT);
            req.setExpiration(expiration);
            req.setContentType(contentType == null || contentType.trim().isEmpty() ? "application/octet-stream" : contentType.trim());
            URL url = ossClient.generatePresignedUrl(req);
            return url.toString();
        } finally {
            ossClient.shutdown();
        }
    }

    public void putTextObject(String objectKey, String content, String contentType) {
        byte[] bytes = content == null ? new byte[0] : content.getBytes(StandardCharsets.UTF_8);
        putObject(objectKey, bytes, contentType == null || contentType.trim().isEmpty() ? "text/plain;charset=utf-8" : contentType.trim());
    }

    public void putObject(String objectKey, byte[] bytes, String contentType) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            byte[] safeBytes = bytes == null ? new byte[0] : bytes;
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(safeBytes.length);
            metadata.setContentType(contentType == null || contentType.trim().isEmpty() ? "application/octet-stream" : contentType.trim());
            ossClient.putObject(bucket, objectKey, new ByteArrayInputStream(safeBytes), metadata);
        } finally {
            ossClient.shutdown();
        }
    }

    public byte[] getObjectBytes(String objectKey) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try (OSSObject object = ossClient.getObject(bucket, objectKey);
             InputStream inputStream = object.getObjectContent()) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[8192];
            int len;
            while ((len = inputStream.read(buffer)) >= 0) {
                outputStream.write(buffer, 0, len);
            }
            return outputStream.toByteArray();
        } catch (IOException ex) {
            throw new IllegalStateException("Failed to read OSS object: " + objectKey, ex);
        } finally {
            ossClient.shutdown();
        }
    }

    private String buildAttachmentDisposition(String downloadFileName) {
        String fileName = downloadFileName == null ? "" : downloadFileName.trim();
        if (fileName.isEmpty()) {
            return "";
        }
        String asciiName = fileName.replaceAll("[^\\x20-\\x7E]", "_").replace("\"", "_");
        if (asciiName.trim().isEmpty()) {
            asciiName = "download";
        }
        String encoded = urlEncodeUtf8(fileName);
        return "attachment; filename=\"" + asciiName + "\"; filename*=UTF-8''" + encoded;
    }

    private String urlEncodeUtf8(String value) {
        try {
            return URLEncoder.encode(value, "UTF-8").replace("+", "%20");
        } catch (Exception ex) {
            return value == null ? "" : value;
        }
    }
}
