package com.jwipc.sys.util;

import com.alibaba.fastjson2.JSONObject;
import com.jwipc.exception.DaoException;
import com.jwipc.prop.FinancialShareProp;
import com.jwipc.security.LoginUtil;
import com.jwipc.sys.constant.SysKingdeeImageClient;
import com.jwipc.sys.constant.SysKingdeeInvoiceCloudClient;
import com.jwipc.sys.dto.FileResposeDTO;
import com.jwipc.sys.dto.FinancialShareApiResultDTO;
import com.jwipc.sys.dto.KingdeeImageApiResultDTO;
import com.jwipc.sys.logic.FileLogic;
import com.jwipc.util.HmacUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *  财务共享工具类
 * </p>
 *
 * @author Chen Tianshi
 * @since 2025/12/8 16:25
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class FinancialShareUtil {
    private final SysKingdeeInvoiceCloudClient sysKingdeeInvoiceCloudClient;
    private final SysKingdeeImageClient sysKingdeeImageClient;
    private final FinancialShareProp financialShareProp;
    private final FileLogic fileLogic;

    private final static String USER_TYPE = "UserName";

    /**
     * 获取发票云助手App Token
     * @return App Token
     */
    public String getShareAppToken() {
        JSONObject reqBody = new JSONObject();
        reqBody.put("appId", financialShareProp.getFinancialShare().getAppId());
        reqBody.put("appSecret", financialShareProp.getFinancialShare().getAppSecret());
        reqBody.put("accountId", financialShareProp.getFinancialShare().getAccountId());

        FinancialShareApiResultDTO invoiceCloudResult = sysKingdeeInvoiceCloudClient.getAppToken(reqBody);

        JSONObject data = handleApiResult(invoiceCloudResult, "获取发票云app_token");
        return data.getString("app_token");
    }

    /**
     * 获取发票云助手Access Token
     * @return Access Token
     */
    public String getShareAccessToken() {
        JSONObject reqBody = new JSONObject();
        reqBody.put("user", financialShareProp.getFinancialShare().getUser());
        reqBody.put("usertype", USER_TYPE);
        reqBody.put("apptoken", getShareAppToken());
        reqBody.put("accountId", financialShareProp.getFinancialShare().getAccountId());

        FinancialShareApiResultDTO invoiceCloudResult = sysKingdeeInvoiceCloudClient.getAccessToken(reqBody);
        JSONObject data = handleApiResult(invoiceCloudResult, "获取发票云access_token");
        return data.getString("access_token");
    }

    /**
     * 获取影像系统token
     * @return token
     */
    public String getImageAccessToken() {
        JSONObject reqBody = new JSONObject();
        reqBody.put("clientId", financialShareProp.getImage().getClientId());
        String checkSign = md5crypt(financialShareProp.getImage().getClientId() + financialShareProp.getImage().getClientSecret());
        reqBody.put("sign", checkSign);
        reqBody.put("fuserName", financialShareProp.getImage().getFuserName());

        KingdeeImageApiResultDTO imageApiResult = sysKingdeeImageClient.getAccessToken(reqBody);
        JSONObject data = handleApiResult(imageApiResult, "获取影像系统accessToken");
        return data.getString("accessToken");
    }

    /**
     * 获取财务共享单据页面跳转链接
     * @param formId 表单ID
     * @param pkId 单据ID
     * @return 跳转链接
     */
    public String getFinancialShareFormUrl(String formId, String pkId) {
        // 1. 参数校验
        if (financialShareProp == null || financialShareProp.getFinancialShare() == null) {
            return "";
        }
        if (StringUtils.isEmpty(pkId)) {
            return "";
        }
        String baseUrl = financialShareProp.getFinancialShare().getHost();
        if (StringUtils.isEmpty(baseUrl)) {
            return "";
        }
        String userId = LoginUtil.getAccount();
        if (StringUtils.isEmpty(userId)) {
            return "";
        }
        // 生成签名部分
        String appType = "zwznsrm";
        String sign = HmacUtil.hmacSha256(userId, appType, HmacUtil.SECRET_KEY);
        return baseUrl + "/?formId=" + formId + "&pkId=" + pkId + "&apptype=" + appType + "&target=" + sign;
    }

    /**
     * 附件上传到共享系统文件服务器并与单据的绑定
     * @param fileId SRM文件ID
     * @param billNo 单据编号
     * @param attachmentKey 附件面板标识
     * @param billNumber 单据标识
     */
    public void uploadFileForBill(Long fileId, String billNo, String attachmentKey, String billNumber) {
        String accessToken = getShareAccessToken();
        FileResposeDTO fileResposeDTO = fileLogic.getForDownload(fileId);
        if (fileResposeDTO == null) {
            throw new DaoException("文件不存在");
        }
        byte[] fileBytes;
        try {
            fileBytes = Files.readAllBytes(Paths.get(fileResposeDTO.getFilePath()));
        } catch (IOException e) {
            throw new DaoException("文件【" + fileResposeDTO.getFileName() + "】不存在");
        }
        JSONObject reqBody = new JSONObject();
        reqBody.put("billNo", billNo);
        reqBody.put("attachmentKey", attachmentKey);
        reqBody.put("billNumber", billNumber);

        FinancialShareApiResultDTO uploadResult = sysKingdeeInvoiceCloudClient.uploadFileForBill(accessToken, fileBytes, fileResposeDTO.getFileName(), reqBody);
        handleApiResult(uploadResult, "附件上传到共享系统文件服务器并与单据的绑定，文件【" + fileResposeDTO.getFileName() + "】");
    }

    /**
     * 批量上传文件到共享系统
     * @param fileIdList SRM文件ID列表
     * @param billNo 单据编号
     * @param attachmentKey 附件面板标识
     * @param billNumber 单据标识
     * @return 处理失败的信息
     */
    public List<String> uploadBatchFileForBill(List<Long> fileIdList, String billNo, String attachmentKey, String billNumber) {
        List<String> errorMessageList = new ArrayList<>();
        for (Long fileId : fileIdList) {
            try {
                uploadFileForBill(fileId, billNo, attachmentKey, billNumber);
            } catch (DaoException e) {
                errorMessageList.add(e.getMessage());
            }
        }
        return errorMessageList;
    }

    /**
     * 处理请求结果
     * @param apiResultDTO 请求结果
     * @param actionDescription 动作描述
     * @return 成功数据
     */
    public JSONObject handleApiResult(FinancialShareApiResultDTO apiResultDTO, String actionDescription) {
        if (apiResultDTO.getStatus()) {
            return apiResultDTO.getData();
        } else {
            String errorMessage = String.format(
                    "%s时发生错误，请联系管理员",
                    actionDescription
            );
            String logMessage = String.format(
                    "%s时发生错误，错误信息：%s",
                    actionDescription,
                    apiResultDTO.getMessage()
            );
            log.error(logMessage);
            throw new DaoException(errorMessage);
        }
    }

    /**
     * 处理请求结果
     * @param apiResultDTO 请求结果
     * @param actionDescription 动作描述
     * @return 成功数据
     */
    public JSONObject handleApiResult(KingdeeImageApiResultDTO apiResultDTO, String actionDescription) {
        if (StringUtils.equals("0000", apiResultDTO.getErrcode())) {
            return apiResultDTO.getData();
        } else {
            String errorMessage = String.format(
                    "%s时发生错误，请联系管理员",
                    actionDescription
            );
            String logMessage = String.format(
                    "%s时发生错误，错误信息：%s",
                    actionDescription,
                    apiResultDTO.getDescription()
            );
            log.error(logMessage);
            throw new DaoException(errorMessage);
        }
    }

    /**
     * 生成MD5签名
     * @param input 需要加密的字段
     * @return MD5加密后的32位小写十六进制字符串
     */
    public static String md5crypt(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes());

            StringBuilder hexString = new StringBuilder();
            for (byte b : digest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5算法不可用", e);
        }
    }
}
