package org.springblade.system.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import jakarta.annotation.Resource;
import jakarta.validation.ConstraintViolation;
import org.springblade.common.utils.sql.SqlUtil;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.system.domain.FilesClassifyNameStandardDto;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springblade.common.annotation.SysLog;
import org.springblade.common.core.controller.BaseController;
import org.springblade.common.core.domain.AjaxResult;
import org.springblade.common.core.domain.R;
import org.springblade.system.domain.ProjectDto;
import org.springblade.common.enums.OperaType;
import org.springblade.common.utils.bean.FileDto;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.system.domain.DaKyProFilesContentsDto;
import org.springblade.system.domain.RectificationDto;
import org.springblade.system.domain.vo.DaKyProFilesContentsVo;
import org.springblade.system.mapper.FileManageMapper;
import org.springblade.system.service.FileManageService;
import org.springblade.system.service.ProjectService;
import org.springblade.system.util.WebFileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springblade.common.annotation.RequiresPermissions;
import org.springblade.common.core.page.TableDataInfo;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author：liang.chao
 * @Date：2025/9/11 - 13:29
 * 档案管理
 */
@RestController
@RequestMapping("/fileManage")
@Slf4j
@NonDS
public class FileManagementController extends BaseController {

    @Autowired
    private FileManageService fileManageService;

    @Autowired
    private ProjectService service;

    @Resource
    private FileManageMapper fileManageMapper;

    @Resource
    private WebFileUtils webFileUtils;

    @Value("${bonus.profile}")
    private String uploadDir;

    @PostMapping("getProjectList")
    @SysLog(title = "档案管理列表", module = "档案管理->项目管理", businessType = OperaType.QUERY, details = "档案管理列表", logType = 1)
    @RequiresPermissions("file:manage:list")
    public TableDataInfo getProjectList(@RequestBody ProjectDto dto) {
        try {
			// 直接使用 dto 中的分页参数
			PageHelper.startPage(dto.getPageNum() != null ? dto.getPageNum() : 1,
					dto.getPageSize() != null ? dto.getPageSize() : 10,
					SqlUtil.escapeOrderBySql(dto.getOrderBy()))
				.setReasonable(dto.getReasonable() != null ? dto.getReasonable() : true);
            List<ProjectDto> list = service.list(dto);
            return getDataTable(list);
        } catch (Exception e) {
            log.error(e.toString(), e);
            return getDataTable(new ArrayList<>());
        }
    }
    @PostMapping("getProjectNameById")
    public AjaxResult getProjectNameById(@RequestBody ProjectDto dto) {
        try {
            return AjaxResult.success(service.getProjectNameById(dto));
        } catch (Exception e) {
            log.error(e.toString(), e);
            return AjaxResult.error("请求出错了");
        }
    }

    @PostMapping("getFileManageTree")
    @SysLog(title = "档案管理树", module = "档案管理->档案左侧树", businessType = OperaType.QUERY, details = "档案管理树", logType = 1)
    @RequiresPermissions("file:manage:tree")
    public R<List<DaKyProFilesContentsDto>> getFileManageTree(@RequestBody DaKyProFilesContentsDto dto) {
        try {
            List<DaKyProFilesContentsDto> list = fileManageService.list(dto);
            return R.ok(list);
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail("请求出错了");
        }
    }

    @PostMapping("getFileManageTreeByAddOrUpdate")
    @SysLog(title = "档案管理树", module = "档案管理->档案左侧树", businessType = OperaType.QUERY, details = "档案管理树", logType = 1)
    @RequiresPermissions("file:manage:tree")
    public R<List<DaKyProFilesContentsDto>> getFileManageTreeByAddOrUpdate(@RequestBody DaKyProFilesContentsDto dto) {
        try {
            List<DaKyProFilesContentsDto> list = fileManageService.getFileManageTree(dto);
            return R.ok(list);
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail("请求出错了");
        }
    }


