package com.yd.oss.service.service.impl;

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.PutObjectRequest;
import com.yd.common.enums.CommonEnum;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.oss.service.dto.FileMetadata;
import com.yd.oss.service.dto.UploadResult;
import com.yd.oss.service.model.OssFile;
import com.yd.oss.service.model.OssOperationLog;
import com.yd.oss.service.model.OssProvider;
import com.yd.oss.service.service.IOssFileService;
import com.yd.oss.service.service.IOssProviderService;
import com.yd.oss.service.service.OssService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URL;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.UUID;

@Service
@Primary
@Slf4j
public class AliYunOssServiceImpl implements OssService {

    @Autowired
    private IOssProviderService ossProviderService;

    @Autowired
    private IOssFileService ossFileService;

    @Autowired
    private OSS ossClient; // 注入OSS客户端

    @Autowired
    private String defaultBucket; // 注入默认存储桶

    @Autowired
    private OssProvider currentProvider; // 注入当前提供商

    /**
     * 上传文件
     * @param inputStream
     * @param fileName
     * @param bucketName
     * @param uploadUser
     * @return
     */
    @Override
    public String uploadFile(InputStream inputStream, String fileName, String bucketName, String uploadUser) {
        long startTime = System.currentTimeMillis(); // 记录开始时间
        String operationResult = "success"; // 操作结果
        String errorMessage = null; // 错误信息
        OssFile ossFile = null; // 文件记录

        try {
            // 读取输入流到字节数组
            byte[] fileData = readInputStreamToBytes(inputStream);
            long fileSize = fileData.length;
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(fileData);

            // 生成文件key
            String fileKey = generateFileKey(fileName);
            String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;
            String fileType = getFileType(fileName);

            // 创建上传请求
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(fileSize);
            metadata.setContentType(getContentType(fileType));
            // 设置公共读权限（如果需要）
//            metadata.setObjectAcl(CannedAccessControlList.PublicRead);

            PutObjectRequest putObjectRequest = new PutObjectRequest(
                    actualBucket, fileKey, byteArrayInputStream, metadata);

            // 上传文件
            ossClient.putObject(putObjectRequest);

            // 创建文件记录
            ossFile = new OssFile();
            ossFile.setFileBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_OSS_FILE.getCode()));
            ossFile.setFileKey(fileKey);
            ossFile.setOriginalName(fileName);
            ossFile.setFileSize(fileSize);
            ossFile.setFileType(fileType);
            ossFile.setProviderBizId(currentProvider.getProviderBizId());
            ossFile.setBucketName(actualBucket);
            ossFile.setUploadUser(uploadUser);

            // 保存文件元数据到数据库
            ossFileService.saveFileMetadata(ossFile);

            // 创建操作日志
            OssOperationLog operationLog = new OssOperationLog();
            operationLog.setFileBizId(ossFile.getFileBizId());
            operationLog.setOperationType("upload");
            operationLog.setOperationUser(uploadUser);
            operationLog.setOperationResult(operationResult);
            operationLog.setErrorMessage(errorMessage);
            operationLog.setClientIp(getClientIp());
            operationLog.setCostTime(System.currentTimeMillis() - startTime);

            // 保存操作日志到数据库
            ossFileService.saveOperationLog(operationLog);

