package com.cool.modules.dingtalk.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 钉钉API工具类
 * 封装钉钉开放平台API调用，支持AccessToken管理、重试机制等
 */
@Slf4j
@Component
public class DingtalkApiUtil {

    // 钉钉API基础URL
    private static final String DINGTALK_API_BASE_URL = "https://oapi.dingtalk.com";

    // AccessToken缓存
    private String accessToken;
    private Long expiresTime;

    // 钉钉配置（从数据库读取后设置）
    private String appKey;
    private String appSecret;

    /**
     * 设置钉钉配置
     *
     * @param appKey    应用Key
     * @param appSecret 应用Secret
     */
    public void setConfig(String appKey, String appSecret) {
        log.info("【钉钉API】设置钉钉配置, appKey: {}", appKey);
        this.appKey = appKey;
        this.appSecret = appSecret;
        // 清空缓存的token
        this.accessToken = null;
        this.expiresTime = null;
    }

    /**
     * 获取AccessToken（支持自动刷新）
     *
     * @return AccessToken
     */
    public synchronized String getAccessToken() {
        log.info("【钉钉API】获取AccessToken, 当前token是否过期: {}", isExpiringSoon());

        // 如果token不存在或即将过期，重新获取
        if (accessToken == null || isExpiringSoon()) {
            refreshAccessToken();
        }

        return accessToken;
    }

    /**
     * 判断Token是否即将过期（剩余时间小于5分钟）
     */
    private boolean isExpiringSoon() {
        if (expiresTime == null) {
            return true;
        }
        long currentTime = System.currentTimeMillis();
        long remainingTime = expiresTime - currentTime;
        return remainingTime < TimeUnit.MINUTES.toMillis(5);
    }

