package com.jeeplus.auth.service;

import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jeeplus.auth.config.DingTalkProperties;
import com.jeeplus.common.redis.RedisUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class DingTalkAuthService {

    private static final String CACHE_NAME = "dingtalk:auth";
    private static final String ACCESS_TOKEN_KEY = "accessToken:";
    private static final String BIND_KEY = "bind:";
    private static final long ACCESS_TOKEN_EXPIRE_SECONDS = 7000L;
    private static final long BIND_KEY_EXPIRE_SECONDS = 600L;

    @Autowired
    private DingTalkProperties dingTalkProperties;

    @Autowired
    private RedisUtils redisUtils;

    public DingTalkProperties.TenantConfig getTenantConfig(String tenantId) {
        return dingTalkProperties.getRequiredConfig(tenantId);
    }

    public DingTalkUser getUserByAuthCode(String tenantId, String authCode) {
        if (StringUtils.isBlank(authCode)) {
            throw new IllegalArgumentException("DingTalk authCode is required");
        }
        String accessToken = getAccessToken(tenantId);
        JSONObject requestBody = JSONUtil.createObj().set("code", authCode);
        String response = HttpRequest
                .post("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token=" + URLUtil.encodeQuery(accessToken))
                .body(requestBody.toString())
                .timeout(10000)
                .execute()
                .body();
        JSONObject responseJson = JSONUtil.parseObj(response);
        Integer errCode = responseJson.getInt("errcode");
        if (errCode == null || errCode != 0) {
            throw new IllegalStateException("DingTalk get user info failed: " + responseJson.getStr("errmsg"));
        }
        JSONObject result = responseJson.getJSONObject("result");
        if (result == null || StringUtils.isBlank(result.getStr("userid"))) {
            throw new IllegalStateException("DingTalk user id is empty");
        }
        return new DingTalkUser(result.getStr("userid"), result.getStr("unionid"));
    }

    public String createBindKey(String tenantId, String ddId) {
        String bindKey = UUID.randomUUID().toString().replace("-", "");
        redisUtils.set(CACHE_NAME, BIND_KEY + bindKey, tenantId + "|" + ddId);
        redisUtils.expire(CACHE_NAME, BIND_KEY + bindKey, BIND_KEY_EXPIRE_SECONDS);
        return bindKey;
    }

    public BindInfo getBindInfo(String bindKey) {
        Object value = redisUtils.get(CACHE_NAME, BIND_KEY + bindKey);
        if (value == null) {
            throw new IllegalArgumentException("DingTalk bind key is expired");
        }
        String[] parts = value.toString().split("\\|", 2);
        if (parts.length != 2 || StringUtils.isBlank(parts[0]) || StringUtils.isBlank(parts[1])) {
            throw new IllegalArgumentException("DingTalk bind key is invalid");
        }
        return new BindInfo(parts[0], parts[1]);
    }

    public void removeBindKey(String bindKey) {
        redisUtils.delete(CACHE_NAME, BIND_KEY + bindKey);
    }

    private String getAccessToken(String tenantId) {
        String cacheKey = ACCESS_TOKEN_KEY + StringUtils.defaultIfBlank(tenantId, dingTalkProperties.getDefaultTenantId());
        Object cachedToken = redisUtils.get(CACHE_NAME, cacheKey);
        if (cachedToken != null && StringUtils.isNotBlank(cachedToken.toString())) {
            return cachedToken.toString();
        }
        DingTalkProperties.TenantConfig tenantConfig = dingTalkProperties.getRequiredConfig(tenantId);
        String url = "https://oapi.dingtalk.com/gettoken?appkey=" + URLUtil.encodeQuery(tenantConfig.getResolvedAppKey())
                + "&appsecret=" + URLUtil.encodeQuery(tenantConfig.getAppSecret());
        String response = HttpRequest.get(url).timeout(10000).execute().body();
        JSONObject responseJson = JSONUtil.parseObj(response);
        Integer errCode = responseJson.getInt("errcode");
        if (errCode == null || errCode != 0 || StringUtils.isBlank(responseJson.getStr("access_token"))) {
            throw new IllegalStateException("DingTalk access token failed: " + responseJson.getStr("errmsg"));
        }
        String accessToken = responseJson.getStr("access_token");
        redisUtils.set(CACHE_NAME, cacheKey, accessToken);
        redisUtils.expire(CACHE_NAME, cacheKey, ACCESS_TOKEN_EXPIRE_SECONDS);
        return accessToken;
    }

    @Data
    @AllArgsConstructor
    public static class DingTalkUser {
        private String userId;
        private String unionId;
    }

    @Data
    @AllArgsConstructor
    public static class BindInfo {
        private String tenantId;
        private String ddId;
    }
}
