package cn.iocoder.yudao.module.infra.service.file;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.file.core.client.FileClient;
import cn.iocoder.yudao.framework.file.core.utils.FileTypeUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.BpmFileUploadReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.BusinessFileUploadReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.BpmFileDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.BusinessFileDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import cn.iocoder.yudao.module.infra.dal.mysql.file.BpmFileMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.file.BusinessFileMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileContentMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;

/**
 * 文件 Service 实现类
 */
@Service
public class FileServiceImpl implements FileService {

    @Resource
    private FileConfigService fileConfigService;

    @Resource
    private FileMapper fileMapper;
    @Resource
    private BpmFileMapper bpmFileMapper;

    @Resource
    private BusinessFileMapper businessFileMapper;

    @Resource
    private FileContentMapper fileContentMapper;

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

    @Override
    @SneakyThrows
    public BpmFileDO createReturnFile(MultipartFile file) {

        Long userId = SecurityFrameworkUtils.getLoginUserId();
        long timestamp = System.currentTimeMillis();

        String[] fileInfo = new String[4];
        String name = file.getOriginalFilename();
        String path = null;
        byte[] content = IoUtil.readBytes(file.getInputStream());

        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);

        StrUtil.isEmpty(path);//path = FileUtils.generatePath(content, name);
        path = userId + "_" + timestamp;
        String beginPath = name.replace(".", "_");
        path = beginPath + "_" + path + "." + FileNameUtil.extName(name);
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }

        // 上传到文件存储器
        FileClient client = fileConfigService.getMasterFileClient();
        Assert.notNull(client, "客户端(master) 不能为空");
        String url = client.upload(content, path, type);

        // 保存到数据库
        BpmFileDO fileDo = new BpmFileDO();
        fileDo.setConfigId(client.getId());
        fileDo.setName(name);
        fileDo.setPath(path);
        fileDo.setUrl(url);
        fileDo.setType(type);
        fileDo.setSize(content.length);
        fileDo.setUploadUserId(userId);
        bpmFileMapper.insert(fileDo);

        fileInfo[0] = name;
        fileInfo[1] = fileDo.getUrl();
        fileInfo[2] = fileDo.getType();
        // 返回上传文件的访问路径
        //return "{\"name\": \""+ fileInfo[0]+"\",   \"path\": \""+ fileInfo[1]+"\",   \"type\": \""+ fileInfo[2]+"\" }";
        return fileDo;
    }

    @Override
    @SneakyThrows
    public String createFile(String name, String path, byte[] content) {
        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);
        if (StrUtil.isEmpty(path)) {
            path = FileUtils.generatePath(content, name);
        }
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }

        // 上传到文件存储器
        FileClient client = fileConfigService.getMasterFileClient();
        Assert.notNull(client, "客户端(master) 不能为空");
        String url = client.upload(content, path, type);

        // 保存到数据库
        FileDO file = new FileDO();
        file.setConfigId(client.getId());
        file.setName(name);
        file.setPath(path);
        file.setUrl(url);
        file.setType(type);
        file.setSize(content.length);
        fileMapper.insert(file);
        return url;
    }

    @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);
    }

    @Override
    public void deleteFile(String url) throws Exception {
        String path = url.substring(url.lastIndexOf("/") + 1);
        PageResult<FileDO> fileDOPageResult = fileMapper.selectPage(new FilePageReqVO().setPath(path));
        FileDO file = null;
        if (fileDOPageResult != null && fileDOPageResult.getTotal() > 0) {
            file = fileDOPageResult.getList().get(0);

        }
        if (file == null) {
            throw exception(FILE_NOT_EXISTS);
        }

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

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

    @Override
    public void deleteBpmFile(String url) throws Exception {
        String path = url.substring(url.lastIndexOf("/") + 1);
        PageResult<BpmFileDO> fileDOPageResult = bpmFileMapper.selectPage(new FilePageReqVO().setPath(path));
        BpmFileDO file = null;
        if (fileDOPageResult != null && fileDOPageResult.getTotal() > 0) {
            file = fileDOPageResult.getList().get(0);
        }
        if (file == null) {
            throw exception(FILE_NOT_EXISTS);
        }

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

        // 删除记录
        bpmFileMapper.deleteById(file.getId());
    }

    @Override
    public void uploadBpmFileProcessInstanceId(BpmFileUploadReqVO reqVO) {
        String processInstanceId = reqVO.getProcessInstanceId();
        List<String> urls = reqVO.getUrls();
        LambdaUpdateWrapper<BpmFileDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.in(BpmFileDO::getUrl, urls);
        lambdaUpdateWrapper.set(BpmFileDO::getProcessInstanceId, processInstanceId); // 假设 bid 是要更新的 bid 值
        // 调用 MyBatis Plus 的 update 方法执行批量更新
        bpmFileMapper.update(null, lambdaUpdateWrapper);
    }

    @Override
    @SneakyThrows
    public BusinessFileDO createBusinessReturnFile(MultipartFile file, Long businessType) {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        long timestamp = System.currentTimeMillis();

        String[] fileInfo = new String[4];
        String name = file.getOriginalFilename();
        String path = null;
        byte[] content = IoUtil.readBytes(file.getInputStream());

        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);

        StrUtil.isEmpty(path);//path = FileUtils.generatePath(content, name);
        path = userId + "_" + timestamp;
        String beginPath = name.replace(".", "_");
        path = beginPath + "_" + path + "." + FileNameUtil.extName(name);
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }

        // 上传到文件存储器
        FileClient client = fileConfigService.getMasterFileClient();
        Assert.notNull(client, "客户端(master) 不能为空");
        String url = client.upload(content, path, type);

        // 保存到数据库
        BusinessFileDO fileDo = new BusinessFileDO();
        fileDo.setConfigId(client.getId());
        fileDo.setName(name);
        fileDo.setPath(path);
        fileDo.setUrl(url);
        fileDo.setType(type);
        fileDo.setSize(content.length);
        fileDo.setUploadUserId(userId);
        fileDo.setBusinessType(businessType) ;


        //如果业务类型是2 ，说明是保存用户签名图片，那么将用户ID存入businessInstanceId中
        if( businessType == 2) {
            //先查询当前用户，是否存有签名，如果没有新增，如果存在，则更新url，并删除infra_file_content中对应的记录
            BusinessFileDO businessFileDO = businessFileMapper.selectOneByBusinessInstanceId(businessType, userId.toString()) ;
            if (businessFileDO == null) {
                fileDo.setBusinessInstanceId(userId+"") ;
                businessFileMapper.insert(fileDo);
            }else {
                client.delete(businessFileDO.getPath());
                businessFileDO.setConfigId(client.getId());
                businessFileDO.setName(name);
                businessFileDO.setPath(path);
                businessFileDO.setUrl(url);
                businessFileDO.setType(type);
                businessFileDO.setSize(content.length);
                businessFileDO.setUploadUserId(userId);
                businessFileDO.setBusinessType(businessType) ;
                businessFileMapper.updateById(businessFileDO) ;
                fileDo = businessFileDO ;
            }
        }else {
            businessFileMapper.insert(fileDo);
        }

        fileInfo[0] = name;
        fileInfo[1] = fileDo.getUrl();
        fileInfo[2] = fileDo.getType();
        return fileDo;
    }

    /**
     * 删除bpm文件
     * @param url
     * @throws Exception
     */
    @Override
    @SneakyThrows
    public void deleteBusinessFile(String url) {
        String path = url.substring(url.lastIndexOf("/") + 1);
        PageResult<BusinessFileDO> fileDOPageResult = businessFileMapper.selectPage(new FilePageReqVO().setPath(path));
        BusinessFileDO file = null;
        if (fileDOPageResult != null && fileDOPageResult.getTotal() > 0) {
            file = fileDOPageResult.getList().get(0);
        }
        if (file == null) {
            throw exception(FILE_NOT_EXISTS);
        }

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

        // 删除记录
        businessFileMapper.deleteById(file.getId());
    }

    /**
     * 更新文件的流程实例ID
     * @param reqVO
     * @throws Exception
     */
    @Override
    @SneakyThrows
    public void uploadBusinessFileProcessInstanceId(BusinessFileUploadReqVO reqVO) {
        String businessInstanceId = reqVO.getBusinessInstanceId();
        List<String> urls = reqVO.getUrls();
        LambdaUpdateWrapper<BusinessFileDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.in(BusinessFileDO::getUrl, urls);
        lambdaUpdateWrapper.set(BusinessFileDO::getBusinessInstanceId, businessInstanceId); // 假设 bid 是要更新的 bid 值
        // 调用 MyBatis Plus 的 update 方法执行批量更新
        businessFileMapper.update(null, lambdaUpdateWrapper);
    }

    @Override
    public String getUserSignImgPath(Long userId) {
        BusinessFileDO businessFileDO = businessFileMapper.selectOneByBusinessInstanceId(2L, userId.toString()) ;
        if(businessFileDO != null) {
            return businessFileDO.getUrl() ;
        }else {
            return "" ;
        }
    }

    @SneakyThrows
    @Override
    public String createBusinessFile(Long bussinessType, String name, byte[] content) {
        // 插入 infra_file_content
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        long timestamp = System.currentTimeMillis();
        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);
        String path = userId + "_" + timestamp;
        String beginPath = name.replace(".", "_");
        path = beginPath + "_" + path + "." + FileNameUtil.extName(name);
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }
        // 上传到文件存储器
        FileClient client = fileConfigService.getMasterFileClient();
        Assert.notNull(client, "客户端(master) 不能为空");
        String url = client.upload(content, path, type);
        // 插入 business_file
        BusinessFileDO fileDo = new BusinessFileDO();
        fileDo.setConfigId(client.getId());
        fileDo.setName(name);
        fileDo.setPath(path);
        fileDo.setUrl(url);
        fileDo.setType(type);
        fileDo.setSize(content.length);
        fileDo.setUploadUserId(userId);
        fileDo.setBusinessType(bussinessType) ;
        fileDo.setBusinessInstanceId(userId+"") ;
        businessFileMapper.insert(fileDo);
        return url ;
    }


    @Override
    public String updateBusinessFileContent(String url, Long businessType, String name, byte[] content) {
        //根据url查询，url对应path
        BusinessFileDO fileDO = businessFileMapper.selectOneByUrl(url) ;
        if( fileDO == null ) {
            //说明是历史数据，头像的地址存在了infra_file表中
            //删除infra_file  和  infra_file_content 相关数据
            try {
                deleteFile(url) ;
            } catch (Exception e) {

            }
            //在business_表中插入新的数据
            url = createBusinessFile(businessType,  name, content) ;
        }else {
            String path = fileDO.getPath();
            //通过path 更新content的内容
            fileContentMapper.updateOneContent(path, content) ;
        }
        return url ;
    }
}
