package digital.laboratory.platform.inspection.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.XWPFTemplate;
import digital.laboratory.platform.common.core.constant.CommonConstants;
import digital.laboratory.platform.common.core.util.R;
import digital.laboratory.platform.common.feign.RemoteWord2PDFService;
import digital.laboratory.platform.common.oss.service.OssFile;
import digital.laboratory.platform.inspection.service.CommonFeignService;
import digital.laboratory.platform.sys.dto.UserInfo;
import digital.laboratory.platform.sys.entity.Area;
import digital.laboratory.platform.sys.entity.SysOrg;
import digital.laboratory.platform.sys.entity.SysUser;
import digital.laboratory.platform.sys.feign.RemoteOrgService;
import digital.laboratory.platform.sys.feign.RemoteUserService;
import feign.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 通用的feign请求封装接口服务层接口 实现类
 */
@Slf4j
@Service
public class CommonFeignServiceImpl implements CommonFeignService {

    @Resource
    private RemoteOrgService remoteOrgService;

    @Resource
    private RemoteUserService remoteUserService;

    @Resource
    private RemoteWord2PDFService remoteWord2PDFService;

    @Resource
    private OssFile ossFile;

    /**
     * 根据用户获取远程系统机构信息
     *
     * @param orgId 用户信息
     * @return 对应的远程系统机构信息
     * @throws RuntimeException 如果未找到对应的机构信息，则抛出运行时异常
     */
    @Override
    public SysOrg remoteGetSysOrg(String orgId) {
        SysOrg sysOrg = null;
        R<SysOrg> r = remoteOrgService.getByIdWithoutToken(orgId);
        if (r != null && r.getCode() == CommonConstants.SUCCESS) {
            sysOrg = r.getData();
        } else {
            throw new RuntimeException(String.format("没有找到 orgId 为 %s 的机构, 请确认用户所属机构的正确性!", orgId));
        }
        return sysOrg;
    }

    /**
     * 取指定环节可用的用户列表
     * 当流程进行到某个环节的时候, 需要某个用户对这个环节进行处理(通过或不通过)。
     * 这里取可用的用户列表
     * <p>
     * 涉及到的环境有以下几个:
     * 1、创建委托及提交
     * 委托的提交者就是委托的创建者。不存在不通过的可能。
     * 2、审核
     * 审核者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托审核权限
     * 3、审批
     * 审核者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托审批权限
     * 4、送检确认
     * 送检确认者有几个条件: (1)必须与委托的创建者是同一个机构, 或上级机构的人 (2)必须拥有委托送检确认权限
     * 5、受理
     * 受理者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托受理权限
     *
     * @return
     */
    /**
     * 根据权限和机构远程获取用户列表
     *
     * @param orgId    组织ID
     * @param permission 权限列表
     * @return 用户列表
     */
    @Override
    public List<SysUser> remoteGetUsersByPermission(String orgId, List<String> permission) {
        R<List<SysUser>> r = remoteUserService.innerGetUsersByPermission(orgId, permission);
        if (r != null && r.getCode() == CommonConstants.SUCCESS) {
            return r.getData();
        } else {
            throw new RuntimeException(String.format("根据权限 [%s] 和 机构id [%s] 远程获取用户列表!", permission, orgId));
        }
    }

    /**
     * 通过机构ID远程获取机构所在省市信息
     *
     * @param orgId 机构ID
     * @return 包含机构所在省市信息的Area对象列表
     * @throws RuntimeException 当根据机构ID获取机构所在省市信息失败时抛出
     */
    @Override
    public List<Area> remoteGetProvinceCityInfo(String orgId) {
        List<Area> result = null;
        R<List<Area>> r = remoteOrgService.fetchProvinceCityInfoByOrgId(orgId);
        if (r != null && r.getCode() == CommonConstants.SUCCESS) {
            result = r.getData();
        } else {
            throw new RuntimeException("根据机构id获取机构所在省市信息失败！");
        }
        return result;
    }