    @PostMapping("getFileManage")
    @SysLog(title = "第四级档案信息", module = "档案管理->档案右侧列表", businessType = OperaType.QUERY, details = "第四级档案信息", logType = 1)
    @RequiresPermissions("file:manage:query")
    public TableDataInfo getFileManage(@RequestBody DaKyProFilesContentsDto dto) {
        if (dto.getParentId().equals("0")) {
            return getDataTable(new ArrayList<>());
        } else {
            Integer level = fileManageMapper.getLevelById(dto);
            if (level == 4) {
				// 直接使用 dto 中的分页参数
				PageHelper.startPage(dto.getPageNum() != null ? dto.getPageNum() : 1,
						dto.getPageSize() != null ? dto.getPageSize() : 10,
						SqlUtil.escapeOrderBySql(dto.getOrderBy()))
					.setReasonable(dto.getReasonable() != null ? dto.getReasonable() : true);
                List<DaKyProFilesContentsDto> list = fileManageService.FileManage(dto);
                return getDataTable(list);
            } else {
                return getDataTable(new ArrayList<>());
            }
        }
    }

    @PostMapping("getFileManageById")
    @SysLog(title = "获取当前节点数据", module = "档案管理->档案右侧列表", businessType = OperaType.QUERY, details = "获取当前节点数据", logType = 1)
    @RequiresPermissions("file:manage:query")
    public AjaxResult getFileManageById(@RequestBody DaKyProFilesContentsDto dto) {
        return AjaxResult.success(fileManageService.getFileManageById(dto));
    }
    @PostMapping("addRectification")
    @SysLog(title = "加入整改清单", module = "档案管理->档案右侧列表", businessType = OperaType.QUERY, details = "加入整改清单", logType = 1)
    @RequiresPermissions("file:manage:rectification")
    public AjaxResult addRectification(@RequestBody @Validated RectificationDto dto) {
        return fileManageService.addRectification(dto);
    }
    @PostMapping("updateRectification")
    @SysLog(title = "修改整改清单", module = "档案管理->档案右侧列表", businessType = OperaType.QUERY, details = "修改整改清单", logType = 1)
    @RequiresPermissions("file:manage:rectification")
    public AjaxResult updateRectification(@RequestBody @Validated RectificationDto dto) {
        return fileManageService.updateRectification(dto);
    }
    @PostMapping("delRectification")
    @SysLog(title = "删除整改清单", module = "档案管理->档案右侧列表", businessType = OperaType.QUERY, details = "删除整改清单", logType = 1)
    @RequiresPermissions("file:manage:rectification")
    public AjaxResult delRectification(@RequestBody RectificationDto dto) {
        return fileManageService.delRectification(dto);
    }
    @PostMapping("issue")
    @SysLog(title = "整改清单->整改下发", module = "档案管理->档案右侧列表", businessType = OperaType.QUERY, details = "整改清单->整改下发", logType = 1)
    @RequiresPermissions("file:manage:issue")
    public AjaxResult issue(@RequestBody RectificationDto dto) {
        return fileManageService.issue(dto);
    }
    @PostMapping("selectRectificationList")
    @SysLog(title = "整改清单列表", module = "档案管理->档案右侧列表", businessType = OperaType.QUERY, details = "整改清单列表", logType = 1)
    @RequiresPermissions("file:manage:rectification")
    public TableDataInfo selectRectificationList(@RequestBody RectificationDto dto) {
		// 直接使用 dto 中的分页参数
		PageHelper.startPage(dto.getPageNum() != null ? dto.getPageNum() : 1,
				dto.getPageSize() != null ? dto.getPageSize() : 10,
				SqlUtil.escapeOrderBySql(dto.getOrderBy()))
			.setReasonable(dto.getReasonable() != null ? dto.getReasonable() : true);
        return getDataTable(fileManageService.selectRectificationList(dto));
    }

