package cn.iocoder.yudao.module.system.service.dingding;

import cn.iocoder.yudao.module.system.enums.dingding.Constants;
import cn.iocoder.yudao.module.system.enums.dingding.DingUrlConstant;
import cn.iocoder.yudao.module.system.util.dingding.DingAppConfig;
import cn.iocoder.yudao.module.system.util.dingding.RedisCache;
import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders;
import com.aliyun.dingtalkcontact_1_0.models.GetUserResponse;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.*;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;

import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author hzy
 * @description: 钉钉第三方应用授权
 * @param: null
 * @return:
 * @date: 2024/5/9 14:29
 */
@Service
@Slf4j
public class DingThirdTokenService {
    @Resource
    private DingAppConfig dingAppConfig;
    @Resource
    private RedisCache redisCache;



    public static Client createClient2_1_0() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new Client(config);
    }

    public static com.aliyun.dingtalkcontact_1_0.Client createClient_1_0() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkcontact_1_0.Client(config);
    }

    public GetUserTokenResponse getUserAccessToken(String code) {
        GetUserTokenResponse userToken = null;
        try {
            Client client = this.createClient2_1_0();
            GetUserTokenRequest request = new GetUserTokenRequest()
                    .setClientSecret(dingAppConfig.getAppSecret())
                    .setClientId(dingAppConfig.getAppKey())
                    .setCode(code).setGrantType(Constants.AUTHORIZATION_CODE);
            userToken = client.getUserToken(request);
        } catch (ApiException e) {
            log.error("getAccessToken failed", e);
        } catch (Exception e) {
            log.error("getAccessToken failed", e);
        }
        return userToken;
    }

    /**
     * 在此方法中，为了避免频繁获取access_token，
     * 在距离上一次获取access_token时间在两个小时之内的情况，
     * 将直接从持久化存储中读取access_token
     * <p>
     * 因为access_token和jsapi_ticket的过期时间都是7200秒
     * 所以在获取access_token的同时也去获取了jsapi_ticket
     * 注：jsapi_ticket是在前端页面JSAPI做权限验证配置的时候需要使用的
     * 具体信息请查看开发者文档--权限验证配置
     *
     * @return accessToken 或错误信息
     */
    public String getCorpAccessToken(String corpId) {
        String redisKeyPrefix = Constants.DAILY_DING_AUTH + corpId + ":";
        // 从持久化存储中读取
        String corpAccessToken = redisCache.getCacheObject(redisKeyPrefix + Constants.DAILY_DING_CORP_ACCESS_TOKEN);
        log.info("从Redis缓存中获取到的第三方企业{},corpAccessToken = {}", corpAccessToken);
        if (corpAccessToken != null) {
            return corpAccessToken;
        }
        try {
            com.aliyun.dingtalkoauth2_1_0.Client client = this.createClient2_1_0();
            String suiteTicket = redisCache.getCacheObject(redisKeyPrefix + Constants.DAILY_DING_SUITE_TICKET);
            log.info("从Redis缓存中获取到的第三方企业{},suiteTicket = {}", corpId, suiteTicket);
            com.aliyun.dingtalkoauth2_1_0.models.GetCorpAccessTokenRequest getCorpAccessTokenRequest = new com.aliyun.dingtalkoauth2_1_0.models.GetCorpAccessTokenRequest()
                    .setSuiteKey(dingAppConfig.getAppKey())
                    .setSuiteSecret(dingAppConfig.getAppSecret())
                    .setAuthCorpId(corpId)
                    .setSuiteTicket(suiteTicket);
            GetCorpAccessTokenResponse response = client.getCorpAccessToken(getCorpAccessTokenRequest);
            GetCorpAccessTokenResponseBody body = response.getBody();
            corpAccessToken = body.getAccessToken();
            Long expireIn = body.getExpireIn();
            log.info("从Redis缓存中获取到的第三方企业{},corpAccessToken = {}",corpId, corpAccessToken);
            redisCache.setCacheObject(Constants.DAILY_DING_CORP_ACCESS_TOKEN, corpAccessToken, expireIn.intValue(), TimeUnit.MINUTES);
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
            return corpAccessToken;
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
            return corpAccessToken;
        }
        return corpAccessToken;
    }


    //服务商获取第三方应用授权企业的access_token
    public String getThirdCorpAccessToken(String corpId,String suiteTicket) {
//        String redisKeyPrefix = Constants.DAILY_DING_AUTH + corpId + ":";
        // 从持久化存储中读取
        String corpAccessToken = null;
//        String corpAccessToken = redisCache.getCacheObject(redisKeyPrefix + Constants.DAILY_DING_CORP_ACCESS_TOKEN);
//        log.info("从Redis缓存中获取到的第三方企业{},corpAccessToken = {}", corpAccessToken);
//        if (corpAccessToken != null) {
//            return corpAccessToken;
//        }
        try {
            log.info("获取到的第三方企业{},suiteTicket = {}", corpId, suiteTicket);
            DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/service/get_corp_token");
            OapiServiceGetCorpTokenRequest req = new OapiServiceGetCorpTokenRequest();
            req.setAuthCorpid(corpId);
            OapiServiceGetCorpTokenResponse execute = client.execute(req, dingAppConfig.getAppKey(), dingAppConfig.getAppSecret(), suiteTicket);
            corpAccessToken = execute.getAccessToken();
            Long expireIn = execute.getExpiresIn();
//            log.info("从Redis缓存中获取到的第三方企业{},corpAccessToken = {}", execute.getAccessToken());
//            redisCache.setCacheObject(Constants.DAILY_DING_CORP_ACCESS_TOKEN, corpAccessToken, expireIn.intValue(), TimeUnit.MINUTES);
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
            return corpAccessToken;
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
            return corpAccessToken;
        }
        return corpAccessToken;
    }

    public OapiV2UserGetuserinfoResponse getUserUnfo(String code, String access_token) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo");
        OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
        req.setCode(code);
        OapiV2UserGetuserinfoResponse rsp = null;
        try {
            rsp = client.execute(req, access_token);
        } catch (ApiException e) {
            throw new RuntimeException(e);
        }
        return rsp;
    }

    /**
     *
     * @param userId
     * @param access_token
     * @return
     */
    public OapiV2UserGetResponse getUser(String userId, String access_token) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
        OapiV2UserGetResponse rsp = null;
        try {
            OapiV2UserGetRequest req = new OapiV2UserGetRequest();
            req.setUserid(userId);
            req.setLanguage("zh_CN");
            rsp = client.execute(req, access_token);
        } catch (ApiException e) {
            throw new RuntimeException(e);
        }
        return rsp;
    }

    /**
     *
     * @param UnionId
     * @param access_token
     * @return
     */
    public OapiUserGetbyunionidResponse getUserUnionId(String UnionId, String access_token) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
        OapiUserGetbyunionidResponse rsp = null;
        try {
            OapiUserGetbyunionidRequest req = new OapiUserGetbyunionidRequest();
            req.setUnionid(UnionId);
            rsp = client.execute(req, access_token);
        } catch (ApiException e) {
            throw new RuntimeException(e);
        }
        return rsp;
    }

    /**
     *
     * @param access_token
     * @return
     */
    public OapiV2DepartmentListsubResponse getDept(String access_token) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");
        OapiV2DepartmentListsubResponse rsp = null;
        try {
            OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
            req.setDeptId(1L);
            req.setLanguage("zh_CN");
            rsp = client.execute(req, access_token);
        } catch (ApiException e) {
            throw new RuntimeException(e);
        }
        return rsp;
    }



    public OapiV2UserGetResponse getAuthUser(String accessToken, String userId) {
        DingTalkClient client = new DefaultDingTalkClient(DingUrlConstant.URL_USER_GET_V2);
        OapiV2UserGetResponse response;
        OapiV2UserGetRequest request = new OapiV2UserGetRequest();
        request.setUserid(userId);
        request.setLanguage("en_US");
        try {
            response = client.execute(request, accessToken);
        } catch (ApiException e) {
            log.error("Failed to getUserName: " + e.getErrMsg());
            return null;
        }
        return response;
    }

    //获取应用管理后台免登的用户信息
    public GetSsoUserInfoResponse getSsoUserInfo(String xAcsDingtalkAccessToken, String code) {
        GetSsoUserInfoResponse ssoUserInfoWithOptions = null;
        try {
            com.aliyun.dingtalkoauth2_1_0.Client client = this.createClient2_1_0();
            GetSsoUserInfoHeaders getSsoUserInfoHeaders = new GetSsoUserInfoHeaders();
            getSsoUserInfoHeaders.xAcsDingtalkAccessToken = xAcsDingtalkAccessToken;
            GetSsoUserInfoRequest getSsoUserInfoRequest = new GetSsoUserInfoRequest()
                    .setCode(code);
            ssoUserInfoWithOptions = client.getSsoUserInfoWithOptions(getSsoUserInfoRequest, getSsoUserInfoHeaders, new RuntimeOptions());
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        }
        return ssoUserInfoWithOptions;
    }


    /**
     * @description: 获取用户通讯录个人信息
     * @param: accessToken
     * @param: unionId
     * @return: java.lang.Object
     * nick 用户的钉钉昵称。
     * avatarUrl头像URL。
     * mobile用户的手机号。
     * openId用户的openId。
     * unionId用户的unionId。
     * email用户的个人邮箱。
     * stateCode手机号对应的国家号。
     */
    public GetUserResponse getAddressBookUserInfo(String accessToken, String unionId) {
        GetUserResponse response = null;
        try {
            com.aliyun.dingtalkcontact_1_0.Client client = this.createClient_1_0();
            GetUserHeaders getUserHeaders = new GetUserHeaders();
            getUserHeaders.xAcsDingtalkAccessToken = accessToken;
            response = client.getUserWithOptions(unionId, getUserHeaders, new RuntimeOptions());
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        }
        return response;
    }


    /****************************************************企业应用授权***********************************************************/

    //获取第三方企业应用的suite_access_token
    public OapiServiceGetSuiteTokenResponse getSuiteToken(String corpId) {
        OapiServiceGetSuiteTokenResponse rsp = null;
        String redisKeyPrefix = Constants.DAILY_DING_AUTH + corpId + ":";
        // 从持久化存储中读取
        String corpAccessToken = redisCache.getCacheObject(redisKeyPrefix + Constants.DAILY_DING_CORP_ACCESS_TOKEN);
        log.info("从Redis缓存中获取到的第三方企业{},corpAccessToken = {}", corpAccessToken);
        try {
            String suiteTicket = redisCache.getCacheObject(redisKeyPrefix + Constants.DAILY_DING_SUITE_TICKET);
            log.info("从Redis缓存中获取到的第三方企业{},suiteTicket = {}", corpId, suiteTicket);
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/service/get_suite_token");
            OapiServiceGetSuiteTokenRequest req = new OapiServiceGetSuiteTokenRequest();
            req.setSuiteKey(dingAppConfig.getAppKey());
            req.setSuiteSecret(dingAppConfig.getAppSecret());
            req.setSuiteTicket(suiteTicket);
            rsp = client.execute(req);
            redisCache.setCacheObject(Constants.DAILY_DING_CORP_ACCESS_TOKEN, rsp.getSuiteAccessToken(), rsp.getExpiresIn().intValue(), TimeUnit.MINUTES);
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        }
        return rsp;
    }

    //获取企业授权信息
    public Object getAuthInfoRequest(String corpId) {
        OapiServiceGetAuthInfoResponse rsp = null;
        String redisKeyPrefix = Constants.DAILY_DING_AUTH + corpId + ":";
        // 从持久化存储中读取
        String corpAccessToken = redisCache.getCacheObject(redisKeyPrefix + Constants.DAILY_DING_CORP_ACCESS_TOKEN);
        log.info("从Redis缓存中获取到的第三方企业{},corpAccessToken = {}", corpAccessToken);
        try {
            String suiteTicket = redisCache.getCacheObject(redisKeyPrefix + Constants.DAILY_DING_SUITE_TICKET);
            log.info("从Redis缓存中获取到的第三方企业{},suiteTicket = {}", corpId, suiteTicket);
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/service/get_auth_info");
            OapiServiceGetAuthInfoRequest req = new OapiServiceGetAuthInfoRequest();
            req.setSuiteKey(suiteTicket);
            req.setAuthCorpid(corpId);
            // 第三方企业应用的填写应用SuiteKey和SuiteSecret。
            // 定制应用填写应用的CustomKey和CustomSecret。
            rsp = client.execute(req, dingAppConfig.getAppKey(), dingAppConfig.getAppSecret(), suiteTicket);
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        }
        return rsp;
    }

    //获取应用未激活的企业列表
    public OapiServiceGetUnactiveCorpResponse getUnactiveCorpResponse(Long appId, String suiteAccessToken) {
        OapiServiceGetUnactiveCorpResponse rsp = null;
        try {
            DingTalkClient client = new DefaultDingTalkClient(
                    "https://oapi.dingtalk.com/service/get_unactive_corp?suite_access_token=" + suiteAccessToken + "");
            OapiServiceGetUnactiveCorpRequest req = new OapiServiceGetUnactiveCorpRequest();
            req.setAppId(appId);
            rsp = client.execute(req);
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        }
        return rsp;
    }

    //重新授权未激活应用的企业
    public OapiServiceReauthCorpResponse getReauthCorpResponse(String appId, String suiteAccessToken, List<String> corpidList) {
        OapiServiceReauthCorpResponse rsp = null;
        try {
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/service/reauth_corp?suite_access_token=" + suiteAccessToken + "");
            OapiServiceReauthCorpRequest req = new OapiServiceReauthCorpRequest();
            req.setAppId(appId);
            req.setCorpidList(corpidList);
            rsp = client.execute(req);
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        }
        return rsp;
    }

    //获取企业开通应用后的授权信息
    public GetAuthInfoResponse getAuthInfoResponse(String corpId, String xAcsDingtalkAccessToken) {
        GetAuthInfoResponse rsp = null;
        String redisKeyPrefix = Constants.DAILY_DING_AUTH + corpId + ":";
        // 从持久化存储中读取
        try {
            GetAuthInfoHeaders getAuthInfoHeaders = new GetAuthInfoHeaders();
            getAuthInfoHeaders.xAcsDingtalkAccessToken = xAcsDingtalkAccessToken;
            GetAuthInfoRequest getAuthInfoRequest = new GetAuthInfoRequest()
                    .setAuthCorpId(corpId);
            // 第三方企业应用的填写应用SuiteKey和SuiteSecret。
            // 定制应用填写应用的CustomKey和CustomSecret。
            rsp = this.createClient2_1_0().getAuthInfoWithOptions(getAuthInfoRequest, getAuthInfoHeaders, new RuntimeOptions());
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("getAccessToken failed", err.message);
            }
        }
        return rsp;
    }

    /***********************************************************************************/
    //获取jsapiTicket
    public CreateJsapiTicketResponse createJsapiTicketResponse(String xAcsDingtalkAccessToken) {
        CreateJsapiTicketResponse rsp = null;
        try {
            com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketHeaders createJsapiTicketHeaders = new com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketHeaders();
            createJsapiTicketHeaders.xAcsDingtalkAccessToken = xAcsDingtalkAccessToken;
            rsp = this.createClient2_1_0().createJsapiTicketWithOptions(createJsapiTicketHeaders, new RuntimeOptions());
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        }
        return rsp;
    }
    //查询个人授权记录
    public GetPersonalAuthRuleResponse getPersonalAuthRule(String xAcsDingtalkAccessToken) {
        GetPersonalAuthRuleResponse rsp = null;
        try {
            GetPersonalAuthRuleHeaders getPersonalAuthRuleHeaders = new GetPersonalAuthRuleHeaders();
            getPersonalAuthRuleHeaders.xAcsDingtalkAccessToken = xAcsDingtalkAccessToken;
            rsp = this.createClient2_1_0().getPersonalAuthRuleWithOptions(getPersonalAuthRuleHeaders, new RuntimeOptions());
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        }
        return rsp;
    }

}