    /**
     * 远程调用或者用户信息
     * @param username
     * @return
     */
    @Override
    public SysUser remoteGetUserByUsername(String username){
        R<UserInfo> info = remoteUserService.innerGetUserInfoByUsername(username);
        if (info != null && info.getCode() == CommonConstants.FAIL) {
            throw new RuntimeException(String.format("获取用户名为 %s 的用户信息失败！", username));
        }
        return info.getData().getSysUser();
    }

    /**
     * 远程调用获取用户信息
     * @param userId
     * @return
     */
    @Override
    public SysUser remoteGetUserById(String userId){
        R<SysUser> info = remoteUserService.innerGetById(userId);
        if (info != null && info.getCode() == CommonConstants.FAIL) {
            throw new RuntimeException(String.format("获取用户名id为 %s 的用户信息失败！", userId));
        }
        return info.getData();
    }

    @Override
    public boolean remoteGenerateWord2PDF(XWPFTemplate template, String originalFilename, String savePath) throws Exception{
        ByteArrayOutputStream fosWord = new ByteArrayOutputStream();
        template.write(fosWord);
        template.close();

        //------------
        ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray());
        fosWord.close();

        MockMultipartFile mockMultipartFile = new MockMultipartFile("file", originalFilename + ".docx", "image/jpg", fisWord);
        Response response = remoteWord2PDFService.word2pdf(mockMultipartFile);
        fisWord.close();


        ByteArrayOutputStream outPDF = new ByteArrayOutputStream();
        IoUtil.copy(response.body().asInputStream(), outPDF, IoUtil.DEFAULT_MIDDLE_BUFFER_SIZE);
        ByteArrayInputStream isPDF = new ByteArrayInputStream(outPDF.toByteArray());
        outPDF.close();

        boolean b = ossFile.fileSave(savePath, isPDF);

        isPDF.close();

        log.info("转换为 PDF 结束");
        return b;
    }

    /**
     * 远程调用根据文件路径获取文件
     * @param filePath minio上的文件路径
     * @return
     * @throws Exception
     */
    @Override
    public ByteArrayInputStream remoteGetFile(String filePath) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ossFile.fileGet(filePath, bos);

        byte[] templateArray = bos.toByteArray();

        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray);
        bos.close();
        return bis;
    }

    /**
     * 远程调用根据文件路径获取文件
     * @param filePath minio上的文件路径
     * @param fileName 名称
     * @param httpServletResponse
     * @throws Exception
     */
    @Override
    public void remoteGetFile(String filePath, String fileName, HttpServletResponse httpServletResponse) throws Exception {
        ossFile.fileGet(filePath, httpServletResponse.getOutputStream());
        if (StrUtil.isNotBlank(fileName)) {
            httpServletResponse.setContentType(fileName);
        }
    }

    /**
     * 远程调用根据文件路径获取文件列表
     * @param filePath minio上的文件路径
     */
    @Override
    public List<String> remoteGetFileList(String filePath) {
        if (StrUtil.isNotBlank(filePath)) {
            List<String> fileNameList = ossFile.fileList(filePath);
            List<String> fileList = fileNameList.stream().map(fileName -> {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                try {
                    ossFile.fileGet(filePath + "/" + fileName, byteArrayOutputStream);
                    return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("文件获取失败， 文件路径为： {}", filePath + "/" + fileName);
                }
                return null;
            }).collect(Collectors.toList());
            return fileList;
        } else {
            throw new RuntimeException("文件路径不能为空！");
        }
    }

    /**
     * 远程调用-上传文件
     * @param file 上传的文件对象
     * @param path 上传到minio的位置
     * @return
     */
    @Override
    public Map<String, String> remoteUploadFile(MultipartFile file, String path) {
        boolean r = ossFile.fileUpload(file, path);
        if (r) {
            HashMap<String, String> resultData = new HashMap<>();
            resultData.put("fileName", FileNameUtil.getName(file.getOriginalFilename()));
            resultData.put("path", path);
            log.info("文件上传成功！");
            return resultData;
        } else {
            String failMsg = String.format("文件名为 %s 的文件上传失败！", file.getOriginalFilename());
            log.error(failMsg);
            throw new RuntimeException(failMsg);
        }
    }
}
