package com.ruoyi.jarvis.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.jarvis.config.WPS365Config;
import com.ruoyi.jarvis.domain.dto.WPS365TokenInfo;
import com.ruoyi.jarvis.service.IWPS365OAuthService;
import com.ruoyi.jarvis.service.IWPS365ApiService;
import com.ruoyi.jarvis.util.WPS365ApiUtil;
import com.ruoyi.common.core.redis.RedisCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * WPS365 OAuth授权服务实现类
 * 
 * @author system
 */
@Service
public class WPS365OAuthServiceImpl implements IWPS365OAuthService {
    
    private static final Logger log = LoggerFactory.getLogger(WPS365OAuthServiceImpl.class);
    
    /** Redis key前缀，用于存储用户token */
    private static final String TOKEN_KEY_PREFIX = "wps365:token:";
    
    /** Token过期时间（秒），默认30天 */
    private static final long TOKEN_EXPIRE_TIME = 30 * 24 * 60 * 60;
    
    @Autowired
    private WPS365Config wps365Config;
    
    @Autowired
    private RedisCache redisCache;
    
    @Autowired
    private IWPS365ApiService wps365ApiService;
    
    @Override
    public String getAuthUrl(String state) {
        if (wps365Config == null) {
            throw new RuntimeException("WPS365配置未加载，请检查WPS365Config是否正确注入");
        }
        
        String appId = wps365Config.getAppId();
        String redirectUri = wps365Config.getRedirectUri();
        String oauthUrl = wps365Config.getOauthUrl();
        
        log.debug("获取授权URL - appId: {}, redirectUri: {}, oauthUrl: {}", appId, redirectUri, oauthUrl);
        
        // 验证配置参数
        if (appId == null || appId.trim().isEmpty()) {
            throw new RuntimeException("WPS365应用ID未配置，请检查application.yml中的wps365.app-id配置");
        }
        if (redirectUri == null || redirectUri.trim().isEmpty()) {
            throw new RuntimeException("WPS365回调地址未配置，请检查application.yml中的wps365.redirect-uri配置");
        }
        
        // 构建授权URL
        StringBuilder authUrl = new StringBuilder();
        authUrl.append(oauthUrl);
        
        // WPS365可能使用 app_id 而不是 client_id，先尝试 client_id（标准OAuth2参数）
        // 如果失败，可能需要改为 app_id
        authUrl.append("?client_id=").append(appId);
        log.debug("授权URL参数 - client_id: {}", appId);
        
        // 重要：redirect_uri必须与WPS365开放平台配置的回调地址完全一致
        // 包括协议(https)、域名、路径，不能有多余的斜杠
        String finalRedirectUri = redirectUri.trim();
        // 确保URL末尾没有斜杠（除非是根路径）
        if (finalRedirectUri.endsWith("/") && !finalRedirectUri.equals("https://")) {
            finalRedirectUri = finalRedirectUri.substring(0, finalRedirectUri.length() - 1);
        }
        
        // 验证redirect_uri不为空
        if (finalRedirectUri == null || finalRedirectUri.isEmpty()) {
            throw new RuntimeException("redirect_uri不能为空，请检查application.yml中的wps365.redirect-uri配置");
        }
        
        try {
            String encodedRedirectUri = java.net.URLEncoder.encode(finalRedirectUri, "UTF-8");
            authUrl.append("&redirect_uri=").append(encodedRedirectUri);
            log.info("使用回调地址: {} (编码后: {})", finalRedirectUri, encodedRedirectUri);
        } catch (java.io.UnsupportedEncodingException e) {
            log.error("URL编码失败", e);
            authUrl.append("&redirect_uri=").append(finalRedirectUri);
        }
        
        // response_type参数（必需）
        authUrl.append("&response_type=code");
        log.debug("授权URL参数 - response_type: code");
        
        // scope参数（必需，根据WPS365官方文档）
        // 优先使用配置文件中指定的scope，如果没有配置则使用默认值
        // 重要：WPS365官方文档明确要求使用英文逗号分隔，且权限名称必须与后台注册的完全一致
        // 根据官方文档：https://open.wps.cn/documents/app-integration-dev/wps365/server/
        // 权限名称格式为：kso.xxx.read 或 kso.xxx.readwrite（不是 file.read）
        String scope = wps365Config.getScope();
        if (scope == null || scope.trim().isEmpty()) {
            // 默认scope，根据WPS365官方文档：
            // 1. 必须使用英文逗号分隔（不是空格）
            // 2. 权限名称必须以 kso. 开头，格式如：kso.file.read, kso.file.readwrite
            // 3. 常见权限名称（根据官方文档）：
            //    - kso.file.read (文件读取)
            //    - kso.file.readwrite (文件读写)
            //    - kso.doclib.readwrite (文档库读写)
            //    - kso.wiki.readwrite (知识库读写)
            //    - 对于在线表格（AirSheet/KSheet），可能需要 kso.file.readwrite
            // 
            // 如果报错invalid_scope，请：
            // 1. 登录WPS365开放平台：https://open.wps.cn/
            // 2. 进入"开发配置" > "权限管理"
            // 3. 查看已申请权限的准确名称（必须以 kso. 开头）
            // 4. 在application.yml中配置scope参数，使用逗号分隔
            scope = "kso.file.readwrite";  // 默认使用文件读写权限（支持在线表格操作）
        }
        scope = scope.trim();
        
        // URL编码scope参数
        try {
            String encodedScope = java.net.URLEncoder.encode(scope, "UTF-8");
            authUrl.append("&scope=").append(encodedScope);
            log.debug("授权URL参数 - scope: {} (编码后: {})", scope, encodedScope);
        } catch (java.io.UnsupportedEncodingException e) {
            log.error("Scope URL编码失败", e);
            authUrl.append("&scope=").append(scope);
        }
        
        // state参数（推荐，用于防止CSRF攻击）
        if (state == null || state.trim().isEmpty()) {
            state = UUID.randomUUID().toString();
        }
        authUrl.append("&state=").append(state);
        log.debug("授权URL参数 - state: {}", state);
        
        String result = authUrl.toString();
        log.info("生成授权URL: {}", result);
        log.warn("⚠️ 请确保WPS365开放平台配置的回调地址与以下地址完全一致（包括协议、域名、路径）:");
        log.warn("⚠️ 回调地址: {}", finalRedirectUri);
        log.info("📋 授权请求参数清单:");
        log.info("   - client_id: {}", appId);
        log.info("   - redirect_uri: {}", finalRedirectUri);
        log.info("   - response_type: code");
        log.info("   - scope: {}", scope);
        log.info("   - state: {}", state);
        log.info("如果仍然报错，请检查:");
        log.info("   1. WPS365平台配置的回调地址是否与上述redirect_uri完全一致");
        log.info("   2. 参数名是否正确（WPS365可能使用app_id而不是client_id）");
        log.info("   3. scope权限是否已在WPS365平台申请");
        return result;
    }
    
