package com.vctgo.file.controller;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.base.Splitter;
import com.vctgo.common.core.constant.RoleConstants;
import com.vctgo.common.core.web.controller.BaseController;
import com.vctgo.common.core.web.domain.AjaxResult;
import com.vctgo.common.mybatisplus.aop.TemporaryIgnoreTables;
import com.vctgo.common.security.annotation.Logical;
import com.vctgo.common.security.annotation.RequiresPermissions;
import com.vctgo.common.security.utils.SecurityUtils;
import com.vctgo.file.domain.FileReUserOperate;
import com.vctgo.file.domain.FileShare;
import com.vctgo.file.mapper.FileReUserOperateMapper;
import com.vctgo.file.service.ICloudStorageFileService;
import com.vctgo.file.service.IFileInfoService;
import com.vctgo.file.service.IFileReUserOperateService;
import com.vctgo.file.utils.ConstantUtils;
import com.vctgo.system.api.domain.FileInfo;
import com.vctgo.system.api.domain.SysRole;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 云盘核心业务逻辑
 *
 * @author vctgo
 */
@Api(tags = "云盘核心业务")
@RestController
@RequestMapping("/cloudStorage")
public class CloudStorageController extends BaseController {

    @Autowired
    private ICloudStorageFileService cloudDiskFileService;
    @Autowired
    private IFileInfoService fileInfoService;
    @Autowired
    private IFileReUserOperateService fileReUserOperateService;
    @Autowired
    private FileReUserOperateMapper fileReUserOperateMapper;

    /**
     * 分页--云盘获取列表
     */
    @ApiOperation(value = "分页--云盘获取列表")
    @Transactional
    @GetMapping("/page")
    @RequiresPermissions("cloud:storage:info:page")
    public AjaxResult pageFileInfo(FileInfo fileInfo) {
        if (SecurityUtils.getTenantId() != 9999L) {
            fileInfo.setTenantId(SecurityUtils.getTenantId());
        }
        return AjaxResult.success(cloudDiskFileService.pageFileInfo(fileInfo));
    }

    /**
     * 分页--忽略租户的云盘获取列表
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "分页--忽略租户的云盘获取列表")
    @Transactional
    @GetMapping("/inner/page/neglect/tenant")
    public AjaxResult innerPageFileInfoNeglectTenant(String fileInfo) {
        return AjaxResult.success(JSONObject.toJSONString(cloudDiskFileService.pageFileInfo(JSONObject.parseObject(fileInfo, FileInfo.class))));
    }

    /**
     * 不分页--通过文件Id获取文件列表信息
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "不分页--通过文件Id获取文件列表信息")
    @Transactional
    @GetMapping("/inner/list/file")
    public AjaxResult innerListFile(String fileInfo) {
        return AjaxResult.success(JSONObject.toJSONString(cloudDiskFileService.innerListFile(JSONObject.parseObject(fileInfo, FileInfo.class))));
    }

    /**
     * 不分页--云盘获取列表
     */
    @ApiOperation(value = "不分页--云盘获取列表")
    @Transactional
    @GetMapping("/list")
    @RequiresPermissions("cloud:storage:info:list")
    public AjaxResult listFileInfo(FileInfo fileInfo) {
        return cloudDiskFileService.listFileInfo(fileInfo);
    }

    /**
     * 云盘文件上传请求
     */
    @ApiOperation(value = "云盘文件上传请求")
    @Transactional
    @PostMapping("/upload")
    @RequiresPermissions(value = {"cloud:storage:info:upload","profile:file:info:upload"})
    public AjaxResult upload(MultipartFile file) {
        return cloudDiskFileService.upload(file);
    }

