package com.cy.guoyan.admin.module.infra.service.file;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.cy.guoyan.admin.framework.common.pojo.PageResult;
import com.cy.guoyan.admin.framework.common.util.object.BeanUtils;
import com.cy.guoyan.admin.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
import com.cy.guoyan.admin.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import com.cy.guoyan.admin.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
import com.cy.guoyan.admin.module.infra.dal.dataobject.file.FileDO;
import com.cy.guoyan.admin.module.infra.dal.mysql.file.FileMapper;
import com.cy.guoyan.admin.module.infra.framework.file.core.client.FileClient;
import com.cy.guoyan.admin.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import com.cy.guoyan.admin.module.infra.framework.file.core.utils.FileTypeUtils;
import com.google.common.annotations.VisibleForTesting;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.fs.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
import static com.cy.guoyan.admin.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cy.guoyan.admin.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;

/**
 * 文件 Service 实现类
 *
 * @author 木易
 */
@Service
@Slf4j
public class FileServiceImpl implements FileService {

    /**
     * 上传文件的前缀，是否包含日期（yyyyMMdd）
     *
     * 目的：按照日期，进行分目录
     */
    static boolean PATH_PREFIX_DATE_ENABLE = true;
    /**
     * 上传文件的后缀，是否包含时间戳
     *
     * 目的：保证文件的唯一性，避免覆盖
     * 定制：可按需调整成 UUID、或者其他方式
     */
    static boolean PATH_SUFFIX_TIMESTAMP_ENABLE = true;

    @Resource
    private FileConfigService fileConfigService;

    @Resource
    private FileMapper fileMapper;

    @Value("${file.upload-dir}")
    private String fileUploadDir;

    @Override
    public PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO) {
        return fileMapper.selectPage(pageReqVO);
    }

    @Override
    @SneakyThrows
    public String createFile(byte[] content, String name, String directory, String type, String restricted) {
        // 1.1 处理 type 为空的情况
        if (StrUtil.isEmpty(type)) {
            type = FileTypeUtils.getMineType(content, name);
        }
        // 1.2 处理 name 为空的情况
        if (StrUtil.isEmpty(name)) {
            name = DigestUtil.sha256Hex(content);
        }
        if (StrUtil.isEmpty(FileUtil.extName(name))) {
            // 如果 name 没有后缀 type，则补充后缀
            String extension = FileTypeUtils.getExtension(type);
            if (StrUtil.isNotEmpty(extension)) {
                name = name + extension;
            }
        }

        // 2.1 生成上传的 path，需要保证唯一
        String path = generateUploadPath(content, name);
        String filePath = fileUploadDir + path;
        FileUtil.writeBytes(content, filePath);
        String url = "";
        if (StringUtils.isBlank( restricted)) {
            url = StrUtil.format("/files/{}", path);
        }else {
            url = StrUtil.format("/files/no/{}", path);
        }
        log.info("📂 文件上传信息如下：");
        log.info("Name：       {}", name);
        log.info("Path：       {}", path);
        log.info("Url：        {}", url);
        log.info("Type：       {}", convertMimeToExtension(type));
        log.info("Size：       {}", formatFileSize(content.length));
        return url;
    }
    /**
     * 生成文件路径
     *
     * @param content      文件内容
     * @param originalName 原始文件名
     * @return path，唯一不可重复
     */
    public static String generatePath(byte[] content, String originalName) {
        String sha256Hex = DigestUtil.sha256Hex(content);
        // 随机字符串生成
        String randomSuffix = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
        // 情况一：如果存在 name，则优先使用 name 的后缀
        if (StrUtil.isNotBlank(originalName)) {
            String extName = FileNameUtil.extName(originalName);
            return StrUtil.isBlank(extName) ? sha256Hex : sha256Hex + "-" + randomSuffix + "." + extName;
        }
        // 情况二：基于 content 计算
        String fileType = FileTypeUtil.getType(new ByteArrayInputStream(content));
        return sha256Hex + "-" + randomSuffix + "." + fileType;
    }
    @Override
    @SneakyThrows
    public Map<String, Object> getFile(Long businessId, String jobTypeCode, String name, String path, byte[] content,String restricted) {
        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);
        if (StrUtil.isEmpty(path)) {
            path = generatePath(content, name);
        }
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }

        path = generateUploadPath(content, name);

        String filePath = fileUploadDir + path;
        FileUtil.writeBytes(content, filePath);
        String url = "";
        if (StringUtils.isBlank( restricted)) {
            url = StrUtil.format("/files/{}", path);
        }else {
            url = StrUtil.format("/restricted/files/{}", path);
        }
        log.info("📂 文件上传信息如下：");
        log.info("Name：       {}", name);
        log.info("Path：       {}", path);
        log.info("Url：        {}", url);
        log.info("Type：       {}", convertMimeToExtension(type));
        log.info("Size：       {}", formatFileSize(content.length));
        Map<String, Object> map = new HashMap<>();
        map.put("url", url);
        map.put("type", convertMimeToExtension(type));
        map.put("size", formatFileSize(content.length));
        map.put("name", name);
        map.put("path", path);
        return map;
    }

