package com.ruoyi.system.service.dingding;

import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
import com.aliyun.teaopenapi.models.Config;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.request.OapiGetJsapiTicketRequest;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.response.OapiGetJsapiTicketResponse;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.dingding.config.DingAppConfig;
import com.ruoyi.common.utils.dingding.config.DingUrlConstant;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * <p>DingLoginController 此类用于：获取access_token 和 jsTicket方法</p>
 * <p>@remark：钉钉企业内部微应用DEMO, 实现了身份验证（免登）功能</p>
 */
@Slf4j
@Service
public class DingTokenService {

    @Resource
    private RedisCache redisCache;

    /**
     * 缓存时间：一小时50分钟
     */
    private static final Integer CACHE_TTL = 60 * 55 * 2;

    @Resource
    private DingAppConfig dingAppConfig;

    /**
     * 在此方法中，为了避免频繁获取access_token，
     * 在距离上一次获取access_token时间在两个小时之内的情况，
     * 将直接从持久化存储中读取access_token
     * <p>
     * 因为access_token和jsapi_ticket的过期时间都是7200秒
     * 所以在获取access_token的同时也去获取了jsapi_ticket
     * 注：jsapi_ticket是在前端页面JSAPI做权限验证配置的时候需要使用的
     * 具体信息请查看开发者文档--权限验证配置
     *
     * @return accessToken 或错误信息
     */
    public AjaxResult getAccessToken() {
        // 从持久化存储中读取
        String accessToken = redisCache.getCacheObject(Constants.DAILY_DING_ACCESS_TOKEN);
        log.info("从Redis缓存中获取到的accessToken = {}", accessToken);
        if (accessToken != null) {
            return AjaxResult.success(accessToken);
        }

        DefaultDingTalkClient client = new DefaultDingTalkClient(DingUrlConstant.URL_GET_TOKEN);
        OapiGettokenRequest request = new OapiGettokenRequest();
        OapiGettokenResponse response;

        request.setAppkey(dingAppConfig.getAppKey());
        request.setAppsecret(dingAppConfig.getAppSecret());
        request.setHttpMethod("GET");

        try {
            response = client.execute(request);
        } catch (ApiException e) {
            log.error("getAccessToken failed", e);
            return AjaxResult.error(e.getErrCode(), e.getErrMsg());
        }

        accessToken = response.getAccessToken();
        log.info("向Redis缓存中存取accessToken = {}", accessToken);

        redisCache.setCacheObject(Constants.DAILY_DING_ACCESS_TOKEN, accessToken, CACHE_TTL, TimeUnit.MINUTES);

        return AjaxResult.success(accessToken);
    }

    /**
     * 使用 Token 初始化账号Client
     * @return Client
     * @throws Exception
     */
    public static Client createClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new Client(config);
    }

    /**
     *  获取用户toKen
     * @return
     */
    public AjaxResult getUserAccessToken(String code){
        try {
            Client client = this.createClient();
            GetUserTokenRequest request = new GetUserTokenRequest()
                    .setClientSecret(dingAppConfig.getAppSecret())
                    .setClientId(dingAppConfig.getAppKey())
                    .setCode(code).setGrantType(Constants.AUTHORIZATION_CODE);

            GetUserTokenResponse response;

            response = client.getUserToken(request);

            return AjaxResult.success(response.getBody().getAccessToken(),response.getBody());

        } catch (ApiException e) {
            log.error("getAccessToken failed", e);
            return AjaxResult.error(e.getErrCode(), e.getErrMsg());
        } catch (Exception e) {
            log.error("getAccessToken failed", e);
            return AjaxResult.error(e.hashCode(), e.getMessage());
        }

    }

    /**
     * 获取JSTicket, 用于js的签名计算
     * 正常的情况下，jsapi_ticket的有效期为7200秒，所以开发者需要在某个地方设计一个定时器，定期去更新jsapi_ticket
     *
     * @return jsTicket或错误信息
     */
    public AjaxResult getJsTicket() {
        // 从持久化存储中读取
        String ticket = redisCache.getCacheObject(Constants.DAILY_DING_JS_TICKET);
        if (ticket != null) {
            return AjaxResult.success(ticket);
        }

        String accessToken;
        AjaxResult tokenSr = getAccessToken();
        if (!tokenSr.isSuccess()) {
            return tokenSr;
        }
        accessToken = (String) tokenSr.get(tokenSr.MSG_TAG);

        DefaultDingTalkClient client = new DefaultDingTalkClient(DingUrlConstant.URL_GET_JSTICKET);
        OapiGetJsapiTicketRequest request = new OapiGetJsapiTicketRequest();
        OapiGetJsapiTicketResponse response;

        request.setHttpMethod("GET");

        try {
            response = client.execute(request, accessToken);
        } catch (ApiException e) {
            log.error("getAccessToken failed", e);
            return AjaxResult.error(e.getErrCode(), e.getErrMsg());
        }

        if (!response.isSuccess()) {
            return AjaxResult.error(response.getErrorCode(), response.getErrmsg());
        }
        ticket = response.getTicket();
        redisCache.setCacheObject(Constants.DAILY_DING_JS_TICKET, ticket, CACHE_TTL, TimeUnit.SECONDS);

        return AjaxResult.success(ticket);
    }


}