    @PostMapping("addFileManageRight")
    @SysLog(title = "新增右侧档案树", module = "档案管理->档案目录管理", businessType = OperaType.INSERT, details = "新增右侧档案树", logType = 1)
    @RequiresPermissions("file:manage:add")
    public R saveArchivalCatalogue(@RequestPart("file") MultipartFile file,
                                   @RequestParam("params") String params) {
        try {
            // 手动解析JSON
            ObjectMapper objectMapper = new ObjectMapper();
            DaKyProFilesContentsVo dto = objectMapper.readValue(params, DaKyProFilesContentsVo.class);

            // 手动验证
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            Validator validator = factory.getValidator();
            Set<ConstraintViolation<DaKyProFilesContentsVo>> violations = validator.validate(dto);

            if (!violations.isEmpty()) {
                // 处理验证错误
                List<String> errors = violations.stream()
                        .map(ConstraintViolation::getMessage)
                        .collect(Collectors.toList());
                return R.fail(400, "参数验证失败: " + String.join(", ", errors));
            }
            // 查询档案名称是否重复
            Integer i = fileManageMapper.selectFileManage(dto);
            if (i > 0) {
                return R.fail("档案名称重复");
            }
            if (file != null && !file.isEmpty()) {
                // 判断是否包含文件命名规范
                List<FilesClassifyNameStandardDto> list = fileManageMapper.getFilesClassifyNameStandard();
                for (FilesClassifyNameStandardDto s : list) {
                    if (s.getStandardType().equals("1") && !file.getOriginalFilename().contains(s.getStandardName())) {
                        return R.fail("文件命名需包含：" + s.getStandardName());
                    } else if (s.getStandardType().equals("0") && file.getOriginalFilename().contains(s.getStandardName())) {
                        return R.fail("文件命名不能包含：" + s.getStandardName());
                    }
                }
                FileDto upload = webFileUtils.upload(file);
                if (upload.getErrMessage() != null) {
                    return R.fail(upload.getErrMessage());
                }
                dto.setFilePath(upload.getFilePath());
                dto.setFileSize(upload.getFileSize());
                dto.setFileName(upload.getFileName());
                dto.setSourceFileName(upload.getSourceFileName());
                dto.setFileType(upload.getFileType());
                dto.setSuffixName(upload.getSuffixName());
                dto.setSourceType("1");

                dto.setUpdateUserId(AuthUtil.getUser().getUserId());
                dto.setUpdateUserName(AuthUtil.getUser().getUserName());
                dto.setCreateUserId(getLoginUser().getUserId());
                dto.setCreateUserName(AuthUtil.getUser().getUserName());
                dto.setDataSource("1");
                dto.setIsUnique("1");
            } else if (file == null || file.isEmpty()) {
                return R.fail("请上传文件");
            }
            R r = fileManageService.saveFileManage(dto);
            dto.setBusinessId(dto.getId());
            fileManageMapper.saveFileSource(dto);
            return r;
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail(e.toString());
        }
    }

    @PostMapping("updateFileManageRight")
    @SysLog(title = "修改档案", module = "档案管理->档案目录管理", businessType = OperaType.UPDATE, details = "修改档案", logType = 1)
    @RequiresPermissions("file:manage:update")
    public R updateFileManage(@RequestPart(value = "file", required = false) MultipartFile file,
                              @RequestParam("params") String params) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            DaKyProFilesContentsVo dto = objectMapper.readValue(params, DaKyProFilesContentsVo.class);

            // 手动验证
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            Validator validator = factory.getValidator();
            Set<ConstraintViolation<DaKyProFilesContentsVo>> violations = validator.validate(dto);