            log.info("文件上传成功: {} -> {}/{}", fileName, actualBucket, fileKey);
            return fileKey;
        } catch (Exception e) {
            operationResult = "failure"; // 标记操作失败
            errorMessage = e.getMessage(); // 记录错误信息
            log.error("阿里云OSS上传文件失败: {}", fileName, e);
            throw new RuntimeException("阿里云OSS上传文件失败: " + fileName, e);
        } finally {
            // 关闭资源
            closeQuietly(inputStream);

            // 记录失败日志
            if ("failure".equals(operationResult)) {
                OssOperationLog operationLog = new OssOperationLog();
                if (ossFile != null) {
                    operationLog.setFileBizId(ossFile.getFileBizId());
                } else {
                    operationLog.setFileBizId("-1"); // 使用无效文件ID
                }
                operationLog.setOperationType("upload");
                operationLog.setOperationUser(uploadUser);
                operationLog.setOperationResult(operationResult);
                operationLog.setErrorMessage(errorMessage);
                operationLog.setClientIp(getClientIp());
                operationLog.setCostTime(System.currentTimeMillis() - startTime);

                // 保存操作日志到数据库
                ossFileService.saveOperationLog(operationLog);
            }
        }
    }

    /**
     * 上传文件（使用默认存储桶）
     * @param inputStream
     * @param fileName
     * @param uploadUser
     * @return
     */
    @Override
    public String uploadFile(InputStream inputStream, String fileName, String uploadUser) {
        // 调用重载方法，使用默认存储桶
        return uploadFile(inputStream, fileName, defaultBucket, uploadUser);
    }

    /**
     * 下载文件
     * @param fileKey
     * @param bucketName
     * @return
     */
    @Override
    public InputStream downloadFile(String fileKey, String bucketName) {
        long startTime = System.currentTimeMillis(); // 记录开始时间
        String operationResult = "success"; // 操作结果
        String errorMessage = null; // 错误信息
        OssFile ossFile = null; // 文件记录

        try {
            // 确定存储桶（如果未指定则使用默认存储桶）
            String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;

            // 获取文件信息
            ossFile = ossFileService.getFileByKey(fileKey);

            // 从阿里云OSS下载文件
            OSSObject ossObject = ossClient.getObject(actualBucket, fileKey);
            InputStream inputStream = ossObject.getObjectContent();

            // 创建操作日志
            OssOperationLog operationLog = new OssOperationLog();
            operationLog.setFileBizId(ossFile.getFileBizId());
            operationLog.setOperationType("download");
            operationLog.setOperationUser("system"); // 系统操作
            operationLog.setOperationResult(operationResult);
            operationLog.setErrorMessage(errorMessage);
            operationLog.setClientIp(getClientIp());
            operationLog.setCostTime(System.currentTimeMillis() - startTime);

            // 保存操作日志到数据库
            ossFileService.saveOperationLog(operationLog);

            log.info("文件下载成功: {}/{}", actualBucket, fileKey);
            return inputStream;
        } catch (Exception e) {
            operationResult = "failure"; // 标记操作失败
            errorMessage = e.getMessage(); // 记录错误信息
            log.error("阿里云OSS下载文件失败: {}", fileKey, e);
            throw new RuntimeException("阿里云OSS下载文件失败: " + fileKey, e);
        } finally {
            // 记录失败日志
            if ("failure".equals(operationResult)) {
                OssOperationLog operationLog = new OssOperationLog();
                if (ossFile != null) {
                    operationLog.setFileBizId(ossFile.getFileBizId());
                } else {
                    operationLog.setFileBizId("-1"); // 使用无效文件ID
                }
                operationLog.setOperationType("download");
                operationLog.setOperationUser("system");
                operationLog.setOperationResult(operationResult);
                operationLog.setErrorMessage(errorMessage);
                operationLog.setClientIp(getClientIp());
                operationLog.setCostTime(System.currentTimeMillis() - startTime);

                // 保存操作日志到数据库
                ossFileService.saveOperationLog(operationLog);
            }
        }
    }

    /**
     * 下载文件（使用默认存储桶）
     * @param fileKey
     * @return
     */
    @Override
    public InputStream downloadFile(String fileKey) {
        // 调用重载方法，使用默认存储桶
        return downloadFile(fileKey, defaultBucket);
    }

    /**
     * 删除文件
     * @param fileKey
     * @param bucketName
     * @param operator
     * @return
     */
    @Override
    public boolean deleteFile(String fileKey, String bucketName, String operator) {
        long startTime = System.currentTimeMillis(); // 记录开始时间
        String operationResult = "success"; // 操作结果
        String errorMessage = null; // 错误信息
        OssFile ossFile = null; // 文件记录

        try {
            // 确定存储桶（如果未指定则使用默认存储桶）
            String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;

            // 获取文件信息
            ossFile = ossFileService.getFileByKey(fileKey);

            // 从阿里云OSS删除文件 TODO
            ossClient.deleteObject(actualBucket, ossFile.getFileKey());

            // 标记文件为已删除
            ossFileService.markFileAsDeleted(fileKey);

            // 创建操作日志
            OssOperationLog operationLog = new OssOperationLog();
            operationLog.setFileBizId(ossFile.getFileBizId());
            operationLog.setOperationType("delete");
            operationLog.setOperationUser(operator);
            operationLog.setOperationResult(operationResult);
            operationLog.setErrorMessage(errorMessage);
            operationLog.setClientIp(getClientIp());
            operationLog.setCostTime(System.currentTimeMillis() - startTime);

            // 保存操作日志到数据库
            ossFileService.saveOperationLog(operationLog);

            log.info("文件删除成功: {}/{}", actualBucket, fileKey);
            return true;
        } catch (Exception e) {
            operationResult = "failure"; // 标记操作失败
            errorMessage = e.getMessage(); // 记录错误信息
            log.error("阿里云OSS删除文件失败: {}", fileKey, e);
            throw new RuntimeException("阿里云OSS删除文件失败: " + fileKey, e);
        } finally {
            // 记录失败日志
            if ("failure".equals(operationResult)) {
                OssOperationLog operationLog = new OssOperationLog();
                if (ossFile != null) {
                    operationLog.setFileBizId(ossFile.getFileBizId());
                } else {
                    operationLog.setFileBizId("-1"); // 使用无效文件ID
                }
                operationLog.setOperationType("delete");
                operationLog.setOperationUser(operator);
                operationLog.setOperationResult(operationResult);
                operationLog.setErrorMessage(errorMessage);
                operationLog.setClientIp(getClientIp());
                operationLog.setCostTime(System.currentTimeMillis() - startTime);

                // 保存操作日志到数据库
                ossFileService.saveOperationLog(operationLog);
            }
        }
    }

    /**
     * 删除文件（使用默认存储桶）
     * @param fileKey
     * @param operator
     * @return
     */
    @Override
    public boolean deleteFile(String fileKey, String operator) {
        // 调用重载方法，使用默认存储桶
        return deleteFile(fileKey, defaultBucket, operator);
    }

    /**
     * 获取文件URL
     * @param fileKey
     * @param bucketName
     * @param expiration
     * @return
     */
    @Override
    public String getFileUrl(String fileKey, String bucketName, Duration expiration) {
        try {
            // 确定存储桶（如果未指定则使用默认存储桶）
            String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;

            // 生成预签名URL（带过期时间）
            Date expirationDate = new Date(System.currentTimeMillis() + expiration.toMillis());
            URL url = ossClient.generatePresignedUrl(actualBucket, fileKey, expirationDate);

            return url.toString();
        } catch (Exception e) {
            log.error("阿里云OSS获取文件URL失败: {}", fileKey, e);
            throw new RuntimeException("阿里云OSS获取文件URL失败: " + fileKey, e);
        }
    }

    /**
     * 获取文件URL（使用默认存储桶）
     * @param fileKey
     * @param expiration
     * @return
     */
    @Override
    public String getFileUrl(String fileKey, Duration expiration) {
        // 调用重载方法，使用默认存储桶
        return getFileUrl(fileKey, defaultBucket, expiration);
    }

    /**
     * 判断文件是否存在
     * @param fileKey
     * @param bucketName
     * @return
     */
    @Override
    public boolean doesFileExist(String fileKey, String bucketName) {
        try {
            // 确定存储桶（如果未指定则使用默认存储桶）
            String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;

            // 检查文件是否存在
            return ossClient.doesObjectExist(actualBucket, fileKey);
        } catch (Exception e) {
            log.error("阿里云OSS检查文件存在失败: {}", fileKey, e);
            throw new RuntimeException("阿里云OSS检查文件存在失败: " + fileKey, e);
        }
    }

    /**
     * 判断文件是否存在（使用默认存储桶）
     * @param fileKey
     * @return
     */
    @Override
    public boolean doesFileExist(String fileKey) {
        // 调用重载方法，使用默认存储桶
        return doesFileExist(fileKey, defaultBucket);
    }

    /**
     * 获取文件元数据
     * @param fileKey
     * @param bucketName
     * @return
     */
    @Override
    public FileMetadata getFileMetadata(String fileKey, String bucketName) {
        try {
            // 确定存储桶（如果未指定则使用默认存储桶）
            String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;

            // 获取文件元数据
            ObjectMetadata objectMetadata = ossClient.getObjectMetadata(actualBucket, fileKey);

            // 转换为通用文件元数据格式
            FileMetadata metadata = new FileMetadata();
            metadata.setFileKey(fileKey);
            metadata.setFileSize(objectMetadata.getContentLength());
            metadata.setContentType(objectMetadata.getContentType());
//            metadata.setLastModified(objectMetadata.getLastModified());
            metadata.setEtag(objectMetadata.getETag());

            return metadata;
        } catch (Exception e) {
            log.error("阿里云OSS获取文件元数据失败: {}", fileKey, e);
            throw new RuntimeException("阿里云OSS获取文件元数据失败: " + fileKey, e);
        }
    }

    /**
     * 获取文件元数据（使用默认存储桶）
     * @param fileKey
     * @return
     */
    @Override
    public FileMetadata getFileMetadata(String fileKey) {
        // 调用重载方法，使用默认存储桶
        return getFileMetadata(fileKey, defaultBucket);
    }

    /**
     * 切换OSS提供商
     * @param providerName
     */
    @Override
    public void switchProvider(String providerName) {
        try {
            // 根据名称获取OSS提供商
            OssProvider provider = ossProviderService.getProviderByName(providerName);

            // 切换到新的提供商
            switchToProvider(provider);

            log.info("已切换到OSS提供商: {}", providerName);
        } catch (Exception e) {
            log.error("切换OSS提供商失败: {}", providerName, e);
            throw new RuntimeException("切换OSS提供商失败: " + providerName, e);
        }
    }

    /**
     * 切换OSS提供商（通过ID）
     * @param providerId
     */
    @Override
    public void switchProvider(Long providerId) {
        try {
            // 根据ID获取OSS提供商
            OssProvider provider = ossProviderService.getProviderById(providerId);

            // 切换到新的提供商
            switchToProvider(provider);

            log.info("已切换到OSS提供商ID: {}", providerId);
        } catch (Exception e) {
            log.error("切换OSS提供商失败: {}", providerId, e);
            throw new RuntimeException("切换OSS提供商失败: " + providerId, e);
        }
    }

    // 切换到指定的OSS提供商
    private void switchToProvider(OssProvider provider) {
        // 关闭旧的客户端
        if (this.ossClient != null) {
            this.ossClient.shutdown();
        }

        // 创建新的客户端
        this.currentProvider = provider;
        this.ossClient = new OSSClientBuilder().build(
                provider.getEndpoint(),
                provider.getAccessKey(),
                provider.getSecretKey()
        );
        this.defaultBucket = provider.getBucketName();
    }

    /**
     * 获取当前使用的提供商
     * @return
     */
    @Override
    public OssProvider getCurrentProvider() {
        return currentProvider;
    }

    /**
     * 设置默认存储桶
     * @param bucketName
     */
    @Override
    public void setDefaultBucket(String bucketName) {
        this.defaultBucket = bucketName;
        log.info("已设置默认存储桶: {}", bucketName);
    }

    /**
     * 获取默认存储桶
     * @return
     */
    @Override
    public String getDefaultBucket() {
        return defaultBucket;
    }

    /**
     * 获取上传结果（包含文件信息和访问URL）
     * @param fileKey
     * @param expiration
     * @return
     */
    @Override
    public UploadResult getUploadResult(String fileKey, Duration expiration) {
        try {
            // 获取文件信息
            OssFile file = ossFileService.getFileByKey(fileKey);

            // 生成访问URL
            String accessUrl = getFileUrl(fileKey, expiration);

            // 使用 split 方法以 "?" 为分隔符分割字符串
            String[] parts = accessUrl.split("\\?");
            String url = parts[0]; // 取问号之前的部分

            // 创建上传结果
            UploadResult result = new UploadResult();
            result.setFileKey(fileKey);
            result.setAccessUrl(accessUrl);
            result.setUrl(url);
            result.setFileSize(file.getFileSize());
            result.setFileType(file.getFileType());
            result.setOriginalName(file.getOriginalName());
            result.setUploadTime(file.getUploadTime());

            return result;
        } catch (Exception e) {
            log.error("获取上传结果失败: {}", fileKey, e);
            throw new RuntimeException("获取上传结果失败: " + fileKey, e);
        }
    }

    /**
     * 生成文件key（唯一标识）
     * @param originalFileName
     * @return
     */
    private String generateFileKey(String originalFileName) {
        // 提取文件扩展名
        String fileExtension = originalFileName.contains(".") ?
                originalFileName.substring(originalFileName.lastIndexOf(".")) : "";

        // 生成唯一文件key：日期目录 + UUID + 扩展名
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/")) +
                UUID.randomUUID().toString().replace("-", "") + fileExtension;
    }

    /**
     * 获取输入流大小
     * @param inputStream
     * @return
     * @throws IOException
     */
    private Long getInputStreamSize(InputStream inputStream) throws IOException {
        if (inputStream instanceof ByteArrayInputStream) {
            // 如果是ByteArrayInputStream，直接获取可用字节数
            return (long) inputStream.available();
        }

        // 对于其他类型的流，需要读取并计算大小
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        long size = 0;

        while ((len = inputStream.read(buffer)) > -1) {
            baos.write(buffer, 0, len);
            size += len;
        }

        return size;
    }

    /**
     * 读取输入流到字节数组
     * @param inputStream
     * @return
     * @throws IOException
     */
    private byte[] readInputStreamToBytes(InputStream inputStream) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int nRead;
        byte[] data = new byte[1024];
        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, nRead);
        }
        buffer.flush();
        return buffer.toByteArray();
    }

    /**
     * 重置输入流
     * @param originalStream
     * @param size
     * @return
     * @throws IOException
     */
    private InputStream resetInputStream(InputStream originalStream, long size) throws IOException {
        if (originalStream instanceof ByteArrayInputStream) {
            // 如果是ByteArrayInputStream，可以直接重置
            ((ByteArrayInputStream) originalStream).reset();
            return originalStream;
        }

        // 对于其他类型的流，需要重新创建
        // 注意：这里简化处理，实际应用中可能需要更复杂的逻辑
        byte[] data = new byte[(int) size];
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
            return bais;
        }
    }

    /**
     * 获取文件类型（扩展名）
     * @param fileName
     * @return
     */
    private String getFileType(String fileName) {
        return fileName.contains(".") ?
                fileName.substring(fileName.lastIndexOf(".") + 1) : "";
    }

    /**
     * 根据文件类型获取内容类型
     * @param fileType
     * @return
     */
    private String getContentType(String fileType) {
        switch (fileType.toLowerCase()) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "pdf":
                return "application/pdf";
            case "txt":
                return "text/plain";
            case "html":
                return "text/html";
            case "css":
                return "text/css";
            case "js":
                return "application/javascript";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 获取客户端IP
     * @return
     */
    private String getClientIp() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String ip = request.getHeader("X-Forwarded-For");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            return ip;
        } catch (Exception e) {
            return "unknown";
        }
    }

    /**
     * 安静关闭资源
     * @param closeable
     */
    private void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                // 安静关闭，忽略异常
            }
        }
    }

    /**
     * 创建虚拟文件记录（用于操作日志）
     * @return
     */
    private OssFile createDummyOssFile() {
        OssFile file = new OssFile();
        file.setId(-1L); // 使用无效ID
        return file;
    }
}
