package cn.axzo.workflow.server.common.util;

import cn.axzo.basics.common.BeanMapper;
import cn.axzo.maokai.api.client.OrganizationalNodeUserQueryApi;
import cn.axzo.maokai.api.vo.request.OrgNodeUserBriefInfoListReq;
import cn.axzo.maokai.api.vo.response.OrgNodeUserBriefInfoResp;
import cn.axzo.nanopart.doc.api.domain.FileReplaceContent;
import cn.axzo.nanopart.doc.api.filetemplate.FileTemplateApi;
import cn.axzo.nanopart.doc.api.filetemplate.request.FileTemplateReplaceRequest;
import cn.axzo.nanopart.doc.api.filetemplate.request.SimpleFileTemplateReplaceRequest;
import cn.axzo.nanopart.doc.api.index.request.DeleteNodeRequest;
import cn.axzo.oss.http.api.ServerFileServiceApi;
import cn.axzo.oss.http.model.ApiSignUrlDownloadRequest;
import cn.axzo.oss.http.model.ApiSignUrlDownloadResponse;
import cn.axzo.workflow.common.enums.FileTypeEnum;
import cn.axzo.workflow.common.model.dto.SignatureDTO;
import cn.axzo.workflow.common.model.dto.UploadFieldDTO;
import cn.axzo.workflow.common.model.dto.VariableObjectDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.engine.cmd.CustomGetProcessInstanceVariablesToObjectCmd;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.google.common.collect.Lists;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_NAME;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_NAME_DESC;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_PHONE;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION;
import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC;
import static cn.axzo.workflow.common.model.dto.VariableObjectDTO.Type.signatureAndAdvice;

/**
 * WPS 工具类
 *
 * @author wangli
 * @since 2025-04-09 20:41
 */
@Component
public class WpsUtil {
    public static final List<FileTypeEnum> WPS_SUPPORTED_FILE_TYPES = Lists.newArrayList(FileTypeEnum.WORD, FileTypeEnum.EXCEL);
    @Resource
    private FileTemplateApi fileTemplateApi;
    @Resource
    private ServerFileServiceApi serverFileServiceApi;
    @Resource
    private OrganizationalNodeUserQueryApi organizationalNodeUserQueryApi;