            if (!violations.isEmpty()) {
                // 处理验证错误
                List<String> errors = violations.stream()
                        .map(ConstraintViolation::getMessage)
                        .collect(Collectors.toList());
                return R.fail(400, "参数验证失败: " + String.join(", ", errors));
            }
            dto.setUpdateUserId(getLoginUser().getUserId());
            dto.setUpdateUserName(AuthUtil.getUser().getUserName());
            Integer i = fileManageMapper.selectFileManage(dto);
            if (i > 0) {
                return R.fail("档案名称重复");
            }
            if (file != null && !file.isEmpty()) {
                // 判断是否包含文件命名规范
                List<FilesClassifyNameStandardDto> list = fileManageMapper.getFilesClassifyNameStandard();
                for (FilesClassifyNameStandardDto s : list) {
                    if (s.getStandardType().equals("1") && !file.getOriginalFilename().contains(s.getStandardName())) {
                        return R.fail("文件命名需包含：" + s.getStandardName());
                    } else if (s.getStandardType().equals("0") && file.getOriginalFilename().contains(s.getStandardName())) {
                        return R.fail("文件命名不能包含：" + s.getStandardName());
                    }
                }
                FileDto upload = webFileUtils.upload(file);
                if (upload.getErrMessage() != null) {
                    return R.fail(upload.getErrMessage());
                }
                dto.setFilePath(upload.getFilePath());
                dto.setFileSize(upload.getFileSize());
                dto.setFileName(upload.getFileName());
                dto.setSourceFileName(upload.getSourceFileName());
                dto.setFileType(upload.getFileType());
                dto.setSuffixName(upload.getSuffixName());
                dto.setSourceType("1");
                fileManageMapper.updateFileSource(dto);
            }
            return fileManageService.updateFileManage(dto);
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail(e.toString());
        }
    }

    @PostMapping("addFileManageLeft")
    @SysLog(title = "新增左侧档案列表", module = "档案管理->档案目录管理", businessType = OperaType.INSERT, details = "新增左侧档案列表", logType = 1)
    @RequiresPermissions("file:manage:add")
    public R saveArchivalCatalogue(@RequestBody @Validated DaKyProFilesContentsDto dto) {
        try {
            Integer num = fileManageService.getMaxSort(dto);
            if (num == null) {
                num = 0;
            }
            if (dto.getSort() <= num) {
                return R.fail("排序序号需大于" + num);
            }
            return fileManageService.saveFileManage(dto);
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail("请求出错了");
        }
    }

    @PostMapping("updateFileManageLeft")
    @SysLog(title = "修改档案", module = "档案管理->档案目录管理", businessType = OperaType.UPDATE, details = "修改档案", logType = 1)
    @RequiresPermissions("file:manage:update")
    public R updateFileManage(@RequestBody @Validated DaKyProFilesContentsDto dto) {
        try {
            Integer child = fileManageMapper.getchild(dto);
            if (child > 0){
                return R.fail("该节点下存在子节点或该节点下存在档案文件，无法修改");
            }
            Integer num = fileManageService.getMaxSort(dto);
            if (num == null) {
                num = 0;
            }
            int sort = fileManageService.getSortById(dto.getId());
            if (sort != dto.getSort() && dto.getSort() <= num) {
                return R.fail("排序序号需大于" + num);
            }
            return fileManageService.updateFileManage(dto);
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail("请求出错了");
        }
    }

    @PostMapping("delFileManage")
    @SysLog(title = "删除档案", module = "档案管理->档案目录管理", businessType = OperaType.DELETE, details = "删除档案", logType = 1)
    @RequiresPermissions("file:manage:del")
    public R delFileManage(@RequestBody DaKyProFilesContentsDto dto) {
        try {
            Integer child = fileManageMapper.getchild(dto);
            if (child > 0){
                return R.fail("该节点下存在子节点或该节点下存在档案文件，无法删除");
            }
            Integer i = fileManageService.delFileManage(dto);
            if (i > 0) {
                return R.ok();
            } else {
                return R.fail("删除失败");
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail("请求出错了");
        }
    }

    @PostMapping("updateIntegrityStatus")
    @SysLog(title = "档案移交确认", module = "档案管理->档案目录管理", businessType = OperaType.DELETE, details = "档案移交确认", logType = 1)
    @RequiresPermissions("file:manage:confirm")
    public R updateIntegrityStatus(@RequestBody DaKyProFilesContentsDto dto) {
        try {
			// 该工程下若存在整改文件，则不能进行移交
			if (fileManageMapper.getRectificationFile(dto) > 0) {
				return R.fail("该工程下存在整改文件，请先处理整改文件");
			}
            Integer i = fileManageService.updateIntegrityStatus(dto);
            if (i > 0) {
                return R.ok();
            } else {
                return R.fail("移交失败");
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
            return R.fail("请求出错了");
        }
    }

    /**
     * 档案同步(这里先不做，返回确认即可)
     */
    @PostMapping("fileExtract")
    @SysLog(title = "档案同步", module = "档案管理->档案目录管理", businessType = OperaType.DELETE, details = "档案抽取", logType = 1)
    @RequiresPermissions("file:manage:extract")
    public R fileExtract(@RequestBody DaKyProFilesContentsDto dto) {
        return R.ok();
    }

    @PostMapping("getMaxSort")
    @SysLog(title = "获取最大排序号", module = "档案管理->档案目录管理", businessType = OperaType.QUERY, details = "获取最大排序号", logType = 1)
    public R getMaxSort(@RequestBody DaKyProFilesContentsDto dto) {
        Integer num = fileManageService.getMaxSort(dto);
        if (num == null) {
            num = 0;
        }
        return R.ok(num);
    }

    @PostMapping("/getFileAsBase64")
    public R getFileAsBase64(@RequestBody DaKyProFilesContentsDto dto) {
        Map<String, Object> response = new HashMap<>();
        DaKyProFilesContentsVo record = fileManageService.getFileById(Long.parseLong(dto.getId()));

        if (record == null || StringUtils.isBlank(record.getFilePath())) {
            return R.fail("文件未找到");
        }

        // 直接使用字符串处理构建安全路径
        String safeFullPath = buildSecureFullPath(uploadDir, record.getFilePath());
        if (safeFullPath == null) {
            return R.fail("无效的文件路径");
        }

        File file = new File(safeFullPath);
        if (!file.exists() || !file.isFile()) {
            return R.fail("文件未找到");
        }

        try {
            byte[] fileBytes = Files.readAllBytes(file.toPath());
            String base64String = Base64.getEncoder().encodeToString(fileBytes);

            response.put("fileName", record.getFileName());
            response.put("suffix_name", record.getSuffixName());
            response.put("fileBase64", base64String);
            return R.ok(response);
        } catch (IOException e) {
            return R.fail("文件读取失败");
        }
    }

    /**
     * 构建安全的完整文件路径
     */
    private String buildSecureFullPath(String baseDir, String filePath) {
        if (StringUtils.isBlank(filePath)) {
            return null;
        }

        // 统一路径分隔符
        String unifiedPath = filePath.replace('\\', '/');

        // 移除开头的斜杠
        unifiedPath = unifiedPath.replaceAll("^/+", "");

        // 检查路径遍历
        if (unifiedPath.contains("../") || unifiedPath.contains("..\\")) {
            return null;
        }

        // 检查绝对路径
        if (unifiedPath.matches("^[a-zA-Z]:/.*") || unifiedPath.startsWith("/")) {
            return null;
        }

        // 构建完整路径
        String fullPath;
        if (baseDir.endsWith(File.separator)) {
            fullPath = baseDir + unifiedPath;
        } else {
            fullPath = baseDir + File.separator + unifiedPath;
        }

        // 使用File的getCanonicalPath进行最终验证
        try {
            File canonicalFile = new File(fullPath);
            String canonicalPath = canonicalFile.getCanonicalPath();

            // 验证是否仍在基础目录内
            String canonicalBaseDir = new File(baseDir).getCanonicalPath();
            if (!canonicalPath.startsWith(canonicalBaseDir)) {
                return null;
            }

            return canonicalPath;
        } catch (IOException e) {
            return null;
        }
    }
}
