package com.kingdee.eas.custom.integration.base.util;

import com.kingdee.bos.BOSException;
import com.kingdee.bos.BOSObjectFactory;
import com.kingdee.bos.Context;
import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
import com.kingdee.bos.util.BOSObjectType;
import com.kingdee.bos.util.BOSUuid;
import com.kingdee.eas.base.permission.IUser;
import com.kingdee.eas.base.permission.UserCollection;
import com.kingdee.eas.base.permission.UserFactory;
import com.kingdee.eas.base.permission.UserInfo;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.cp.eip.sso.ltpa.LtpaTokenManager;
import com.kingdee.eas.cp.eip.sso.util.CASLoginConfigPropUtil;
import com.kingdee.eas.cp.eip.sso.util.CloudParamUtil;
import com.kingdee.eas.cp.eip.sso.util.PropertiesUtil;
import com.kingdee.eas.custom.integration.base.bizEnum.UserMappingFieldEnum;
import com.kingdee.eas.custom.integration.base.exception.CommonErrorCodes;
import com.kingdee.eas.custom.integration.base.exception.IntegrationException;
import com.kingdee.eas.framework.ICoreBillBase;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Description sHRApi工具类
 * @Date 2026/2/25 10:02
 * @Created by HeYuan
 */
public class SHRApiUtils {
    private final static Logger logger = LoggerFactory.getLogger(SHRApiUtils.class);

    //移动端域名
    public static final String MBOS_DOMAIN = "https://mbos.kdeascloud.com";
    //单据关键词
    private static final Set<String> BILL_KEY_WORDS = new HashSet<>();

    static {
        BILL_KEY_WORDS.add("AssignmentId");
        BILL_KEY_WORDS.add("assignId");
        BILL_KEY_WORDS.add("assignmentId");
        BILL_KEY_WORDS.add("billID");
        BILL_KEY_WORDS.add("billId");
    }