    /**
     * 通过租户Id和文件夹Id在云盘文件上传文件
     *
     * @param tenantId    租户id
     * @param directoryId 文件夹id
     * @param file        文件
     * @return
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "通过租户Id和文件夹Id在云盘文件上传文件")
    @Transactional
    @PostMapping("/upload/tenant/directory")
    @RequiresPermissions("cloud:storage:info:directory")
    public AjaxResult uploadByTenantIdAndDirectoryId(Long tenantId, Long directoryId, MultipartFile file) {
        return cloudDiskFileService.uploadByTenantIdAndDirectoryId(tenantId, directoryId, file);
    }

    /**
     * 预检-通过文件夹Id上传文件
     */
    @ApiOperation(value = "通过文件夹Id上传文件预检")
    @Transactional
    @GetMapping("/beforeCheck/upload/directory/{directoryId}")
    public AjaxResult beforeCheckUploadByDirectoryId(@PathVariable String directoryId) {
        // 非超级管理员、文件资源管理员，上传文件需要校验权限
        List<String> roleKeyList = SecurityUtils.getLoginUser().getSysRolesList().stream().map(SysRole::getRoleKey).collect(Collectors.toList());
        if (!roleKeyList.contains(RoleConstants.ROLE_KEY_ADMIN) && !roleKeyList.contains(RoleConstants.ROLE_KEY_FILE_RESOURCE_USER)) {
            long authCount1 = fileReUserOperateService.selectCountFileReUserOperate(new QueryWrapper<FileReUserOperate>().lambda()
                    .eq(FileReUserOperate::getTenantId, SecurityUtils.getTenantId())
                    .eq(FileReUserOperate::getFileId, directoryId)
                    .eq(FileReUserOperate::getUserId, SecurityUtils.getUserId())
                    .eq(FileReUserOperate::getIsDeleted, ConstantUtils.IS_DELETED_0));
            if (authCount1 == 0L) {
                //继承权限验证
                FileInfo fileInfo = fileInfoService.selectById(Long.valueOf(directoryId));
                List<String> ancestorsFileIdList = Splitter.on(",").splitToStream(fileInfo.getAncestors()).collect(Collectors.toList());
                Collections.reverse(ancestorsFileIdList);
                List<FileReUserOperate> fileReUserOperateRetainList = fileReUserOperateMapper.selectGroupPermissionListByUserIdAndFileId(ancestorsFileIdList, String.valueOf(SecurityUtils.getUserId()));
                for (String fileId : ancestorsFileIdList) {
                    Optional<FileReUserOperate> fileReUserOperateOptional = fileReUserOperateRetainList.stream().filter(fileReUserOperate -> fileId.equals(fileReUserOperate.getFileId().toString())).findFirst();
                    if (fileReUserOperateOptional.isPresent()) {
                        FileReUserOperate fileReUserOperate = fileReUserOperateOptional.get();
                        if (fileReUserOperate.getFileUserAuthCode().equals(ConstantUtils.FILE_AUTH_GROUP_AUTH_MANAGE) || fileReUserOperate.getFileUserAuthCode().equals(ConstantUtils.FILE_AUTH_GROUP_MANAGE) || fileReUserOperate.getFileUserAuthCode().equals(ConstantUtils.FILE_AUTH_GROUP_UP_DOWN)) {
                            return AjaxResult.success();
                        } else {
                            return AjaxResult.error("当前文件夹下尚未分配上传文件权限，请联系管理员分配后再试");
                        }
                    }
                }
            } else {
                List<String> fileUserAuthCodeList = new ArrayList<>();
                fileUserAuthCodeList.add(ConstantUtils.FILE_AUTH_GROUP_UP_DOWN);
                fileUserAuthCodeList.add(ConstantUtils.FILE_AUTH_GROUP_MANAGE);
                fileUserAuthCodeList.add(ConstantUtils.FILE_AUTH_GROUP_AUTH_MANAGE);
                long authCount2 = fileReUserOperateService.selectCountFileReUserOperate(new QueryWrapper<FileReUserOperate>().lambda()
                        .eq(FileReUserOperate::getTenantId, SecurityUtils.getTenantId())
                        .eq(FileReUserOperate::getFileId, directoryId)
                        .eq(FileReUserOperate::getUserId, SecurityUtils.getUserId())
                        .in(FileReUserOperate::getFileUserAuthCode, fileUserAuthCodeList)
                        .eq(FileReUserOperate::getIsDeleted, ConstantUtils.IS_DELETED_0));
                if (authCount2 == 0L) {
                    return AjaxResult.error("当前文件夹下尚未分配上传文件权限，请联系管理员分配后再试");
                }
            }
        }
        return AjaxResult.success();
    }

    /**
     * 通过文件夹Id传文件
     *
     * @param directoryId 文件夹id
     * @param file        文件
     * @param serviceName 服务名
     * @return
     */
    @ApiOperation(value = "通过文件夹Id在云盘文件上传文件")
    @Transactional
    @PostMapping("/upload/directory")
    @RequiresPermissions(value = {"cloud:storage:info:upload:directory", "process:discuss:add:file"}, logical = Logical.OR)
    public AjaxResult uploadByDirectoryId(Long directoryId, String serviceName, MultipartFile file) {
        return cloudDiskFileService.uploadByDirectoryId(directoryId, serviceName, file);
    }

