package com.ruoyi.jarvis.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.jarvis.config.KdocsCloudConfig;
import com.ruoyi.jarvis.domain.dto.KdocsTokenInfo;
import com.ruoyi.jarvis.service.IKdocsOAuthService;
import com.ruoyi.jarvis.service.IKdocsOpenApiService;
import com.ruoyi.jarvis.util.KdocsOpenApiClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class KdocsOAuthServiceImpl implements IKdocsOAuthService {

    private static final Logger log = LoggerFactory.getLogger(KdocsOAuthServiceImpl.class);
    private static final String TOKEN_KEY_PREFIX = "kdocs:token:";
    private static final long TOKEN_EXPIRE_SECONDS = 30L * 24 * 60 * 60;

    @Autowired
    private KdocsCloudConfig kdocsCloudConfig;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IKdocsOpenApiService kdocsOpenApiService;

    private static String enc(String s) {
        try {
            return URLEncoder.encode(s == null ? "" : s, "UTF-8").replace("+", "%20");
        } catch (UnsupportedEncodingException e) {
            throw new AssertionError(e);
        }
    }

    @Override
    public String getAuthUrl(String state) {
        if (StringUtils.isBlank(kdocsCloudConfig.getAppId())) {
            throw new RuntimeException("kdocs.app-id 未配置");
        }
        if (StringUtils.isBlank(kdocsCloudConfig.getRedirectUri())) {
            throw new RuntimeException("kdocs.redirect-uri 未配置");
        }
        String redirect = kdocsCloudConfig.getRedirectUri().trim();
        if (redirect.endsWith("/") && redirect.length() > "https://".length()) {
            redirect = redirect.substring(0, redirect.length() - 1);
        }
        if (StringUtils.isBlank(state)) {
            state = UUID.randomUUID().toString();
        }
        String scope = kdocsCloudConfig.getScope() != null ? kdocsCloudConfig.getScope().trim() : "user_basic,access_personal_files,edit_personal_files";
        return kdocsCloudConfig.getApiHost().replaceAll("/$", "")
            + "/h5/auth?app_id=" + enc(kdocsCloudConfig.getAppId())
            + "&scope=" + enc(scope)
            + "&redirect_uri=" + enc(redirect)
            + "&state=" + enc(state);
    }

    @Override
    public KdocsTokenInfo getAccessTokenByCode(String code) {
        String host = kdocsCloudConfig.getApiHost().replaceAll("/$", "");
        String url = host + "/api/v1/oauth2/access_token?code=" + enc(code)
            + "&app_id=" + enc(kdocsCloudConfig.getAppId())
            + "&app_key=" + enc(kdocsCloudConfig.getAppKey());
        JSONObject root = KdocsOpenApiClient.exchange("GET", url, null, true);
        JSONObject data = KdocsOpenApiClient.requireBusinessData(root);

        KdocsTokenInfo info = new KdocsTokenInfo();
        info.setAccessToken(data.getString("access_token"));
        info.setRefreshToken(data.getString("refresh_token"));
        Integer exp = data.getInteger("expires_in");
        if (exp == null && data.get("expires_in") != null) {
            try {
                exp = Integer.parseInt(data.get("expires_in").toString());
            } catch (NumberFormatException ignored) {
            }
        }
        info.setExpiresIn(exp != null ? exp : 7200);

        String openId = null;
        try {
            JSONObject user = kdocsOpenApiService.getUserInfoFlat(info.getAccessToken());
            openId = user != null ? user.getString("open_id") : null;
            if (openId == null && user != null) {
                openId = user.getString("user_id");
            }
        } catch (Exception e) {
            log.warn("换取 token 后拉取用户信息失败: {}", e.getMessage());
        }
        if (StringUtils.isBlank(openId)) {
            String at = info.getAccessToken();
            openId = at != null && at.length() > 12 ? "kdocs_" + at.substring(0, 12) : "kdocs_default";
        }
        info.setUserId(openId);
        return info;
    }

    @Override
    public KdocsTokenInfo refreshAccessToken(String refreshToken, String existingUserId) {
        String host = kdocsCloudConfig.getApiHost().replaceAll("/$", "");
        String url = host + "/api/v1/oauth2/refresh_token?app_id=" + enc(kdocsCloudConfig.getAppId());
        JSONObject body = new JSONObject();
        body.put("app_key", kdocsCloudConfig.getAppKey());
        body.put("refresh_token", refreshToken);
        JSONObject root = KdocsOpenApiClient.exchange("POST", url, body.toJSONString(), false);
        JSONObject data = KdocsOpenApiClient.requireBusinessData(root);

        KdocsTokenInfo info = new KdocsTokenInfo();
        info.setAccessToken(data.getString("access_token"));
        info.setRefreshToken(data.getString("refresh_token"));
        Integer exp = data.getInteger("expires_in");
        if (exp == null && data.get("expires_in") != null) {
            try {
                exp = Integer.parseInt(data.get("expires_in").toString());
            } catch (NumberFormatException ignored) {
            }
        }
        info.setExpiresIn(exp != null ? exp : 7200);
        info.setUserId(existingUserId);
        return info;
    }

    @Override
    public KdocsTokenInfo getCurrentToken() {
        try {
            KdocsTokenInfo direct = redisCache.getCacheObject(TOKEN_KEY_PREFIX + "default_user");
            if (direct != null) {
                return direct;
            }
            String pattern = TOKEN_KEY_PREFIX + "*";
            Collection<String> keys = redisCache.keys(pattern);
            if (keys != null) {
                for (String key : keys) {
                    KdocsTokenInfo t = redisCache.getCacheObject(key);
                    if (t != null && isTokenValid(t)) {
                        return t;
                    }
                }
                for (String key : keys) {
                    KdocsTokenInfo t = redisCache.getCacheObject(key);
                    if (t != null) {
                        return t;
                    }
                }
            }
        } catch (Exception e) {
            log.warn("扫描 kdocs token 失败: {}", e.getMessage());
        }
        return null;
    }

    @Override
    public void saveToken(String userId, KdocsTokenInfo tokenInfo) {
        if (StringUtils.isBlank(userId)) {
            throw new IllegalArgumentException("userId 不能为空");
        }
        if (tokenInfo == null || tokenInfo.getAccessToken() == null) {
            throw new IllegalArgumentException("token 不能为空");
        }
        redisCache.setCacheObject(TOKEN_KEY_PREFIX + userId, tokenInfo, (int) TOKEN_EXPIRE_SECONDS, TimeUnit.SECONDS);
        log.info("已保存 Kdocs token userId={}", userId);
    }

    @Override
    public void clearToken(String userId) {
        if (StringUtils.isBlank(userId)) {
            return;
        }
        redisCache.deleteObject(TOKEN_KEY_PREFIX + userId);
    }

    @Override
    public boolean isTokenValid(KdocsTokenInfo tokenInfo) {
        return tokenInfo != null
            && tokenInfo.getAccessToken() != null
            && StringUtils.isNotBlank(tokenInfo.getAccessToken())
            && !tokenInfo.isExpired();
    }

    public KdocsTokenInfo getTokenByUserId(String userId) {
        if (StringUtils.isBlank(userId)) {
            return null;
        }
        return redisCache.getCacheObject(TOKEN_KEY_PREFIX + userId);
    }
}