    /**
     * @param shrServerName sHR服务名(只包含ip:端口)
     * @param userNumber    账号
     * @param redirectUrl   重定向地址
     * @return
     * @throws Exception
     */
    public static String generateDirectLoginUrl(
            String shrServerName,
            String userNumber,
            String redirectUrl) {
        logger.info("单点sHR地址: shrServerName: {}, userNumber: {}, redirectUrl: {}", shrServerName, userNumber, redirectUrl);
        try {
            if (StringUtils.isBlank(shrServerName)) {
                throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "参数shrServerName不能为空");
            }
            if (StringUtils.isBlank(userNumber)) {
                throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "参数userNumber不能为空");
            }
            if (StringUtils.isBlank(redirectUrl)) {
                throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "参数redirectUrl不能为空");
            }
            // 生成LTPA token作为密码
            String password = LtpaTokenManager.generate(userNumber, LtpaTokenManager.getDefaultLtpaConfig()).toString();
            logger.info("生成LTPA token密码: {}", password);

            String utf8String = StandardCharsets.UTF_8.toString();
            // 使用 UriComponentsBuilder 安全构建 URL
            String targetUrl = UriComponentsBuilder.fromUriString(shrServerName + "/shr/index2sso.jsp")
                    .queryParam("username", URLEncoder.encode(userNumber, utf8String))
                    .queryParam("password", URLEncoder.encode(password, utf8String))
                    .queryParam("redirectTo", URLEncoder.encode(URLEncoder.encode(redirectUrl, utf8String), utf8String))
                    .build(true)
                    .toUriString();

            logger.info("构建单点sHR地址: {}", targetUrl);
            return targetUrl;
        } catch (Exception e) {
            logger.error("构建单点sHR地址失败: {}", e.getMessage(), e);
            throw new IntegrationException(CommonErrorCodes.BUILD_SSO_SHR_URL_FAILED, "构建单点sHR地址失败");
        }
    }

    /**
     * 生成单点MBos链接
     *
     * @param userNumber  用户账号
     * @param redirectUrl 重定向地址
     * @return
     */
    public static String generateToMBosUrl(String userNumber, String redirectUrl) throws UnsupportedEncodingException {
        logger.info("移动端单点，请求体：userNumber {}, redirectUrl: {}", userNumber, redirectUrl);
        if (StringUtils.isBlank(userNumber)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "参数userNumber不能为空");
        }
        if (StringUtils.isBlank(redirectUrl)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "参数redirectUrl不能为空");
        }
        // 假设移动端需要返回简化数据
        if (redirectUrl.startsWith(SHRApiUtils.MBOS_DOMAIN)) {
            //生成Mbos令牌
            return HttpUtils.appendUrlParam(redirectUrl, MbosTokenUtils.generateMbosTokenMap(userNumber));
        } else {
            logger.error("构建移动bos单点地址失败，重定向Mbos地址错误: {}", redirectUrl);
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "构建移动bos单点地址失败");
        }
    }


    /**
     * 根据映射字段获取用户信息
     *
     * @param userMappingField
     * @param number
     * @return
     * @throws Exception
     */
    public static UserInfo getUserInfo(
            UserMappingFieldEnum userMappingField,
            String number) {
        if (userMappingField == null) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "映射字段不存在");
        }
        UserInfo userInfo = null;
        if (UserMappingFieldEnum.USER_NUMBER.equals(userMappingField)) {
            userInfo = SHRApiUtils.getUserInfoByUserNumber(number);
        } else if (UserMappingFieldEnum.PERSON_NUMBER.equals(userMappingField)) {
            //使用工号映射
            userInfo = SHRApiUtils.getUserInfoByPeronNumber(number);
        } else {
            userInfo = SHRApiUtils.getUserInfoByUserNumber(number);
        }
        return userInfo;
    }

    /**
     * 根据工号获取用户信息
     *
     * @param personNumber 员工工号
     */
    public static UserInfo getUserInfoByPeronNumber(String personNumber) {
        if (StringUtils.isBlank(personNumber)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "参数personNumber不能为空");
        }
        try {
            // 创建上下文并获取飞书配置服务实例
            Context ctx = SHRApiUtils.getContext();

            IUser iUser = UserFactory.getLocalInstance(ctx);
            //没有被删除的用户
            UserCollection userCollection = iUser.getUserCollection("where isDelete='0' and person.number = '" + personNumber + "'");
            if (userCollection.isEmpty()) {
                logger.error("SHR没有对应的用户, personNumber:{}", personNumber);
                throw new IntegrationException(CommonErrorCodes.USER_NOT_EXIST, "sHR系统不存在工号[" + personNumber + "]，请联系管理员");
            } else if (userCollection.size() > 1) {
                logger.error("SHR用户存在多个, personNumber:{}", personNumber);
                throw new IntegrationException(CommonErrorCodes.USER_NOT_EXIST, "SHR用户存在多个，工号[" + personNumber + "]，请联系管理员");
            }
            return userCollection.get(0);
        } catch (BOSException e) {
            logger.error("获取用户信息失败: {}", e.getMessage(), e);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "获取用户信息失败");
        }
    }


    /**
     * 根据用户账号获取用户信息
     *
     * @param userNumber 用户账号
     */
    public static UserInfo getUserInfoByUserNumber(String userNumber) {
        if (StringUtils.isBlank(userNumber)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "参数userNumber不能为空");
        }

        try {
            // 创建上下文并获取飞书配置服务实例
            Context ctx = SHRApiUtils.getContext();

            IUser iUser = UserFactory.getLocalInstance(ctx);
            //没有被删除的用户
            UserCollection userCollection = iUser.getUserCollection("where isDelete='0' and number = '" + userNumber + "'");
            if (userCollection.isEmpty()) {
                logger.error("SHR没有对应的用户, userNumber:{}", userNumber);
                throw new IntegrationException(CommonErrorCodes.USER_NOT_EXIST, "sHR系统账号[" + userNumber + "]不存在，请联系管理员");
            } else if (userCollection.get(0).isIsDelete()) {
                logger.error("当前用户已禁用, userNumber:{}", userNumber);
                throw new IntegrationException(CommonErrorCodes.USER_DISABLED, "SHR系统账号[" + userNumber + "]已禁用，请联系管理员");
            }
            return userCollection.get(0);
        } catch (BOSException e) {
            logger.error("获取用户信息失败: {}", e.getMessage(), e);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "获取用户信息失败");
        }
    }

    /**
     * 获取系统上下文 Context 对象
     * <p>
     * 该方法从 CASLoginConfig.properties 配置文件中读取数据中心和语言配置，
     * 并创建管理员身份的上下文对象用于后续业务操作。
     *
     * @return Context 上下文对象，包含数据中心、语言和用户信息
     * @throws IntegrationException 当配置文件不存在或配置项缺失时抛出异常
     */
    public static Context getContext() {
        String fullName = PropertiesUtil.getDefaultFullName("CASLoginConfig.properties");
        File casLoginConfigPropFile = new File(fullName);
        if (!casLoginConfigPropFile.exists()) {
            logger.error("CASLoginConfig 配置文件不存在，请检查配置文件：{}", fullName);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "CASLoginConfig 配置文件不存在!");
        }

        // 获取数据中心和语言配置
        String dataCenter = CASLoginConfigPropUtil.getDataCenter();
        String locale = CASLoginConfigPropUtil.getLocale();
        logger.info("getFeishuConfigByAppId 数据中心代码：{}, 语言代码：{}", dataCenter, locale);

        if (StringUtils.isNotBlank(dataCenter) && StringUtils.isNotBlank(locale)) {
            // 创建上下文并获取飞书配置服务实例
            Context ctx = CloudParamUtil.getContext(dataCenter, locale, "administrator");
            if (ctx.getCaller() == null || StringUtils.isBlank(ctx.getCaller().toString())) {
                ctx.setCaller(new ObjectUuidPK("00000000-0000-0000-0000-00000000000013B7DE7F"));
            }
            return ctx;
        } else {
            logger.error("未获取到数据中心代码和语言代码，请检查配置文件：{}", fullName);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "未获取到数据中心代码和语言代码，请检查配置文件");
        }
    }


    /**
     * 获取系统上下文 Context 对象
     * <p>
     * 该方法从 CASLoginConfig.properties 配置文件中读取数据中心和语言配置，
     * 并创建管理员身份的上下文对象用于后续业务操作。
     *
     * @return Context 上下文对象，包含数据中心、语言和用户信息
     * @throws IntegrationException 当配置文件不存在或配置项缺失时抛出异常
     */
    public static Context getContext(String userNumber) {
        if (StringUtils.isBlank(userNumber)) {
            logger.error("SHRApiUtils getContext 用户名为空");
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "用户名为空");
        }
        String fullName = PropertiesUtil.getDefaultFullName("CASLoginConfig.properties");
        File casLoginConfigPropFile = new File(fullName);
        if (!casLoginConfigPropFile.exists()) {
            logger.error("CASLoginConfig 配置文件不存在，请检查配置文件：{}", fullName);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "CASLoginConfig 配置文件不存在!");
        }

        // 获取数据中心和语言配置
        String dataCenter = CASLoginConfigPropUtil.getDataCenter();
        String locale = CASLoginConfigPropUtil.getLocale();
        logger.info("getFeishuConfigByAppId 数据中心代码：{}, 语言代码：{}", dataCenter, locale);

        if (StringUtils.isNotBlank(dataCenter) && StringUtils.isNotBlank(locale)) {
            // 创建上下文并获取飞书配置服务实例
            Context ctx = CloudParamUtil.getContext(dataCenter, locale, userNumber);

            if (ctx.getCaller() == null || StringUtils.isBlank(ctx.getCaller().toString())) {
                ctx.setCaller(new ObjectUuidPK("00000000-0000-0000-0000-00000000000013B7DE7F"));
            }
            return ctx;
        } else {
            logger.error("未获取到数据中心代码和语言代码，请检查配置文件：{}", fullName);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "未获取到数据中心代码和语言代码，请检查配置文件");
        }
    }


    /**
     * 获取系统上下文 Context 对象
     * <p>
     * 该方法从 CASLoginConfig.properties 配置文件中读取数据中心和语言配置，
     * 并创建管理员身份的上下文对象用于后续业务操作。
     *
     * @return Context 上下文对象，包含数据中心、语言和用户信息
     * @throws IntegrationException 当配置文件不存在或配置项缺失时抛出异常
     */
    public static Context getContext(Context ctx) {
        if (ctx == null) {
            return getContext();
        } else {
            if (ctx.getCaller() == null || StringUtils.isBlank(ctx.getCaller().toString())) {
                ctx.setCaller(new ObjectUuidPK("00000000-0000-0000-0000-00000000000013B7DE7F"));
            }
            return ctx;
        }
    }


    /**
     * 校验单据是否存在
     *
     * @param redirect
     * @return
     * @throws BOSException
     * @throws SQLException
     */
    public static void checkBillExist(String redirect)
            throws UnsupportedEncodingException {
        logger.info("校验单据是否存在, 重定向地址: {}", redirect);
        if (StringUtils.isEmpty(redirect)) {
            throw new IntegrationException(CommonErrorCodes.REDIRECT_URL_EMPTY, "重定向地址不能为空!");
        }
        Map<String, String> urlParamValueMap = HttpUtils.getUrlParamValueMap(redirect);
        if (urlParamValueMap.isEmpty()) {
            return;
        }

        try {
            //流程id
            String assignmentId = urlParamValueMap.get("AssignmentId");
            if (StringUtils.isEmpty(assignmentId)) {
                assignmentId = urlParamValueMap.get("assignId");
            }
            if (StringUtils.isEmpty(assignmentId)) {
                assignmentId = urlParamValueMap.get("assignmentId");
            }
            //单据id
            String billID = urlParamValueMap.get("billID");
            if (!StringUtils.isEmpty(assignmentId) && !StringUtils.isEmpty(billID)) {
                //校验单据是否存在
                Context ctx = SHRApiUtils.getContext();
                billID = URLDecoder.decode(billID, "UTF-8");
                BOSObjectType type = BOSUuid.read(billID).getType();
                ICoreBillBase bosObject = (ICoreBillBase) BOSObjectFactory.createBOSObject(ctx, type);
                if (!bosObject.exists(new ObjectUuidPK(billID))) {
                    throw new IntegrationException(CommonErrorCodes.BILL_NOT_EXIST, "单据不存在，单据id[" + billID + "]");
                }
            }
        } catch (BOSException e) {
            logger.error("校验单据是否存在, 重定向地址: {}, 异常: {}", redirect, e.getMessage(), e);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "校验单据是否存在失败: " + e.getMessage(), e);
        } catch (EASBizException e) {
            logger.error("校验单据是否存在, 重定向地址: {}, 异常: {}", redirect, e.getMessage(), e);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "校验单据是否存在失败: " + e.getMessage(), e);
        }
    }
}
