package com.emoon.filedownloader.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.emoon.common.core.domain.R;
import com.emoon.common.core.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * 文件下载控制器
 *
 * @author destiny
 * @description: 提供文件base64编码下载到服务器和删除文件的功能
 * @date 2025/12/20 21:29
 */
@Slf4j
@RestController
@RequestMapping("/api/file")
public class DownloadController {

    /**
     * 文件存储根目录，配置在application.yml中
     */
    @Value("${file.download.path:/tmp/downloads}")
    private String basePath;

    /**
     * 下载文件到服务器
     * 接收文件的base64编码，保存到指定目录并返回文件路径
     *
     * @param requestData 请求数据，包含base64编码的文件内容和文件名
     * @return 文件在服务器上的完整路径
     */
    @PostMapping("/download")
    public R<Map<String, String>> downloadFile(@RequestBody Map<String, String> requestData) {
        try {
            String base64Content = requestData.get("content");
            String fileName = requestData.get("fileName");

            // 参数校验
            if (StringUtils.isEmpty(base64Content)) {
                return R.fail("文件内容不能为空");
            }
            if (StringUtils.isEmpty(fileName)) {
                return R.fail("文件名不能为空");
            }

            // 安全性检查：防止路径遍历攻击
            if (fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) {
                return R.fail("文件名包含非法字符");
            }

            // 创建存储目录
            File baseDir = new File(basePath);
            if (!baseDir.exists()) {
                baseDir.mkdirs();
            }

            // 生成唯一文件名（防止文件名冲突）
            String fileExtension = FileUtil.extName(fileName);
            String baseFileName = FileUtil.mainName(fileName);
            String uniqueFileName = baseFileName + "_" + IdUtil.simpleUUID() + "." + fileExtension;

            // 构建完整的文件路径
            Path filePath = Paths.get(basePath, uniqueFileName);

            // 解码base64并写入文件
            String cleanBase64 = base64Content.replaceAll("^data:[^;]+;base64,", "");
            byte[] fileBytes = Base64.getDecoder().decode(cleanBase64);
            Files.write(filePath, fileBytes);

            // 计算文件MD5（用于后续校验）
            String fileMd5 = DigestUtil.md5Hex(fileBytes);

            // 构建返回结果
            Map<String, String> result = new HashMap<>();
            result.put("fileName", uniqueFileName);
            result.put("filePath", filePath.toString());
            result.put("originalFileName", fileName);
            result.put("fileSize", String.valueOf(fileBytes.length));
            result.put("md5", fileMd5);

            log.info("文件下载成功: 原文件名={}, 存储路径={}, 大小={}bytes", fileName, filePath, fileBytes.length);

            return R.ok("文件下载成功", result);

        } catch (IOException e) {
            log.error("文件下载失败", e);
            return R.fail("文件下载失败：" + e.getMessage());
        } catch (IllegalArgumentException e) {
            log.error("base64解码失败", e);
            return R.fail("文件内容格式错误，请检查base64编码");
        } catch (Exception e) {
            log.error("系统异常", e);
            return R.fail("系统异常，请稍后重试");
        }
    }

    /**
     * 删除服务器上的文件
     * 只能删除配置的basePath下的文件，防止误删
     *
     * @param requestData 请求数据，包含要删除的文件名
     * @return 删除结果
     */
    @DeleteMapping("/delete")
    public R<String> deleteFile(@RequestBody Map<String, String> requestData) {
        try {
            String fileName = requestData.get("fileName");

            // 参数校验
            if (StringUtils.isEmpty(fileName)) {
                return R.fail("文件名不能为空");
            }

            // 安全性检查：防止路径遍历攻击
            if (fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) {
                return R.fail("文件名包含非法字符");
            }

            // 构建文件路径（确保只能删除basePath下的文件）
            Path filePath = Paths.get(basePath, fileName);
            File file = filePath.toFile();

            // 检查文件是否存在
            if (!file.exists()) {
                return R.fail("文件不存在");
            }

            // 检查文件是否在允许的目录下（双重安全保障）
            String canonicalBasePath = new File(basePath).getCanonicalPath();
            String canonicalFilePath = file.getCanonicalPath();
            if (!canonicalFilePath.startsWith(canonicalBasePath)) {
                log.warn("尝试删除不在允许目录下的文件: {}", canonicalFilePath);
                return R.fail("无权限删除该文件");
            }

            // 删除文件
            boolean deleted = FileUtil.del(file);
            if (deleted) {
                log.info("文件删除成功: {}", filePath);
                return R.ok("文件删除成功");
            } else {
                log.error("文件删除失败: {}", filePath);
                return R.fail("文件删除失败");
            }

        } catch (IOException e) {
            log.error("删除文件时发生IO异常", e);
            return R.fail("删除文件失败：" + e.getMessage());
        } catch (Exception e) {
            log.error("系统异常", e);
            return R.fail("系统异常，请稍后重试");
        }
    }

    /**
     * 获取文件信息（可选功能，用于检查文件是否存在）
     *
     * @param fileName 文件名
     * @return 文件信息
     */
    @GetMapping("/info/{fileName}")
    public R<Map<String, Object>> getFileInfo(@PathVariable String fileName) {
        try {
            // 安全性检查
            if (fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) {
                return R.fail("文件名包含非法字符");
            }

            Path filePath = Paths.get(basePath, fileName);
            File file = filePath.toFile();

            if (!file.exists()) {
                return R.fail("文件不存在");
            }

            // 检查文件是否在允许的目录下
            String canonicalBasePath = new File(basePath).getCanonicalPath();
            String canonicalFilePath = file.getCanonicalPath();
            if (!canonicalFilePath.startsWith(canonicalBasePath)) {
                return R.fail("无权限访问该文件");
            }

            // 构建文件信息
            Map<String, Object> fileInfo = new HashMap<>();
            fileInfo.put("fileName", fileName);
            fileInfo.put("filePath", filePath.toString());
            fileInfo.put("fileSize", file.length());
            fileInfo.put("lastModified", file.lastModified());
            fileInfo.put("exists", file.exists());

            return R.ok("获取文件信息成功", fileInfo);

        } catch (IOException e) {
            log.error("获取文件信息失败", e);
            return R.fail("获取文件信息失败：" + e.getMessage());
        }
    }
}