    /**
     * 调用 wps 文件变量替换接口
     *
     * @param fileCode 基于该模板进行变量替换
     * @param fileKey  如果有值，则变量替换完成后，直接将文档覆盖该 fileKey 对应的文件；
     * @return 返回替换变量后的文件oss 的 fileKey
     */
    public String wpsFileVariableReplace(List<VariableObjectDTO> wpsVariables,
                                         String fileCode, String fileKey, String fileName) {

        List<FileReplaceContent> fileReplaceContents = BeanMapper.copyList(wpsVariables.stream()
                .filter(i -> Objects.nonNull(i.getValue()))
                .filter(i -> !(Objects.equals(i.getType().name(), "img") && !StringUtils.hasText(i.getValue().toString())))
                .filter(i -> Objects.equals(i.getType().name(), "img") || Objects.equals(i.getType().name(), "text"))
                .collect(Collectors.toList()), FileReplaceContent.class, (s, t) -> {
            t.setKey(s.getDesc());
            if (Objects.equals(s.getType().name(), "img")) {
                if (isJson(s.getValue().toString())) {
                    t.setContent(JSON.parseArray(s.getValue().toString(), UploadFieldDTO.class).get(0).getFileUrl());
                } else {
                    t.setContent(JSON.parseArray(JSON.toJSONString(s.getValue()), UploadFieldDTO.class).get(0).getFileUrl());
                }
            } else {
                t.setContent(Objects.nonNull(s.getValue()) ? s.getValue().toString() : "");
            }
            t.setType(s.getType().name());
            t.setPrefix("[");
            t.setSuffix("]");
        });
        if (StringUtils.hasText(fileCode)) {
            FileTemplateReplaceRequest request = new FileTemplateReplaceRequest();
            request.setFileCode(fileCode);
            request.setFileKey(fileKey);
            request.setReplaceContentList(fileReplaceContents);
            return RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.replaceWordText(request), "替换 WPS 文档变量", request);
        } else {
            SimpleFileTemplateReplaceRequest request = new SimpleFileTemplateReplaceRequest();
            request.setFileName(fileName);
            request.setFileKey(fileKey);
            request.setReplaceContentList(fileReplaceContents);
            return RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.replaceWordTextForFileKey(request), "替换 WPS 文档变量(simple)", request);
        }
    }

    public List<VariableObjectDTO> getWpsReplaceVariables(ProcessEngineConfigurationImpl processEngineConfiguration, String processInstanceId) {
        CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
        List<VariableObjectDTO> wpsVariables =
                commandExecutor.execute(new CustomGetProcessInstanceVariablesToObjectCmd(processInstanceId));

        List<VariableObjectDTO> signatureAndAdvices = Lists.newArrayList();
        wpsVariables.stream().filter(i -> Objects.equals(signatureAndAdvice, i.getType())).forEach(variableObjectDTO -> {
            List<SignatureDTO.SignDetail> signDetails = (List<SignatureDTO.SignDetail>) variableObjectDTO.getValue();
            if (!CollectionUtils.isEmpty(signDetails)) {
                SignatureDTO.SignDetail signDetail = signDetails.get(0);
                ApiSignUrlDownloadRequest request = ApiSignUrlDownloadRequest.builder()
                        .fileKeys(Lists.newArrayList(signDetail.getSignature())).build();
                List<ApiSignUrlDownloadResponse> signUrl = RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(request), "获取手写签图片地址", request);
                signatureAndAdvices.add(VariableObjectDTO.builder()
                        .key(variableObjectDTO.getKey() + "_signature")
                        .desc(variableObjectDTO.getDesc() + "电子签名")
                        .value(Lists.newArrayList(UploadFieldDTO.builder().fileKey(signDetail.getSignature()).fileUrl(signUrl.get(0).getSignUrl()).build()))
                        .type(VariableObjectDTO.Type.img)
                        .build());
                signatureAndAdvices.add(VariableObjectDTO.builder()
                        .key(variableObjectDTO.getKey() + "_advice")
                        .desc(variableObjectDTO.getDesc() + "审批意见")
                        .value(signDetail.getAdvice())
                        .type(VariableObjectDTO.Type.text)
                        .build());
            }
        });
        wpsVariables.addAll(signatureAndAdvices);

        wpsVariables.stream().filter(i -> Objects.equals(i.getKey(), PRINT_VAR_PROCESS_INITIATOR)).findAny().ifPresent(i -> {
            BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(i.getValue());
            Optional<OrgNodeUserBriefInfoResp> user = getUserInfo(initiator);
            wpsVariables.add(VariableObjectDTO.builder()
                    .key(PRINT_VAR_PROCESS_INITIATOR_NAME)
                    .desc(PRINT_VAR_PROCESS_INITIATOR_NAME_DESC)
                    .value(user.isPresent() ? user.get().getRealName() : "")
                    .build());
            wpsVariables.add(VariableObjectDTO.builder()
                    .key(PRINT_VAR_PROCESS_INITIATOR_POSITION)
                    .desc(PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC)
                    .value(user.isPresent() && Objects.nonNull(user.get().getJob()) ? user.get().getJob().getName() : "")
                    .build());
            wpsVariables.add(VariableObjectDTO.builder()
                    .key(PRINT_VAR_PROCESS_INITIATOR_PHONE)
                    .desc(PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC)
                    .value(user.isPresent() ? user.get().getProfile().getPhone() : "")
                    .build());

        });
        return wpsVariables;
    }

    public void deleteWpsFile(String wpsCode) {
        if (!StringUtils.hasText(wpsCode)) {
            return;
        }
        DeleteNodeRequest request = new DeleteNodeRequest();
        request.setCode(wpsCode);
        RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.delete(request), "删除 WPS 文档", request);
    }

    public static boolean isJson(String str) {
        try {
            JSON.parse(str);
            return true;
        } catch (JSONException e) {
            return false;
        }
    }

    public static boolean isWpsFile(FileTypeEnum fileTypeEnum) {
        return WPS_SUPPORTED_FILE_TYPES.contains(fileTypeEnum);
    }

    private Optional<OrgNodeUserBriefInfoResp> getUserInfo(BpmnTaskDelegateAssigner assigner) {
        if (Objects.isNull(assigner)) {
            return Optional.empty();
        }
        OrgNodeUserBriefInfoListReq req = new OrgNodeUserBriefInfoListReq();
        if (StringUtils.hasText(assigner.getTenantId())) {
            req.setWorkspaceId(Long.valueOf(assigner.getTenantId()));
        }
        if (StringUtils.hasText(assigner.getOuId())) {
            req.setOuId(Long.valueOf(assigner.getOuId()));
        }
        if (StringUtils.hasText(assigner.getNodeId())) {
            req.setOrgNodeIds(Lists.newArrayList(Long.valueOf(assigner.getNodeId())));
        }
        if (StringUtils.hasText(assigner.getPersonId())) {
            req.setPersonIds(Lists.newArrayList(Long.valueOf(assigner.getPersonId())));
        }
        req.setNeedJob(true);
        req.setNeedUnit(true);
        req.setNeedProfile(true);
        req.setContainsExited(true);
        List<OrgNodeUserBriefInfoResp> users = RpcExternalUtil.rpcApiResultProcessor(() -> organizationalNodeUserQueryApi.listOrgNodeUsers(req),
                "查询审批人员组织信息", req);

        if (CollectionUtils.isEmpty(users)) {
            return Optional.empty();
        }

        return users.stream().sorted(Comparator.comparing(OrgNodeUserBriefInfoResp::getExited).reversed())
                .collect(Collectors.toList()).stream().findFirst();
    }
}
