package com.cmvr.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.cmvr.common.config.properties.MinioProperties;
import com.cmvr.common.core.domain.entity.SysFileInfo;
import com.cmvr.common.core.minio.MinioService;
import com.cmvr.common.enums.FileType;
import com.cmvr.system.mapper.SysFileMapper;
import com.cmvr.system.service.ISysFileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;


/**
 * 文件上传服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ISysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFileInfo> implements ISysFileService {

    private final MinioService minioService;

    private final MinioProperties minioProps;

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

    @Override
    public List<SysFileInfo> selectFileList(SysFileInfo sysFileInfo) {
        LambdaQueryWrapper<SysFileInfo> wrapper = Wrappers.lambdaQuery();
        wrapper
                .eq(sysFileInfo.getFileType() != null, SysFileInfo::getFileType, sysFileInfo.getFileType())
                .like(StringUtils.isNotBlank(sysFileInfo.getFileName()), SysFileInfo::getFileName, sysFileInfo.getFileName());
        return this.list(wrapper);
    }

    @Override
    public String uploadFile(MultipartFile file, Integer fileType) {
        try {
            // 如果文件已存在，直接复用
            String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
            SysFileInfo fileInfo = findByMd5(md5);
            if (!Objects.isNull(fileInfo)) {
                this.updateById(fileInfo);
                // 返回
                return fileInfo.getFilePath();
            }
            // 上传
            FileType type = FileType.getByCode(fileType);
            String fileName = minioService.upload(minioProps.getBucketName(), type.toString(), file);
            if (StringUtils.isEmpty(fileName)) {
                throw new RuntimeException("文件上传失败");
            }
            String url = generUrl(type, fileName);
            // 保存文件
            saveFileInfo(file.getOriginalFilename(),file.getSize(), md5, url, fileType);
            log.info("文件上传成功,url:{}", url);
            return url;
        } catch (IOException e) {
            log.error("上传文件失败，{}", e.getMessage(), e);
            throw new RuntimeException("上传文件失败");
        }
    }

    @Override
    public String uploadFile(File file, Integer fileType) {
        try {
            // 计算文件的 MD5 校验码
            String md5 = DigestUtils.md5DigestAsHex(Files.newInputStream(file.toPath()));
            SysFileInfo fileInfo = findByMd5(md5);

            if (!Objects.isNull(fileInfo)) {
                this.updateById(fileInfo);
                // 如果文件已存在，直接返回已上传文件的 URL
                return fileInfo.getFilePath();
            }

            // 上传文件到 MinIO
            FileType type = FileType.getByCode(fileType);
            String fileName = minioService.upload(minioProps.getBucketName(), type.toString(), file);

            if (StringUtils.isEmpty(fileName)) {
                throw new RuntimeException("文件上传失败");
            }

            // 生成文件的 URL
            String url = generUrl(type, fileName);
            // 将文件信息保存到数据库
            saveFileInfo(file.getName(),file.length(), md5, url, fileType);
            log.info("文件上传成功，url: {}", url);

            return url;
        } catch (IOException e) {
            log.error("文件上传失败，{}", e.getMessage(), e);
            throw new RuntimeException("文件上传失败");
        }
    }

    @Override
    public int deleteFileByIds(Long[] fileIds) {
        return this.baseMapper.deleteBatchIds(Arrays.asList(fileIds));
    }

    private String generUrl(FileType fileType, String fileName) {
        return StrUtil.join("/", minioProps.getUrl(), minioProps.getBucketName(), fileType, fileName);
    }

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

    private void saveFileInfo(String filename,Long size, String md5, String filePath, Integer fileType) throws IOException {
        SysFileInfo fileInfo = new SysFileInfo();
        fileInfo.setFileName(filename);
        fileInfo.setFileSize(size);
        fileInfo.setFileType(fileType);
        fileInfo.setFilePath(filePath);
        fileInfo.setMd5(md5);
        fileInfo.setCreateTime(new Date());
        this.save(fileInfo);
    }

}