    /**
     * 刷新AccessToken
     */
    private void refreshAccessToken() {
        log.info("【钉钉API】开始刷新AccessToken");

        if (StrUtil.isBlank(appKey) || StrUtil.isBlank(appSecret)) {
            log.error("【钉钉API】appKey或appSecret未配置");
            throw new RuntimeException("钉钉配置未完成，请先配置AppKey和AppSecret");
        }

        try {
            String url = DINGTALK_API_BASE_URL + "/gettoken";
            Map<String, Object> params = new HashMap<>();
            params.put("appkey", appKey);
            params.put("appsecret", appSecret);

            HttpResponse response = HttpRequest.get(url)
                    .form(params)
                    .timeout(10000)
                    .execute();

            String body = response.body();
            log.info("【钉钉API】获取AccessToken响应: {}", body);

            JSONObject jsonObject = JSONUtil.parseObj(body);
            Integer errcode = jsonObject.getInt("errcode");

            if (errcode != null && errcode == 0) {
                this.accessToken = jsonObject.getStr("access_token");
                Integer expiresIn = jsonObject.getInt("expires_in");
                // 计算过期时间（当前时间 + 过期秒数）
                this.expiresTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiresIn);

                log.info("【钉钉API】AccessToken刷新成功, token: {}, 有效期: {}秒", accessToken, expiresIn);
            } else {
                String errmsg = jsonObject.getStr("errmsg", "未知错误");
                log.error("【钉钉API】获取AccessToken失败, errcode: {}, errmsg: {}", errcode, errmsg);
                throw new RuntimeException("获取钉钉AccessToken失败: " + errmsg);
            }
        } catch (Exception e) {
            log.error("【钉钉API】刷新AccessToken异常", e);
            throw new RuntimeException("刷新钉钉AccessToken异常: " + e.getMessage(), e);
        }
    }

    /**
     * 调用钉钉API（POST请求，支持重试）
     *
     * @param apiPath API路径（如 /topapi/v2/department/listsub）
     * @param params  请求参数
     * @return API响应JSON对象
     */
    public JSONObject callApi(String apiPath, Map<String, Object> params) {
        return callApi(apiPath, params, 3);
    }

    /**
     * 调用钉钉API（POST请求，支持重试）
     *
     * @param apiPath    API路径
     * @param params     请求参数
     * @param retryTimes 重试次数
     * @return API响应JSON对象
     */
    private JSONObject callApi(String apiPath, Map<String, Object> params, int retryTimes) {
        log.info("【钉钉API】调用API: {}, 参数: {}", apiPath, params);

        String token = getAccessToken();
        String url = DINGTALK_API_BASE_URL + apiPath + "?access_token=" + token;

        try {
            // 将params转换为JSON字符串
            String jsonBody = JSONUtil.toJsonStr(params);

            HttpResponse response = HttpRequest.post(url)
                    .body(jsonBody)
                    .contentType("application/json")
                    .timeout(15000)
                    .execute();

            String body = response.body();
            log.info("【钉钉API】API响应: {}", body);

            JSONObject jsonObject = JSONUtil.parseObj(body);
            Integer errcode = jsonObject.getInt("errcode");

            if (errcode != null && errcode == 0) {
                return jsonObject;
            } else if (errcode != null && errcode == 40014) {
                // Token过期，重新获取token后重试
                log.warn("【钉钉API】AccessToken过期, 重新获取后重试");
                refreshAccessToken();
                if (retryTimes > 0) {
                    return callApi(apiPath, params, retryTimes - 1);
                }
            } else if (errcode != null && (errcode == 60011 || errcode == 60020)) {
                // 请求频繁或系统繁忙，延迟重试
                log.warn("【钉钉API】请求频繁或系统繁忙, errcode: {}, 延迟后重试", errcode);
                if (retryTimes > 0) {
                    try {
                        Thread.sleep(1000 * (4 - retryTimes)); // 指数退避
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    return callApi(apiPath, params, retryTimes - 1);
                }
            }

            String errmsg = jsonObject.getStr("errmsg", "未知错误");
            log.error("【钉钉API】API调用失败, errcode: {}, errmsg: {}", errcode, errmsg);
            throw new RuntimeException("钉钉API调用失败: " + errmsg);

        } catch (Exception e) {
            log.error("【钉钉API】API调用异常: {}", apiPath, e);
            if (retryTimes > 0) {
                log.info("【钉钉API】重试调用, 剩余次数: {}", retryTimes);
                return callApi(apiPath, params, retryTimes - 1);
            }
            throw new RuntimeException("钉钉API调用异常: " + e.getMessage(), e);
        }
    }

    /**
     * 获取部门子部门列表
     *
     * @param deptId 父部门ID（根部门传1）
     * @return 部门列表JSON
     */
    public JSONObject getDepartmentListSub(Long deptId) {
        log.info("【钉钉API】获取部门子部门列表, deptId: {}", deptId);
        Map<String, Object> params = new HashMap<>();
        params.put("dept_id", deptId);
        return callApi("/topapi/v2/department/listsub", params);
    }

    /**
     * 获取部门用户列表（分页）
     *
     * @param deptId 部门ID
     * @param cursor 分页游标（首次传0）
     * @param size   每页大小（最大100）
     * @return 用户列表JSON
     */
    public JSONObject getUserList(Long deptId, Long cursor, Integer size) {
        log.info("【钉钉API】获取部门用户列表, deptId: {}, cursor: {}, size: {}", deptId, cursor, size);
        Map<String, Object> params = new HashMap<>();
        params.put("dept_id", deptId);
        params.put("cursor", cursor);
        params.put("size", size);
        return callApi("/topapi/v2/user/list", params);
    }

    /**
     * 获取用户详情
     *
     * @param userId 钉钉用户ID
     * @return 用户详情JSON
     */
    public JSONObject getUserDetail(String userId) {
        log.info("【钉钉API】获取用户详情, userId: {}", userId);
        Map<String, Object> params = new HashMap<>();
        params.put("userid", userId);
        return callApi("/topapi/v2/user/get", params);
    }
}