//    @Override
//    @SneakyThrows
//    public String createFile(byte[] content, String name, String directory, String type) {
//        // 1.1 处理 type 为空的情况
//        if (StrUtil.isEmpty(type)) {
//            type = FileTypeUtils.getMineType(content, name);
//        }
//        // 1.2 处理 name 为空的情况
//        if (StrUtil.isEmpty(name)) {
//            name = DigestUtil.sha256Hex(content);
//        }
//        if (StrUtil.isEmpty(FileUtil.extName(name))) {
//            // 如果 name 没有后缀 type，则补充后缀
//            String extension = FileTypeUtils.getExtension(type);
//            if (StrUtil.isNotEmpty(extension)) {
//                name = name + extension;
//            }
//        }
//
//        // 2.1 生成上传的 path，需要保证唯一
//        String path = generateUploadPath(name, directory);
//        // 2.2 上传到文件存储器
//        FileClient client = fileConfigService.getMasterFileClient();
//        Assert.notNull(client, "客户端(master) 不能为空");
//        String url = client.upload(content, path, type);
//
//        // 3. 保存到数据库
//        fileMapper.insert(new FileDO().setConfigId(client.getId())
//                .setName(name).setPath(path).setUrl(url)
//                .setType(type).setSize(content.length));
//        return url;
//    }

    public static String formatFileSize(long sizeInBytes) {
        double kb = sizeInBytes / 1024.0;
        if (kb < 1024) {
            return String.format("%.2f KB", kb);
        } else {
            double mb = kb / 1024.0;
            return String.format("%.2f MB", mb);
        }
    }

    public static String convertMimeToExtension(String mimeType) {
        Map<String, String> mimeMap = new HashMap<>();
        mimeMap.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx");
        mimeMap.put("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx");
        mimeMap.put("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx");
        mimeMap.put("application/pdf", "pdf");
        mimeMap.put("image/jpeg", "jpg");
        mimeMap.put("image/png", "png");
        mimeMap.put("text/plain", "txt");
        // 其他类型你可以继续添加

        return mimeMap.getOrDefault(mimeType, mimeType); // 如果找不到就原样返回
    }



    @VisibleForTesting
    String generateUploadPath(String name, String directory) {
        // 1. 生成前缀、后缀
        String prefix = null;
        if (PATH_PREFIX_DATE_ENABLE) {
            prefix = LocalDateTimeUtil.format(LocalDateTimeUtil.now(), PURE_DATE_PATTERN);
        }
        String suffix = null;
        if (PATH_SUFFIX_TIMESTAMP_ENABLE) {
            suffix = String.valueOf(System.currentTimeMillis());
        }

        // 2.1 先拼接 suffix 后缀
        if (StrUtil.isNotEmpty(suffix)) {
            String ext = FileUtil.extName(name);
            if (StrUtil.isNotEmpty(ext)) {
                name = FileUtil.mainName(name) + StrUtil.C_UNDERLINE + suffix + StrUtil.DOT + ext;
            } else {
                name = name + StrUtil.C_UNDERLINE + suffix;
            }
        }
        // 2.2 再拼接 prefix 前缀
        if (StrUtil.isNotEmpty(prefix)) {
            name = prefix + StrUtil.SLASH + name;
        }
        // 2.3 最后拼接 directory 目录
        if (StrUtil.isNotEmpty(directory)) {
            name = directory + StrUtil.SLASH + name;
        }
        return name;
    }

    public String generateUploadPath(byte[] content, String originalName) {
        // 1. 生成文件内容的 sha256 作为唯一标识
        String fileHash = DigestUtil.sha256Hex(content);

        // 2. 生成短随机字符串，防止重复
        String randomSuffix = IdUtil.fastSimpleUUID().substring(0, 8);

        // 3. 获取文件扩展名
        String ext = FileUtil.extName(originalName);
        String extension = StrUtil.isNotEmpty(ext) ? "." + ext : "";

        // 4. 拼接最终文件名：{hash}-{random}.{ext}
        String fileName = fileHash + "-" + randomSuffix + extension;

        return fileName; // 直接作为相对路径存储，无目录结构
    }


    @Override
    @SneakyThrows
    public FilePresignedUrlRespVO getFilePresignedUrl(String name, String directory) {
        // 1. 生成上传的 path，需要保证唯一
        String path = generateUploadPath(name, directory);

        // 2. 获取文件预签名地址
        FileClient fileClient = fileConfigService.getMasterFileClient();
        FilePresignedUrlRespDTO presignedObjectUrl = fileClient.getPresignedObjectUrl(path);
        return BeanUtils.toBean(presignedObjectUrl, FilePresignedUrlRespVO.class,
                object -> object.setConfigId(fileClient.getId()).setPath(path));
    }

    @Override
    public Long createFile(FileCreateReqVO createReqVO) {
        FileDO file = BeanUtils.toBean(createReqVO, FileDO.class);
        fileMapper.insert(file);
        return file.getId();
    }

    @Override
    public void deleteFile(Long id) throws Exception {
        // 校验存在
        FileDO file = validateFileExists(id);

        // 从文件存储器中删除
        FileClient client = fileConfigService.getFileClient(file.getConfigId());
        Assert.notNull(client, "客户端({}) 不能为空", file.getConfigId());
        client.delete(file.getPath());

        // 删除记录
        fileMapper.deleteById(id);
    }

    private FileDO validateFileExists(Long id) {
        FileDO fileDO = fileMapper.selectById(id);
        if (fileDO == null) {
            throw exception(FILE_NOT_EXISTS);
        }
        return fileDO;
    }

    @Override
    public byte[] getFileContent(Long configId, String path) throws Exception {
        FileClient client = fileConfigService.getFileClient(configId);
        Assert.notNull(client, "客户端({}) 不能为空", configId);
        return client.getContent(path);
    }

}