    /**
     * 创建文件夹权限预检
     */
    @ApiOperation(value = "创建文件夹权限预检")
    @Transactional
    @PostMapping("/beforeCheck/mkdir")
    public AjaxResult beforeCheckMkdir(@RequestBody FileInfo reqFileInfo) {
        // 非超级管理员、文件资源管理员，不可创建FileParentId = 0 的文件，创建FileParentId != 0 的文件需要校验权限
        List<String> roleKeyList = SecurityUtils.getLoginUser().getSysRolesList().stream().map(SysRole::getRoleKey).collect(Collectors.toList());
        if (!roleKeyList.contains(RoleConstants.ROLE_KEY_ADMIN) && !roleKeyList.contains(RoleConstants.ROLE_KEY_FILE_RESOURCE_USER)) {
            if (reqFileInfo.getFileParentId() == 0) {
                return AjaxResult.error("当前文件夹下尚未分配新建文件夹权限，请联系管理员分配后再试");
            } else {
                FileInfo fileInfo = fileInfoService.selectById(reqFileInfo.getFileParentId());
                long authCount1 = fileReUserOperateService.selectCountFileReUserOperate(new QueryWrapper<FileReUserOperate>().lambda()
                        .eq(FileReUserOperate::getTenantId, SecurityUtils.getTenantId())
                        .eq(FileReUserOperate::getFileId, reqFileInfo.getFileParentId())
                        .eq(FileReUserOperate::getUserId, SecurityUtils.getUserId())
                        .eq(FileReUserOperate::getIsDeleted, ConstantUtils.IS_DELETED_0));
                if (authCount1 == 0L) {
                    //继承权限验证
                    List<String> ancestorsFileIdList = Splitter.on(",").splitToStream(fileInfo.getAncestors()).collect(Collectors.toList());
                    Collections.reverse(ancestorsFileIdList);
                    List<FileReUserOperate> fileReUserOperateRetainList = fileReUserOperateMapper.selectGroupPermissionListByUserIdAndFileId(ancestorsFileIdList, String.valueOf(SecurityUtils.getUserId()));
                    for (String fileId : ancestorsFileIdList) {
                        Optional<FileReUserOperate> fileReUserOperateOptional = fileReUserOperateRetainList.stream().filter(fileReUserOperate -> fileId.equals(fileReUserOperate.getFileId().toString())).findFirst();
                        if (fileReUserOperateOptional.isPresent()) {
                            FileReUserOperate fileReUserOperate = fileReUserOperateOptional.get();
                            if (fileReUserOperate.getFileUserAuthCode().equals(ConstantUtils.FILE_AUTH_GROUP_AUTH_MANAGE) || fileReUserOperate.getFileUserAuthCode().equals(ConstantUtils.FILE_AUTH_GROUP_MANAGE)) {
                                return AjaxResult.success();
                            } else {
                                return AjaxResult.error("当前文件夹下尚未分配新建文件夹权限，请联系管理员分配后再试");
                            }
                        }
                    }
                } else {
                    List<String> fileUserAuthCodeList = new ArrayList<>();
                    fileUserAuthCodeList.add(ConstantUtils.FILE_AUTH_GROUP_MANAGE);
                    fileUserAuthCodeList.add(ConstantUtils.FILE_AUTH_GROUP_AUTH_MANAGE);
                    long authCount2 = fileReUserOperateService.selectCountFileReUserOperate(new QueryWrapper<FileReUserOperate>().lambda()
                            .eq(FileReUserOperate::getTenantId, SecurityUtils.getTenantId())
                            .eq(FileReUserOperate::getFileId, reqFileInfo.getFileParentId())
                            .eq(FileReUserOperate::getUserId, SecurityUtils.getUserId())
                            .in(FileReUserOperate::getFileUserAuthCode, fileUserAuthCodeList)
                            .eq(FileReUserOperate::getIsDeleted, ConstantUtils.IS_DELETED_0));
                    if (authCount2 == 0L) {
                        return AjaxResult.error("当前文件夹下尚未分配新建文件夹权限，请联系管理员分配后再试");
                    }
                }
            }
        }
        return AjaxResult.success();
    }

