package com.tiesheng.ding.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.LogFactory;
import com.tiesheng.ding.pojos.*;
import com.tiesheng.util.TimedCacheHelper;
import com.tiesheng.util.exception.ApiException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.*;
import java.util.function.Consumer;

/**
 * @author hao
 */
@Configuration
@ConfigurationProperties(prefix = "tiesheng.ding")
public class DingConfig {

    private static final String CACHE_ACCESS_TOKEN = "cache_ding_access_token_";
    private static final String CACHE_JSAPI_TICKET = "cache_ding_jsapi_ticket_";


    private Map<String, DingConfigBean> configs = MapUtil.newHashMap();
    private DingConfigBean global;

    /**
     * 获取一个DingConfigBean
     *
     * @param service
     * @return
     */
    public DingConfigBean getConfigBean(String service) {
        DingConfigBean bean = configs.get(service);
        if (bean == null) {
            bean = global;
        }
        if (bean == null) {
            throw new ApiException("该服务未配置钉钉授权");
        }
        return bean;
    }

    ///////////////////////////////////////////////////////////////////////////
    // 逻辑方法
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 获取accessToken
     *
     * @return accessToken
     * @see <a href="https://open.dingtalk.com/document/orgapp-server/obtain-orgapp-token" />
     */
    private String getAccessToken(String service) {
        DingConfigBean dingConfigBean = getConfigBean(service);
        String accessToken = TimedCacheHelper.getTimedCache().get(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(), false);
        if (!StrUtil.isEmpty(accessToken)) {
            return accessToken;
        }

        // 重新获取新的token
        Map<String, Object> query = new HashMap<>(10);
        query.put("appkey", dingConfigBean.getAppKey());
        query.put("appsecret", dingConfigBean.getAppSecret());
        String response = HttpUtil.get("https://oapi.dingtalk.com/gettoken", query);
        JSONObject respJson = JSONUtil.parseObj(response);
        accessToken = respJson.getStr("access_token");
        TimedCacheHelper.getTimedCache().put(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(), accessToken, respJson.getLong("expires_in"));

        return accessToken;
    }


    /**
     * 获取jsapi_ticket
     *
     * @return ticket
     * @see <a href="https://open.dingtalk.com/document/isvapp-server/obtain-jsapi_ticket" />
     */
    private String getJsapiTicket(String service) {
        DingConfigBean dingConfigBean = getConfigBean(service);
        String jsapiTicket = TimedCacheHelper.getTimedCache().get(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(), false);
        if (StrUtil.isEmpty(jsapiTicket)) {
            String response = HttpUtil.get("https://oapi.dingtalk.com/get_jsapi_ticket?access_token=" + getAccessToken(service));
            LogFactory.get().info("getJsapiTicket: " + response);
            JSONObject respJson = JSONUtil.parseObj(response);
            if (!Objects.equals(respJson.getStr("errcode"), "0")) {
                throw new ApiException(respJson.getStr("errmsg"));
            }
            jsapiTicket = respJson.getStr("ticket");
            TimedCacheHelper.getTimedCache().put(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(), jsapiTicket, respJson.getLong("expires_in"));
        }
        return jsapiTicket;
    }


    /**
     * 生成jssdk配置
     *
     * @param url
     * @return
     */
    public DingJsapiSignature createJsapiSignature(String service, String url) {
        DingConfigBean dingConfigBean = getConfigBean(service);
        if (StrUtil.contains(url, "#")) {
            url = StrUtil.sub(url, 0, StrUtil.indexOf(url, '#'));
        }
        return DingJsapiSignature.create(dingConfigBean.getCorpId(), dingConfigBean.getAgentId(), url, getJsapiTicket(service));
    }


    /**
     * 通过code获取userId
     *
     * @param service
     * @param code
     * @return
     * @see <a href="https://open.dingtalk.com/document/isvapp-server/obtain-the-userid-of-a-user-by-using-the-log-free"></a>
     */
    public String getUserIdByCode(String service, String code) {
        String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token=" + getAccessToken(service))
                .body(JSONUtil.createObj().putOpt("code", code).toString()).execute().body();
        return JSONUtil.parseObj(response).getJSONObject("result").getStr("userid");
    }


    /**
     * 通过userId获取用户详情
     *
     * @param service
     * @param ddUserId
     * @return
     * @see <a href="https://open.dingtalk.com/document/isvapp-server/query-user-details"></a>
     */
    public DingUserInfo topapiV2UserGet(String service, String ddUserId) {
        DingConfigBean dingConfigBean = getConfigBean(service);
        String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/user/get?access_token=" + getAccessToken(service))
                .body(JSONUtil.createObj().putOpt("userid", ddUserId).toString()).execute().body();
        JSONObject resultJson = JSONUtil.parseObj(response).getJSONObject("result");
        DingUserInfo userInfo = JSONUtil.toBean(resultJson, DingUserInfo.class);

        // 设置一下job_number
        userInfo.setJobNumber(resultJson.getStr("job_number"));
        userInfo.setAppId(dingConfigBean.getAppKey());
        return userInfo;
    }

