package com.fjhx.file.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.file.entity.*;
import com.fjhx.file.mapper.FileInfoMapper;
import com.fjhx.file.service.FileInfoService;
import com.obs.services.ObsClient;
import com.obs.services.model.PostSignatureRequest;
import com.obs.services.model.PostSignatureResponse;
import com.ruoyi.common.constant.DatasourceConstant;
import com.ruoyi.common.core.domain.BaseIdPo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.wrapper.IWrapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 文件表 服务实现类
 * </p>
 *
 * @author zlj
 * @since 2023-03-14
 */
@Service
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileInfoService {

    @Value("${obs.ak}")
    private String ak;

    @Value("${obs.sk}")
    private String sk;

    @Value("${obs.endPoint}")
    private String endPoint;

    @Value("${obs.url}")
    private String url;

    @Value("${obs.bucketName}")
    private String bucketName;

    @Value("${spring.profiles.active}")
    private String active;

    @Override
    public SingVo getSing(SingDto dto) {

        SingVo singVo = new SingVo();

        // 文件后缀名
        String suffix = FileUtil.getSuffix(dto.getFileName());

        // 文件路径
        String objectKey = new StringJoiner("/")
                .add("byteSailing")
                .add(active)
                .add(DateUtil.format(new Date(), "yyyy/MM/dd"))
                .add(IdUtil.fastSimpleUUID() + (ObjectUtil.isEmpty(suffix) ? "" : "." + suffix))
                .toString();

        ObsClient obsClient = null;

        try {
            // 获取oss链接客户端
            obsClient = getObsClient();
            // 指定签名有效期为10秒
            long currentTimeMillis = System.currentTimeMillis() + 1000 * 10;
            // 获取签名
            PostSignatureRequest request = new PostSignatureRequest(currentTimeMillis, bucketName, objectKey);
            PostSignatureResponse temporarySignature = obsClient.createPostSignature(request);
            // 封装签名
            Map<String, String> body = new LinkedHashMap<>();
            body.put("policy", temporarySignature.getPolicy());
            body.put("AccessKeyId", ak);
            body.put("signature", temporarySignature.getSignature());
            body.put("key", objectKey);
            singVo.setUploadBody(body);

        } catch (Exception e) {
            throw new ServiceException("获取签名失败");
        } finally {
            IoUtil.close(obsClient);
        }

        // 保存文件
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileUrl(objectKey);
        fileInfo.setFileName(dto.getFileName());
        save(fileInfo);

        // 封装文件信息
        singVo.setUploadUrl("https://" + bucketName + "." + endPoint);
        singVo.setId(fileInfo.getId());
        singVo.setFileName(dto.getFileName());
        singVo.setFileUrl(url + objectKey);

        return singVo;
    }

    @Override
    public Map<String, List<FileInfoVo>> getList(FileInfoSelectDto dto) {

        if (ObjectUtil.isEmpty(dto.getBusinessIdList())) {
            return new HashMap<>();
        }

        IWrapper<Object> wrapper = IWrapper.getWrapper();
        wrapper.in("fi", FileInfo::getBusinessId, dto.getBusinessIdList());
        wrapper.eq("fi", FileInfo::getBusinessType, dto.getFileType());
        List<FileInfoVo> list = this.baseMapper.getList(wrapper);

        if (list.size() == 0) {
            return new HashMap<>();
        }

        for (FileInfoVo fileInfoVo : list) {
            fileInfoVo.setFileUrl(url + fileInfoVo.getFileUrl());
        }

        return list.stream().collect(Collectors.groupingBy(FileInfoVo::getId));
    }

    /**
     * !!!必须加上Transactional 更改事务传播行为,否则数据源将被缓存,造成数据源切换失败
     *
     * @param obsFileList  文件列表
     * @param businessId   业务id
     * @param businessType 业务文件类型
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @DS(DatasourceConstant.BASE)
    @Override
    public void saveFile(List<ObsFile> obsFileList, Long businessId, Integer businessType) {
        // 切换到从库
        if (obsFileList == null || obsFileList.size() == 0) {
            return;
        }

        List<FileInfo> fileInfoList = BeanUtil.copyToList(obsFileList, FileInfo.class);
        for (FileInfo fileInfo : fileInfoList) {
            fileInfo.setBusinessId(businessId);
            fileInfo.setBusinessType(businessType);
        }
        this.updateBatchById(fileInfoList);
        DynamicDataSourceContextHolder.clear();
    }

    /**
     * !!!必须加上Transactional 更改事务传播行为,否则数据源将被缓存,造成数据源切换失败
     *
     * @param obsFileList  文件列表
     * @param businessId   业务id
     * @param businessType 业务文件类型
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @DS(DatasourceConstant.BASE)
    @Override
    public void editFile(List<ObsFile> obsFileList, Long businessId, Integer businessType) {
        if (obsFileList == null || obsFileList.size() == 0) {
            return;
        }

        // 删除不包含在此列表的业务id文件
        List<Long> obsFileId = obsFileList.stream().map(ObsFile::getId).collect(Collectors.toList());
        this.remove(q -> q.notIn(BaseIdPo::getId, obsFileId).eq(FileInfo::getBusinessId, businessId));

        // 更新在此列表的文件
        saveFile(obsFileList, businessId, businessType);
    }

    /**
     * !!!必须加上Transactional 更改事务传播行为,否则数据源将被缓存,造成数据源切换失败
     *
     * @param businessId   业务id
     * @param businessType 业务文件类型
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @DS(DatasourceConstant.BASE)
    @Override
    public void removeFile(Long businessId, Integer businessType) {
        this.remove(q -> q
                .eq(FileInfo::getBusinessId, businessId)
                .eq(FileInfo::getBusinessType, businessType));
    }

    /**
     * !!!必须加上Transactional 更改事务传播行为,否则数据源将被缓存,造成数据源切换失败
     *
     * @param businessId 业务id
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @DS(DatasourceConstant.BASE)
    @Override
    public void removeFile(Long businessId) {
        this.remove(q -> q.eq(FileInfo::getBusinessId, businessId));
    }

    /**
     * 获取oss链接客户端
     */
    private ObsClient getObsClient() {
        return new ObsClient(ak, sk, endPoint);
    }

}