package com.ruoyi.system.service.impl;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.utils.ImageUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.domain.FileInfo;
import com.ruoyi.system.domain.vo.UploadImageVO;
import com.ruoyi.system.mapper.FileInfoMapper;
import com.ruoyi.system.service.FileService;
import com.ruoyi.system.thirdparty.MinioProperties;
import com.ruoyi.system.thirdparty.MinioService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Date;
import java.util.Objects;

/**
 * 文件上传服务
 *
 * author: Blue date: 2024-09-28 version: 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileServiceImpl  extends ServiceImpl<FileInfoMapper, FileInfo> implements FileService {

    private final MinioService minioSerivce;

    private final MinioProperties minioProps;

    /**
     * 最大图片上传大小
     */
    public static final Long MAX_IMAGE_SIZE = 20 * 1024 * 1024L;
    /**
     * 最大上传文件大小
     */
    public static final Long MAX_FILE_SIZE = 20 * 1024 * 1024L;
    public static final int FILE = 0;//文件
    public static final int PRODUCTIMAGE = 1;//商品图片
    public static final int COURSEIMAGE = 2;//课程图片
    public static final  Boolean isPermanent = true;//课程图片



    @PostConstruct
    public void init() {
        if (!minioSerivce.bucketExists(minioProps.getBucketName())) {
            // 创建bucket
            minioSerivce.makeBucket(minioProps.getBucketName());
            // 公开bucket
            minioSerivce.setBucketPublic(minioProps.getBucketName());
        }
    }


    @Override
    public String uploadFile(MultipartFile file) {
        try {
            Long userId = SecurityUtils.getUserId();
            // 大小校验
            if (file.getSize() > MAX_FILE_SIZE) {
                throw new GlobalException("文件大小不能超过20M");
            }
            // 如果文件已存在，直接复用
            String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
            FileInfo fileInfo = findByMd5(md5);
            if (!Objects.isNull(fileInfo)) {
                // 更新上传时间
                fileInfo.setUploadTime(new Date());
                this.updateById(fileInfo);
                // 返回

                return fileInfo.getFilePath().replace("http://62.234.183.14:9000/","https://www.sanduolantoyoga.com/minio/");
            }
            // 上传
            String fileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getFilePath(), file);
            if (StringUtils.isEmpty(fileName)) {
                throw new GlobalException( "文件上传失败");
            }
            String url = generUrl(FILE, fileName);
            // 保存文件
            saveFileInfo(file, md5, url);
            log.info("文件文件成功，用户id:{},url:{}", userId, url);
            url=url.replace("http://62.234.183.14:9000/","https://www.sanduolantoyoga.com/minio/");
            return url;
        } catch (IOException e) {
            log.error("上传图片失败，{}", e.getMessage(), e);
            throw new GlobalException( "上传图片失败");
        }
    }

    /**
     * 上传商城相关图片
     * @param file
     * @return
     */
    @Transactional
    @Override
    public UploadImageVO uploadProductImage(MultipartFile file) {
        try {
            Long userId = SecurityUtils.getUserId();
            // 大小校验
            if (file.getSize() > MAX_IMAGE_SIZE) {
                throw new GlobalException("图片大小不能超过20M");
            }
            // 图片格式校验
            if (!isImage(file.getOriginalFilename())) {
                throw new GlobalException("图片格式不合法");
            }
            UploadImageVO vo = new UploadImageVO();
            // 如果文件已存在，直接复用
            String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
            FileInfo fileInfo = findByMd5(md5);
            if (!Objects.isNull(fileInfo)) {
                // 更新上传时间和持久化标记
                fileInfo.setIsPermanent(isPermanent || fileInfo.getIsPermanent());
                fileInfo.setUploadTime(new Date());
                this.updateById(fileInfo);
                // 返回
                vo.setOriginUrl(fileInfo.getFilePath());
                vo.setThumbUrl(fileInfo.getCompressedPath());
                return vo;
            }
            // 上传原图
           String fileName=minioSerivce.upload(minioProps.getBucketName(), minioProps.getProductImagePath(), file);



            if (StringUtils.isEmpty(fileName)) {
                throw new GlobalException("图片上传失败");
            }
            vo.setOriginUrl(generUrl(PRODUCTIMAGE, fileName));
            if (file.getSize() > 50 * 1024) {
                // 大于50K的文件需上传缩略图
                byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30);
                String thumbFileName= minioSerivce.upload(minioProps.getBucketName(), minioProps.getProductImagePath(),
                            file.getOriginalFilename(), imageByte, file.getContentType());


                if (StringUtils.isEmpty(thumbFileName)) {
                    throw new GlobalException( "图片上传失败");
                }
                vo.setThumbUrl(generUrl(PRODUCTIMAGE, thumbFileName));
                // 保存文件信息
                saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), isPermanent,PRODUCTIMAGE);
            }else{
                // 小于50k，用原图充当缩略图
                vo.setThumbUrl(generUrl(PRODUCTIMAGE, fileName));
                // 保存文件信息,由于缩略图不允许删除，此时原图也不允许删除
                saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), true,PRODUCTIMAGE);
            }
            log.info("文件图片成功，用户id:{},url:{}", userId, vo.getOriginUrl());
            return vo;
        } catch (IOException e) {
            log.error("上传图片失败，{}", e.getMessage(), e);
            throw new GlobalException( "图片上传失败");
        }
    }

    /**
     * 上传课程相关图片
     * @param file
     * @return
     */
    @Transactional
    @Override
    public UploadImageVO uploadCourseImage(MultipartFile file) {
        try {
            Long userId = SecurityUtils.getUserId();
            // 大小校验
            if (file.getSize() > MAX_IMAGE_SIZE) {
                throw new GlobalException("图片大小不能超过20M");
            }
            // 图片格式校验
            if (!isImage(file.getOriginalFilename())) {
                throw new GlobalException("图片格式不合法");
            }
            UploadImageVO vo = new UploadImageVO();
            // 如果文件已存在，直接复用
            String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
            FileInfo fileInfo = findByMd5(md5);
            if (!Objects.isNull(fileInfo)) {
                // 更新上传时间和持久化标记
                fileInfo.setIsPermanent(isPermanent || fileInfo.getIsPermanent());
                fileInfo.setUploadTime(new Date());
                this.updateById(fileInfo);
                // 返回
                vo.setOriginUrl(fileInfo.getFilePath());
                vo.setThumbUrl(fileInfo.getCompressedPath());
                return vo;
            }
            // 上传原图
            String fileName=minioSerivce.upload(minioProps.getBucketName(), minioProps.getCourseImagePath(), file);



            if (StringUtils.isEmpty(fileName)) {
                throw new GlobalException("图片上传失败");
            }
            vo.setOriginUrl(generUrl(COURSEIMAGE, fileName));
            if (file.getSize() > 50 * 1024) {
                // 大于50K的文件需上传缩略图
                byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30);
                String thumbFileName= minioSerivce.upload(minioProps.getBucketName(), minioProps.getCourseImagePath(),
                        file.getOriginalFilename(), imageByte, file.getContentType());


                if (StringUtils.isEmpty(thumbFileName)) {
                    throw new GlobalException( "图片上传失败");
                }
                vo.setThumbUrl(generUrl(COURSEIMAGE, thumbFileName));
                // 保存文件信息
                saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), isPermanent,COURSEIMAGE);
            }else{
                // 小于50k，用原图充当缩略图
                vo.setThumbUrl(generUrl(COURSEIMAGE, fileName));
                // 保存文件信息,由于缩略图不允许删除，此时原图也不允许删除
                saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), true,COURSEIMAGE);
            }
            log.info("文件图片成功，用户id:{},url:{}", userId, vo.getOriginUrl());
            return vo;
        } catch (IOException e) {
            log.error("上传图片失败，{}", e.getMessage(), e);
            throw new GlobalException( "图片上传失败");
        }
    }
    private String generUrl(int fileType, String fileName) {
        return StrUtil.join("/", minioProps.getEndpoint(), minioProps.getBucketName(), getBucketPath(fileType), fileName);
    }

    private String getBucketPath(int fileType) {
         switch (fileType) {
            case FILE : return minioProps.getFilePath();
            case COURSEIMAGE : return minioProps.getCourseImagePath();
             case PRODUCTIMAGE: return minioProps.getProductImagePath();
            default:return null;
        }
    }

    private FileInfo findByMd5(String md5) {
        LambdaQueryWrapper<FileInfo> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(FileInfo::getMd5, md5);
        return getOne(wrapper);
    }

    private void saveImageFileInfo(MultipartFile file, String md5, String filePath, String compressedPath,
        Boolean isPermanent,int fileType) throws IOException {
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileName(file.getOriginalFilename());
        fileInfo.setFileSize(file.getSize());
        fileInfo.setFileType(fileType);
        fileInfo.setFilePath(filePath);
        fileInfo.setCompressedPath(compressedPath);
        fileInfo.setMd5(md5);
        fileInfo.setIsPermanent(isPermanent);
        fileInfo.setUploadTime(new Date());
        this.save(fileInfo);
    }

    private void saveFileInfo(MultipartFile file, String md5, String filePath) throws IOException {
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileName(file.getOriginalFilename());
        fileInfo.setFileSize(file.getSize());
        fileInfo.setFileType(FILE);
        fileInfo.setFilePath(filePath);
        fileInfo.setMd5(md5);
        fileInfo.setIsPermanent(false);
        fileInfo.setUploadTime(new Date());
        this.save(fileInfo);
    }
    /**
     * 获取文件后缀
     *
     * @param fileName 文件名
     * @return boolean
     */
    public static String getFileExtension(String fileName) {
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    /**
     * 判断文件是否图片类型
     *
     * @param fileName 文件名
     * @return boolean
     */
    public static boolean isImage(String fileName) {
        String extension = getFileExtension(fileName);
        String[] imageExtension = new String[]{"jpeg", "jpg", "bmp", "png", "webp", "gif"};
        for (String e : imageExtension) {
            if (extension.toLowerCase().equals(e)) {
                return true;
            }
        }

        return false;
    }
}