    /**
     * 创建文件夹
     */
    @ApiOperation(value = "创建文件夹")
    @Transactional
    @PostMapping("/mkdir")
    @RequiresPermissions(value = {"cloud:storage:info:mkdir", "file:operate:auth"}, logical = Logical.OR)
    public AjaxResult mkdir(@RequestBody FileInfo reqFileInfo) {
        //同目录下文件夹名称不可重复，如文件夹名称重复提示“文件夹名称重复，保存失败”。
        List<FileInfo> fileInfoList = fileInfoService.selectListByFileInfo(FileInfo.builder()
                .fileParentId(reqFileInfo.getFileParentId())
                .fileName(reqFileInfo.getFileName())
                .tenantId(SecurityUtils.getTenantId())
                .serviceName(reqFileInfo.getServiceName())
                .fileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_1)
                .isDeleted(ConstantUtils.IS_DELETED_0)
                .fileIsHide(ConstantUtils.IS_DELETED_0).build());
        if (CollectionUtils.isNotEmpty(fileInfoList)) {
            return AjaxResult.error("文件名称重复，保存失败");
        }
        FileInfo fileInfo = cloudDiskFileService.mkdir(reqFileInfo);
        return AjaxResult.success(fileInfo);
    }

    /**
     * 云盘通过文件对象创建文件夹
     */
    @ApiOperation(value = "云盘通过文件对象创建文件夹")
    @Transactional
    @PostMapping("/mkdir/file")
    public AjaxResult mkdirByFileInfo(String parentId, String fileInfo) {
        return AjaxResult.success(JSONObject.toJSONString(cloudDiskFileService.mkdirByFileInfo(parentId, JSONObject.parseObject(fileInfo, FileInfo.class))));
    }

    /**
     * 通过租户Id创建文件夹
     *
     * @param tenantId 租户Id
     * @param parentId 文件夹父Id
     * @param dirName  文件夹名称
     * @return
     */
    @ApiOperation(value = "通过租户Id创建文件夹")
    @Transactional
    @PostMapping("/mkdir/tenant/id")
    @RequiresPermissions("cloud:storage:info:id")
    public AjaxResult mkdirByTenantId(Long tenantId, String parentId, String dirName) {
        return cloudDiskFileService.mkdirByTenantId(tenantId, parentId, dirName);
    }

    /**
     * 通过租户和文件对象创建文件夹
     *
     * @param tenantId 租户Id
     * @param parentId 文件夹父Id
     * @param fileInfo 文件夹名称
     * @return
     */
    @ApiOperation(value = "通过租户和文件对象创建文件夹")
    @Transactional
    @PostMapping("/mkdir/tenant/file")
    public AjaxResult mkdirByTenantAndFile(Long tenantId, String parentId, String fileInfo) {
        return AjaxResult.success(JSONObject.toJSONString(cloudDiskFileService.mkdirByTenantAndFile(tenantId, parentId, JSONObject.parseObject(fileInfo, FileInfo.class))));
    }

    /**
     * 云盘通过文件id批量隐藏文件/文件夹---》回收站
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "云盘通过文件id批量隐藏文件/文件夹---》回收站")
    @Transactional
    @PostMapping("/hide/file")
    @RequiresPermissions("cloud:storage:info:hide")
    public AjaxResult hideFileByIds(@RequestBody List<FileInfo> fileInfoList) {
        return cloudDiskFileService.hideFileByIds(fileInfoList);
    }

    /**
     * 服务间调用--云盘通过文件id批量隐藏文件/文件夹---》回收站
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "服务间调用--云盘通过文件id批量隐藏文件/文件夹---》回收站")
    @Transactional
    @PostMapping("/inner/hide/file")
    public AjaxResult innerHideFileByIds(String fileInfoList) {
        return cloudDiskFileService.hideFileByIds(JSONArray.parseArray(fileInfoList, FileInfo.class));
    }

    /**
     * 云盘通过文件id批量恢复文件/文件夹
     */
    @ApiOperation(value = "云盘通过文件id批量恢复文件/文件夹")
    @Transactional
    @PostMapping("/recover/file")
    @RequiresPermissions("cloud:storage:info:recover")
    public AjaxResult recoverFileByIds(@RequestBody List<FileInfo> recoverFileList) {
        return cloudDiskFileService.recoverFileByIds(recoverFileList);
    }

    /**
     * 云盘通过文件id批量恢复文件/文件夹
     */
    @ApiOperation(value = "云盘通过文件id批量恢复文件/文件夹")
    @Transactional
    @PostMapping("/inner/recover/file")
    public AjaxResult innerRecoverFileByIds(String recoverFileList) {
        return cloudDiskFileService.recoverFileByIds(JSONArray.parseArray(recoverFileList, FileInfo.class));
    }

    /**
     * 云盘通过文件id批量移动文件/文件夹
     */
    @ApiOperation(value = "云盘通过文件id批量移动文件/文件夹")
    @Transactional
    @PostMapping("/move/file")
    @RequiresPermissions("cloud:storage:info:move")
    public AjaxResult moveFileByIds(String ids, String toParentId) {
        return cloudDiskFileService.moveFileByIds(ids, toParentId);
    }

    /**
     * 服务间调用--云盘通过文件id批量移动文件/文件夹
     */
    @ApiOperation(value = "云盘通过文件id批量移动文件/文件夹")
    @Transactional
    @PostMapping("/inner/move/file")
    public AjaxResult innerMoveFileByIds(String ids, String toParentId) {
        return cloudDiskFileService.moveFileByIds(ids, toParentId);
    }

    /**
     * 云盘通过文件id分享文件/文件夹
     */
    @ApiOperation(value = "云盘通过文件id分享文件/文件夹")
    @Transactional
    @PostMapping("/share/file")
    @RequiresPermissions("cloud:storage:info:share")
    public AjaxResult shareFileFileShare(@RequestBody FileShare fileShare) {
        return cloudDiskFileService.shareFileFileShare(fileShare);
    }

    /**
     * 云盘通过文件id取消文件/文件夹
     */
    @ApiOperation(value = "云盘通过文件id取消文件/文件夹")
    @Transactional
    @GetMapping("/cancel/share/file/{id}")
    @RequiresPermissions("cloud:storage:info:cancel")
    public AjaxResult cancelShareFile(@PathVariable Long id) {
        return cloudDiskFileService.cancelShareFile(id);
    }

    /**
     * 云盘通过文件id彻底删除文件/文件夹
     */
    @ApiOperation(value = "云盘通过文件id彻底删除文件/文件夹")
    @Transactional
    @PostMapping("/delete/file")
    @RequiresPermissions("cloud:storage:info:delete")
    public AjaxResult deleteFileByIds(@RequestBody List<FileInfo> deleteFileList) {
        return cloudDiskFileService.deleteFileByIds(deleteFileList);
    }

    /**
     * 通过文件存储路径彻底删除文件
     */
    @ApiOperation(value = "通过文件存储路径彻底删除文件")
    @Transactional
    @PostMapping("/delete/fileStorageName")
    public AjaxResult deleteFileByFileStorageName(@RequestBody List<FileInfo> deleteFileList) {
        cloudDiskFileService.deleteFileByFileStorageName(deleteFileList);
        return AjaxResult.success();
    }

    /**
     * 服务内部使用--云盘通过文件id删除文件/文件夹
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = {"file_info"})
    @ApiOperation(value = "服务内部使用--云盘通过文件id删除文件/文件夹")
    @Transactional
    @PostMapping("/inner/delete/file")
    public AjaxResult innerDeleteFileByIds(String fileInfo) {
        return cloudDiskFileService.innerDeleteFileByIds(JSONArray.parseArray(fileInfo, FileInfo.class));
    }

    /**
     * 通过文件id批量下载文件/文件夹
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "通过文件id批量下载文件/文件夹")
    @Transactional
    @PostMapping("/batch/download")
    @RequiresPermissions("cloud:storage:info:batch:download")
    public AjaxResult batchDownload(@RequestBody List<FileInfo> batchDownloadFileList, HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {
        return cloudDiskFileService.batchDownload(batchDownloadFileList, response, request);
    }

    /**
     * 通过文件名称下载压缩包
     */
    @ApiOperation(value = "通过文件id批量下载文件/文件夹")
    @Transactional
    @GetMapping("/download/{fileName}")
    @RequiresPermissions("cloud:storage:info:download")
    public void downloadByFileName(@PathVariable String fileName, HttpServletResponse response, HttpServletRequest request) throws IOException {
        cloudDiskFileService.downloadByFileName(fileName, response, request);
    }

    /**
     * 通过文件ids批量下载文件
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "通过文件ids批量下载文件")
    @Transactional
    @PostMapping("/download")
    @RequiresPermissions(value = {
            "cloud:storage:info:download",
            "cloud:storage:info:batch:download",
            "file:operate:auth",
            "profile:file:info:download",
            "profile:achievement:file:info:download",
            "profile:template:file:info:download",
            "activity:game:download"
    }, logical = Logical.OR)
    public void download(String fileIds, HttpServletResponse response, HttpServletRequest request) {
        cloudDiskFileService.download(fileIds, response, request);
    }

    /**
     * 通过Id更新文件名称
     */
    @TemporaryIgnoreTables(temporaryIgnoreTables = "file_info")
    @ApiOperation(value = "通过Id更新文件名称")
    @PostMapping("/update/file/info")
    public AjaxResult updateFileInfo(String fileId, String fileInfo) {
        return AjaxResult.success(cloudDiskFileService.updateFileInfo(fileId, fileInfo));
    }

    /**
     * 服务间调用--云盘文件上传请求
     */
    @ApiOperation(value = "服务间调用--云盘文件上传请求")
    @Transactional
    @PostMapping(value = "/inner/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public AjaxResult innerUpload(MultipartFile file) {
        return cloudDiskFileService.upload(file);
    }
}