    /**
     * 获取部门列表
     *
     * @param deptId 部门id
     * @return
     * @see <a href='https://open.dingtalk.com/document/orgapp-server/obtain-the-department-list-v2'></a>
     */
    public List<DingDeptVo> topapiV2DepartmentListsub(String service, String deptId) {
        String token = getAccessToken(service);
        if (StrUtil.isEmpty(token)) {
            return new ArrayList<>();
        }
        String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/department/listsub?access_token=" + token)
                .body(JSONUtil.createObj().putOpt("dept_id", deptId).toString()).execute().body();
        DingResponse<List<DingDeptVo>> result = JSONUtil.toBean(response, new TypeReference<DingResponse<List<DingDeptVo>>>() {
        }.getType(), true);

        if (!result.isOk()) {
            result.setResult(new ArrayList<>());
        }
        return result.getResult();
    }


    /**
     * 获取部门用户详情
     *
     * @return
     * @see <a href='https://open.dingtalk.com/document/orgapp-server/queries-the-complete-information-of-a-department-user'></a>
     */
    public DingUserListVo topapiV2UserList(String service, String deptId, Integer cursor) {
        String token = getAccessToken(service);
        if (StrUtil.isEmpty(token)) {
            return DingUserListVo.fail();
        }

        String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/user/list?access_token=" + token)
                .body(JSONUtil.createObj().putOpt("dept_id", deptId).putOpt("cursor", cursor).putOpt("size", 100).toString())
                .execute().body();
        DingResponse<DingUserListVo> result = JSONUtil.toBean(response, new TypeReference<DingResponse<DingUserListVo>>() {
        }.getType(), true);
        if (!result.isOk()) {
            result.setResult(DingUserListVo.fail());
        }
        return result.getResult();
    }

    /**
     * 同步钉钉的通讯录
     *
     * @param deptVo   为null时从第一级获取
     * @param consumer 回调
     */
    public void syncDeptUser(String service, DingDeptVo deptVo, Consumer<DingUserInfo> consumer) {

        if (deptVo == null) {
            deptVo = new DingDeptVo();
            deptVo.setDeptId("1");
        }

        // 同步当前部门的用户
        DingUserListVo userListVo = new DingUserListVo();
        userListVo.setNextCursor(0);
        userListVo.setHasMore(true);
        while (userListVo.getHasMore()) {
            userListVo = topapiV2UserList(service, deptVo.getDeptId(), userListVo.getNextCursor());
            for (DingUserInfo dingUserInfo : userListVo.getList()) {
                dingUserInfo.setDeptVo(deptVo);
                consumer.accept(dingUserInfo);
            }
        }

        // 同步下级部门
        List<DingDeptVo> dingDeptVos = topapiV2DepartmentListsub(service, deptVo.getDeptId());
        for (DingDeptVo dingDeptVo : dingDeptVos) {
            syncDeptUser(service, dingDeptVo, consumer);
        }
    }


    /**
     * 发送工作通知-
     *
     * @param service
     * @param content
     * @param actionUrl
     * @param userIds
     * @return
     */
    public String messageNotification(String service, String title, String content, String actionUrl, List<String> userIds) {

        if (CollUtil.isEmpty(userIds)) {
            return "";
        }

        DingConfigBean configBean = getConfigBean(service);

        JSONObject actionCard = new JSONObject();
        actionCard.set("title", title);
        actionCard.set("markdown", "### " + title + "\n" + content);
        actionCard.set("single_title", "点击查看");
        actionCard.set("single_url", actionUrl);

        JSONObject msg = new JSONObject();
        msg.set("msgtype", "action_card");
        msg.set("action_card", actionCard);

        HashMap<String, Object> body = new HashMap<>(10);
        body.put("agent_id", configBean.getAgentId());
        body.put("userid_list", CollUtil.join(userIds, ","));
        body.put("msg", msg);
        return HttpUtil.post("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=" + getAccessToken(service),
                JSONUtil.toJsonStr(body));
    }

    ///////////////////////////////////////////////////////////////////////////
    // setter\getter
    ///////////////////////////////////////////////////////////////////////////

    public Map<String, DingConfigBean> getConfigs() {
        return configs;
    }

    public void setConfigs(Map<String, DingConfigBean> configs) {
        this.configs = configs;
    }

    public DingConfigBean getGlobal() {
        return global;
    }

    public void setGlobal(DingConfigBean global) {
        this.global = global;
    }
}