    @Override
    public WPS365TokenInfo getAccessTokenByCode(String code) {
        try {
            JSONObject result = WPS365ApiUtil.getAccessToken(
                wps365Config.getAppId(),
                wps365Config.getAppKey(),
                code,
                wps365Config.getRedirectUri(),
                wps365Config.getTokenUrl()
            );
            
            // 解析响应并创建TokenInfo对象
            WPS365TokenInfo tokenInfo = new WPS365TokenInfo();
            tokenInfo.setAccessToken(result.getString("access_token"));
            tokenInfo.setRefreshToken(result.getString("refresh_token"));
            tokenInfo.setTokenType(result.getString("token_type"));
            tokenInfo.setExpiresIn(result.getInteger("expires_in"));
            tokenInfo.setScope(result.getString("scope"));
            
            // WPS365的token响应中可能不包含user_id，需要调用用户信息API获取
            String userId = result.getString("user_id");
            if (userId == null || userId.trim().isEmpty()) {
                // 尝试通过用户信息API获取用户ID
                try {
                    JSONObject userInfo = wps365ApiService.getUserInfo(tokenInfo.getAccessToken());
                    if (userInfo != null) {
                        // 尝试多种可能的用户ID字段名
                        userId = userInfo.getString("id");
                        if (userId == null || userId.trim().isEmpty()) {
                            userId = userInfo.getString("user_id");
                        }
                        if (userId == null || userId.trim().isEmpty()) {
                            userId = userInfo.getString("open_id");
                        }
                        if (userId == null || userId.trim().isEmpty()) {
                            userId = userInfo.getString("uid");
                        }
                        // 如果还是获取不到，使用access_token的前16位作为标识（临时方案）
                        if (userId == null || userId.trim().isEmpty()) {
                            String accessToken = tokenInfo.getAccessToken();
                            if (accessToken != null && accessToken.length() > 16) {
                                userId = "wps365_" + accessToken.substring(0, 16);
                                log.warn("无法从用户信息API获取用户ID，使用access_token前16位作为标识: {}", userId);
                            } else {
                                userId = "wps365_default";
                                log.warn("无法获取用户ID，使用默认值: {}", userId);
                            }
                        } else {
                            log.info("通过用户信息API获取到用户ID: {}", userId);
                        }
                    } else {
                        userId = "wps365_default";
                        log.warn("用户信息API返回为空，使用默认用户ID: {}", userId);
                    }
                } catch (Exception e) {
                    log.warn("调用用户信息API失败，使用默认用户ID: {}", e.getMessage());
                    // 使用access_token的前16位作为标识（临时方案）
                    String accessToken = tokenInfo.getAccessToken();
                    if (accessToken != null && accessToken.length() > 16) {
                        userId = "wps365_" + accessToken.substring(0, 16);
                    } else {
                        userId = "wps365_default";
                    }
                }
            }
            
            tokenInfo.setUserId(userId);
            log.info("成功获取访问令牌 - userId: {}", userId);
            
            return tokenInfo;
        } catch (Exception e) {
            log.error("通过授权码获取访问令牌失败", e);
            throw new RuntimeException("获取访问令牌失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public WPS365TokenInfo refreshAccessToken(String refreshToken) {
        try {
            JSONObject result = WPS365ApiUtil.refreshAccessToken(
                wps365Config.getAppId(),
                wps365Config.getAppKey(),
                refreshToken,
                wps365Config.getRefreshTokenUrl()
            );
            
            // 解析响应并创建TokenInfo对象
            WPS365TokenInfo tokenInfo = new WPS365TokenInfo();
            tokenInfo.setAccessToken(result.getString("access_token"));
            tokenInfo.setRefreshToken(result.getString("refresh_token"));
            tokenInfo.setTokenType(result.getString("token_type"));
            tokenInfo.setExpiresIn(result.getInteger("expires_in"));
            tokenInfo.setScope(result.getString("scope"));
            tokenInfo.setUserId(result.getString("user_id"));
            
            log.info("成功刷新访问令牌 - userId: {}", tokenInfo.getUserId());
            
            return tokenInfo;
        } catch (Exception e) {
            log.error("刷新访问令牌失败", e);
            throw new RuntimeException("刷新访问令牌失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public WPS365TokenInfo getCurrentToken() {
        // 尝试查找所有WPS365 token（通常只有一个）
        // 使用Redis的keys命令查找所有匹配的token key
        try {
            String pattern = TOKEN_KEY_PREFIX + "*";
            // 注意：keys命令在生产环境可能性能较差，但这里token数量通常很少
            java.util.Collection<String> keys = redisCache.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                // 返回第一个找到的有效token
                for (String key : keys) {
                    WPS365TokenInfo tokenInfo = redisCache.getCacheObject(key);
                    if (tokenInfo != null && isTokenValid(tokenInfo)) {
                        log.debug("找到有效的WPS365 token: {}", key);
                        return tokenInfo;
                    }
                }
                // 如果没有有效的token，返回第一个（即使过期）
                for (String key : keys) {
                    WPS365TokenInfo tokenInfo = redisCache.getCacheObject(key);
                    if (tokenInfo != null) {
                        log.debug("找到WPS365 token（可能已过期）: {}", key);
                        return tokenInfo;
                    }
                }
            }
        } catch (Exception e) {
            log.warn("查找WPS365 token失败", e);
        }
        return null;
    }
    
    @Override
    public void saveToken(String userId, WPS365TokenInfo tokenInfo) {
        if (userId == null || userId.trim().isEmpty()) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        if (tokenInfo == null || tokenInfo.getAccessToken() == null) {
            throw new IllegalArgumentException("Token信息不能为空");
        }
        
        String key = TOKEN_KEY_PREFIX + userId;
        redisCache.setCacheObject(key, tokenInfo, (int) TOKEN_EXPIRE_TIME, TimeUnit.SECONDS);
        log.info("保存用户Token - userId: {}", userId);
    }
    
    @Override
    public void clearToken(String userId) {
        if (userId == null || userId.trim().isEmpty()) {
            return;
        }
        
        String key = TOKEN_KEY_PREFIX + userId;
        redisCache.deleteObject(key);
        log.info("清除用户Token - userId: {}", userId);
    }
    
    @Override
    public boolean isTokenValid(WPS365TokenInfo tokenInfo) {
        if (tokenInfo == null) {
            return false;
        }
        
        // 检查是否过期
        if (tokenInfo.isExpired()) {
            log.debug("Token已过期");
            return false;
        }
        
        // 检查必要字段
        if (tokenInfo.getAccessToken() == null || tokenInfo.getAccessToken().trim().isEmpty()) {
            log.debug("Token缺少accessToken");
            return false;
        }
        
        return true;
    }
    
    /**
     * 根据用户ID获取Token（从Redis）
     */
    public WPS365TokenInfo getTokenByUserId(String userId) {
        if (userId == null || userId.trim().isEmpty()) {
            return null;
        }
        
        String key = TOKEN_KEY_PREFIX + userId;
        return redisCache.getCacheObject(key);
    }
}

