package com.greenorange.promotion.service.file.impl;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.greenorange.promotion.common.ErrorCode;
import com.greenorange.promotion.exception.BusinessException;
import com.greenorange.promotion.exception.ThrowUtils;
import com.greenorange.promotion.model.dto.fileInfo.FileInfoAddRequest;
import com.greenorange.promotion.model.dto.fileInfo.UploadFileRequest;
import com.greenorange.promotion.model.entity.FileInfo;
import com.greenorange.promotion.model.enums.FileUploadBizEnum;
import com.greenorange.promotion.service.common.CommonService;
import com.greenorange.promotion.service.file.FileInfoService;
import com.greenorange.promotion.mapper.FileInfoMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
* @author 35880
* @description 针对表【file_info(文件上传列表)】的数据库操作Service实现
* @createDate 2025-05-06 17:02:05
*/
@Service
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo>
    implements FileInfoService{



    // 文件上传的存储目录
    @Value("${file.upload-dir}")
    private String uploadDir;


    // 优化：设置一个合理的缓冲区大小
    private static final int BUFFER_SIZE = 8192; // 8 KB


    /**
     * 校验文件
     */
    @Override
    public void validFile(MultipartFile multipartFile, UploadFileRequest uploadFileRequest) {
        // 获取业务名称
        String biz = uploadFileRequest.getBiz();
        FileUploadBizEnum fileUploadBizEnum = FileUploadBizEnum.getEnumByValue(biz);
        ThrowUtils.throwIf(fileUploadBizEnum == null, ErrorCode.PARAMS_ERROR, "业务名称错误");
        // 文件大小
        long fileSize = multipartFile.getSize();
        // 文件后缀
        String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());
        final long LIMIT = 20 * 1024 * 1024L;
        ThrowUtils.throwIf(fileSize > LIMIT, ErrorCode.PARAMS_ERROR, "文件大小不能超过20MB");
        ThrowUtils.throwIf(!Arrays.asList("jpeg", "jpg", "svg", "png", "webp", "JPG", "pdf", "PDF").contains(fileSuffix),ErrorCode.PARAMS_ERROR, "文件类型错误");
    }



    /**
     * 获取文件保存路径
     */
    @Override
    public String uploadFile(MultipartFile multipartFile, UploadFileRequest uploadFileRequest) throws IOException {
        // 获取业务名称
        String biz = uploadFileRequest.getBiz();
        // 获取文件名
        String fileName = multipartFile.getOriginalFilename();
        // 获取文件类型
        String fileType = FileUtil.getSuffix(fileName);
        // 获取view值
        String view = RandomStringUtils.random(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
        // 获取文件大小
        Double fileSize = multipartFile.getSize() / 1024.0;
        fileSize = Double.valueOf(String.format("%.2f", fileSize));
        // 获取文件哈希值
        InputStream inputStream = multipartFile.getInputStream();
        String hashValue = DigestUtils.sha256Hex(inputStream);
        // 判断是否存在哈希值相同的图片
        LambdaQueryWrapper<FileInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(FileInfo::getHashValue, hashValue);
        FileInfo fileInfo = this.getOne(lambdaQueryWrapper);
        if (fileInfo != null) return biz + "-" + fileInfo.getFileView();
        // 保存文件
        fileInfo = FileInfo.builder()
                .name(fileName)
                .type(fileType)
                .size(fileSize)
                .fileView(view)
                .biz(biz)
                .hashValue(hashValue)
                .build();
        this.save(fileInfo);
        // 创建上传目录，如果不存在
        File file = new File(uploadDir + fileName);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();// 如果路径不存在则创建
        }
        // 将文件上传到目标位置
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE)) {
            bos.write(multipartFile.getBytes());
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败，失败原因：" + e.getMessage());
        }
        return biz + "-" + view;
    }



    /**
     * 文件下载
     * @param filename 业务名称/view值
     * @param response
     */
    @Override
    public void downloadFile(String filename, HttpServletResponse response) throws IOException {
        ThrowUtils.throwIf(!filename.contains("-"), ErrorCode.PARAMS_ERROR);
        String[] split = filename.split("-");
        FileUploadBizEnum fileUploadBizEnum = FileUploadBizEnum.getEnumByValue(split[0]);
        ThrowUtils.throwIf(fileUploadBizEnum == null, ErrorCode.PARAMS_ERROR, "业务类型错误");
        LambdaQueryWrapper<FileInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(FileInfo::getFileView, split[1]);
        FileInfo fileInfo = this.getOne(lambdaQueryWrapper);
        ThrowUtils.throwIf(fileInfo == null, ErrorCode.NOT_FOUND_ERROR, "文件不存在");

        File file = new File(uploadDir + fileInfo.getName());
//		// 设置response的Header
        response.setContentType("application/octet-stream");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileInfo.getName(), StandardCharsets.UTF_8));
        response.setContentLengthLong(file.length());  // 使用 setContentLengthLong 适应大文件

        // 设置缓存相关的 HTTP 头
        long lastModified = file.lastModified();
        response.setDateHeader("Last-Modified", lastModified); // 文件最后修改时间
        response.setHeader("Cache-Control", "public, max-age=86400");  // 缓存一天（24小时）
        response.setHeader("ETag", String.valueOf(lastModified)); // 使用文件最后修改时间作为 ETag

        // 检查浏览器缓存是否有效
        String ifNoneMatch = response.getHeader("If-None-Match");
        if (ifNoneMatch != null && ifNoneMatch.equals(String.valueOf(lastModified))) {
            // 如果 ETag 匹配，表示文件没有变化，返回 304 Not Modified
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }
        // 使用 BufferedInputStream 和 BufferedOutputStream 提高性能
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE);
             BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE)) {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            // 从文件中读取数据并写入响应输出流
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            bos.flush(); // 确保所有数据都已写入响应
        }
    }

}




