package com.ruoyi.jarvis.util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.net.URL;
import java.nio.charset.StandardCharsets;

/**
 * 腾讯文档API工具类
 * 
 * @author system
 */
public class TencentDocApiUtil {
    
    private static final Logger log = LoggerFactory.getLogger(TencentDocApiUtil.class);

    /**
     * 在线表格 batchUpdate 单次请求中操作数上限（官方：单次操作数量小于等于 5）
     * @see <a href="https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/update.html">批量更新接口</a>
     */
    public static final int TENCENT_BATCH_UPDATE_MAX_OPERATIONS = 5;
    
    // 静态初始化块：禁用系统代理，确保腾讯文档API调用直接连接
    static {
        System.setProperty("java.net.useSystemProxies", "false");
        // 清除可能存在的代理设置
        System.clearProperty("http.proxyHost");
        System.clearProperty("http.proxyPort");
        System.clearProperty("https.proxyHost");
        System.clearProperty("https.proxyPort");
        log.info("已禁用系统代理设置，腾讯文档API将直接连接");
    }
    
    /**
     * 获取访问令牌
     * 
     * @param appId 应用ID
     * @param appSecret 应用密钥
     * @param code 授权码
     * @param redirectUri 回调地址
     * @return 包含access_token和refresh_token的JSON对象
     */
    public static JSONObject getAccessToken(String appId, String appSecret, String code, String redirectUri, String tokenUrl) {
        try {
            // 构建请求参数
            StringBuilder params = new StringBuilder();
            params.append("grant_type=authorization_code");
            params.append("&client_id=").append(appId);
            params.append("&client_secret=").append(appSecret);
            params.append("&code=").append(code);
            params.append("&redirect_uri=").append(java.net.URLEncoder.encode(redirectUri, "UTF-8"));
            
            // 直接使用HttpURLConnection，不使用代理，避免被nginx拦截
            URL url = new URL(tokenUrl);
            java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
            HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("Accept", "application/json");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(30000);
            
            // 写入请求参数
            try (OutputStream os = conn.getOutputStream();
                 OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
                osw.write(params.toString());
                osw.flush();
            }
            
            // 读取响应
            int statusCode = conn.getResponseCode();
            BufferedReader reader;
            if (statusCode >= 200 && statusCode < 300) {
                reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            } else {
                reader = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
            }
            
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            
            String responseStr = response.toString();
            log.info("获取访问令牌响应: statusCode={}, response={}", statusCode, responseStr);
            
            if (statusCode < 200 || statusCode >= 300) {
                throw new RuntimeException("获取访问令牌失败: HTTP " + statusCode + ", response=" + responseStr);
            }
            
            return JSON.parseObject(responseStr);
        } catch (Exception e) {
            log.error("获取访问令牌失败", e);
            throw new RuntimeException("获取访问令牌失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 获取应用级账号 Token（推荐使用）
     * 
     * @param appId 应用ID (client_id)
     * @param appSecret 应用密钥 (client_secret)
     * @return 包含 access_token、refresh_token、user_id(即Open-Id) 的JSON对象
     * 接口文档：https://docs.qq.com/open/document/app/oauth2/app-account-token.html
     */
    public static JSONObject getAppAccountToken(String appId, String appSecret) {
        try {
            // 构建请求URL
            String apiUrl = "https://docs.qq.com/oauth/v2/app-account-token"
                + "?client_id=" + appId
                + "&client_secret=" + appSecret;
            
            log.info("获取应用级账号Token - appId: {}", appId);
            
            // 直接使用HttpURLConnection，不使用代理
            URL url = new URL(apiUrl);
            java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
            HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "application/json");
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(30000);
            
            // 读取响应
            int statusCode = conn.getResponseCode();
            BufferedReader reader;
            if (statusCode >= 200 && statusCode < 300) {
                reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            } else {
                reader = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
            }
            
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            
            String responseStr = response.toString();
            log.info("获取应用级账号Token响应: statusCode={}, response={}", statusCode, responseStr);
            
            if (statusCode < 200 || statusCode >= 300) {
                throw new RuntimeException("获取应用级账号Token失败: HTTP " + statusCode + ", response=" + responseStr);
            }
            
            JSONObject result = JSON.parseObject(responseStr);
            
            // 验证响应包含必要字段
            if (!result.containsKey("access_token") || !result.containsKey("user_id")) {
                throw new RuntimeException("应用级账号Token响应格式错误，缺少必要字段: " + responseStr);
            }
            
            log.info("成功获取应用级账号Token - user_id(Open-Id): {}", result.getString("user_id"));
            
            return result;
        } catch (Exception e) {
            log.error("获取应用级账号Token失败", e);
            throw new RuntimeException("获取应用级账号Token失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 刷新访问令牌
     * 
     * @param appId 应用ID
     * @param appSecret 应用密钥
     * @param refreshToken 刷新令牌
     * @param refreshTokenUrl 刷新令牌地址
     * @return 包含新的access_token和refresh_token的JSON对象
     */
    public static JSONObject refreshAccessToken(String appId, String appSecret, String refreshToken, String refreshTokenUrl) {
        try {
            // 构建请求参数
            StringBuilder params = new StringBuilder();
            params.append("grant_type=refresh_token");
            params.append("&client_id=").append(appId);
            params.append("&client_secret=").append(appSecret);
            params.append("&refresh_token=").append(refreshToken);
            
            // 直接使用HttpURLConnection，不使用代理，避免被nginx拦截
            URL url = new URL(refreshTokenUrl);
            java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
            HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("Accept", "application/json");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(30000);
            
            // 写入请求参数
            try (OutputStream os = conn.getOutputStream();
                 OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
                osw.write(params.toString());
                osw.flush();
            }
            
            // 读取响应
            int statusCode = conn.getResponseCode();
            BufferedReader reader;
            if (statusCode >= 200 && statusCode < 300) {
                reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            } else {
                reader = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
            }
            
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            
            String responseStr = response.toString();
            log.info("刷新访问令牌响应: statusCode={}, response={}", statusCode, responseStr);
            
            if (statusCode < 200 || statusCode >= 300) {
                throw new RuntimeException("刷新访问令牌失败: HTTP " + statusCode + ", response=" + responseStr);
            }
            
            return JSON.parseObject(responseStr);
        } catch (Exception e) {
            log.error("刷新访问令牌失败", e);
            throw new RuntimeException("刷新访问令牌失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 调用腾讯文档API
     * 
     * @param accessToken 访问令牌
     * @param clientId 应用ID（Client-Id）
     * @param openId 开放平台用户ID（Open-Id）
     * @param apiUrl API地址
     * @param method 请求方法 GET/POST/PUT/DELETE
     * @param body 请求体（JSON格式）
     * @return API响应
     */
    public static JSONObject callApi(String accessToken, String clientId, String openId, String apiUrl, String method, String body) {
        try {
            log.info("调用腾讯文档API: url={}, method={}", apiUrl, method);
            
            URL url = new URL(apiUrl);
            // 使用Proxy.NO_PROXY确保不使用系统代理，直接连接到腾讯文档API
            java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
            HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
            
            conn.setRequestMethod(method);
            // 根据腾讯文档官方API文档，使用以下三个请求头进行鉴权
            // 文档：https://docs.qq.com/open/document/app/openapi/v3/sheet/batchUpdate.html
            conn.setRequestProperty("Access-Token", accessToken);
            conn.setRequestProperty("Client-Id", clientId);
            conn.setRequestProperty("Open-Id", openId);
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Accept", "application/json");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(30000);
            
            // 写入请求体
            if (body != null && !body.isEmpty()) {
                try (OutputStream os = conn.getOutputStream();
                     OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
                    osw.write(body);
                    osw.flush();
                }
            }
            
            // 读取响应
            int statusCode = conn.getResponseCode();
            log.info("腾讯文档API响应状态码: {}", statusCode);
            
            BufferedReader reader;
            if (statusCode >= 200 && statusCode < 300) {
                reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            } else {
                reader = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
            }
            
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            
            String responseStr = response.toString();
            log.info("腾讯文档API响应: statusCode={}, responseLength={}, responsePreview={}", 
                statusCode, responseStr.length(), 
                responseStr.length() > 200 ? responseStr.substring(0, 200) + "..." : responseStr);
            
            // 检查响应是否被代理拦截（返回HTML说明被拦截了）
            if (responseStr.trim().startsWith("<html") || responseStr.trim().startsWith("<!DOCTYPE")) {
                log.error("响应被代理拦截，返回了HTML页面而不是JSON。响应内容: {}", 
                    responseStr.length() > 500 ? responseStr.substring(0, 500) + "..." : responseStr);
                throw new RuntimeException("请求被代理拦截，返回了HTML页面。请检查系统代理设置或网络配置。响应: " + 
                    (responseStr.length() > 200 ? responseStr.substring(0, 200) : responseStr));
            }
            
            // 尝试解析JSON
            JSONObject result = JSON.parseObject(responseStr);
            
            // 检查错误码
            if (result.containsKey("error_code") && result.getIntValue("error_code") != 0) {
                String errorMsg = result.getString("error_msg");
                log.error("腾讯文档API调用失败: error_code={}, error_msg={}", 
                    result.getIntValue("error_code"), errorMsg);
                throw new RuntimeException("腾讯文档API调用失败: " + errorMsg);
            }
            
            if (statusCode < 200 || statusCode >= 300) {
                throw new RuntimeException("HTTP请求失败: statusCode=" + statusCode + ", response=" + responseStr);
            }
            
            return result;
        } catch (Exception e) {
            log.error("调用腾讯文档API失败: url={}, method={}", apiUrl, method, e);
            throw new RuntimeException("调用腾讯文档API失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 读取表格数据 - V3 API
     * 根据官方文档：https://docs.qq.com/open/document/app/openapi/v3/sheet/get/get_range.html
     * 
     * @param accessToken 访问令牌
     * @param appId 应用ID
     * @param openId 开放平台用户ID
     * @param fileId 文件ID（在线表格的唯一标识）
     * @param sheetId 工作表ID（可从表格链接中获取，如 ?tab=BB08J2 中的 BB08J2）
     * @param range 范围，使用 A1 表示法（如："A10:D11", "A1:Z100"）
     * @param apiBaseUrl API基础地址（默认：https://docs.qq.com/openapi/spreadsheet/v3）
     * @return 表格数据（JSON格式，包含 data.gridData）
     */
    public static JSONObject readSheetData(String accessToken, String appId, String openId, String fileId, String sheetId, String range, String apiBaseUrl) {
        // V3版本API路径格式：/openapi/spreadsheet/v3/files/{fileId}/{sheetId}/{range}
        // range 需 URL 编码，否则 path 中的 ":" 会导致网关/服务端报 range invalid (400001)
        String encodedRange;
        try {
            encodedRange = URLEncoder.encode(range, StandardCharsets.UTF_8.name());
        } catch (java.io.UnsupportedEncodingException e) {
            encodedRange = range.replace(":", "%3A");
        }
        String apiUrl = String.format("%s/files/%s/%s/%s", apiBaseUrl, fileId, sheetId, encodedRange);
        log.info("读取表格数据 - fileId: {}, sheetId: {}, range: {} (encoded: {}), apiUrl: {}", fileId, sheetId, range, encodedRange, apiUrl);
        return callApi(accessToken, appId, openId, apiUrl, "GET", null);
    }
    
    /**
     * 写入表格数据（V3 API）
     * 
     * @param accessToken 访问令牌
     * @param appId 应用ID
     * @param openId 开放平台用户ID
     * @param fileId 文件ID（在线表格的唯一标识）
     * @param sheetId 工作表ID（可从表格链接中获取，如 ?tab=BB08J2 中的 BB08J2）
     * @param range 范围，例如 "A1"（起始单元格位置）
     * @param values 要写入的数据，支持两种格式：
     *              1. 简单二维数组：[["值1", "值2"], ["值3", "值4"]]
     *              2. V3 API完整格式（包含CellData结构）
     * @param apiBaseUrl API基础地址（默认：https://docs.qq.com/openapi/spreadsheet/v3）
     * @return 写入结果
     */
    public static JSONObject writeSheetData(String accessToken, String appId, String openId, String fileId, String sheetId, String range, Object values, String apiBaseUrl) {
        // 腾讯文档 V3 API 没有直接的 PUT 写入接口
        // 必须使用 batchUpdate 接口：https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/update.html
        
        // 解析 A1 表示法，转换为行列索引
        // 例如："M3" -> row=2 (索引从0开始), col=12
        int[] position = parseA1Notation(range);
        int rowIndex = position[0];
        int colIndex = position[1];
        
        // 根据官方文档，使用 updateRangeRequest
        // 参考：https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/request.html#updaterangerequest
        JSONObject updateRangeRequest = new JSONObject();
        updateRangeRequest.put("sheetId", sheetId);
        
        // 构建 gridData
        JSONObject gridData = new JSONObject();
        gridData.put("startRow", rowIndex);      // 从0开始
        gridData.put("startColumn", colIndex);   // 从0开始
        
        // 构建 rows 数组
        JSONArray rows = new JSONArray();
        JSONObject rowData = new JSONObject();
        JSONArray cellValues = new JSONArray();
        
        // 如果 values 是二维数组
        if (values instanceof JSONArray) {
            JSONArray valuesArray = (JSONArray) values;
            if (!valuesArray.isEmpty() && valuesArray.get(0) instanceof JSONArray) {
                JSONArray firstRow = valuesArray.getJSONArray(0);
                if (!firstRow.isEmpty()) {
                    String text = firstRow.getString(0);
                    
                    // 构建单元格数据
                    JSONObject cellData = new JSONObject();
                    JSONObject cellValue = new JSONObject();
                    cellValue.put("text", text);
                    cellData.put("cellValue", cellValue);
                    
                    cellValues.add(cellData);
                }
            }
        }
        
        rowData.put("values", cellValues);
        rows.add(rowData);
        gridData.put("rows", rows);
        
        updateRangeRequest.put("gridData", gridData);
        
        // 构建 requests
        JSONArray requests = new JSONArray();
        JSONObject request = new JSONObject();
        request.put("updateRangeRequest", updateRangeRequest);
        requests.add(request);
        
        // 构建完整请求体
        JSONObject requestBody = new JSONObject();
        requestBody.put("requests", requests);
        
        // 调用 batchUpdate API
        String apiUrl = String.format("%s/files/%s/batchUpdate", apiBaseUrl, fileId);
        
        log.info("写入表格数据（batchUpdate）- fileId: {}, sheetId: {}, range: {}, rowIndex: {}, colIndex: {}", 
            fileId, sheetId, range, rowIndex, colIndex);
        log.debug("写入表格数据 - 请求体: {}", requestBody.toJSONString());
        
        return callApi(accessToken, appId, openId, apiUrl, "POST", requestBody.toJSONString());
    }
    
    /**
     * 批量更新表格（batchUpdate API）
     * 根据官方文档：https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/update.html
     * 
     * @param accessToken 访问令牌
     * @param appId 应用ID
     * @param openId 开放平台用户ID
     * @param fileId 文件ID
     * @param requestBody 请求体，包含 requests 数组
     * @param apiBaseUrl API基础地址
     * @return 更新结果
     */
    public static JSONObject batchUpdate(String accessToken, String appId, String openId, String fileId, JSONObject requestBody, String apiBaseUrl) {
        String apiUrl = String.format("%s/files/%s/batchUpdate", apiBaseUrl, fileId);

        JSONArray requests = requestBody != null ? requestBody.getJSONArray("requests") : null;
        if (requests == null || requests.isEmpty()) {
            throw new IllegalArgumentException("batchUpdate 请求体缺少非空 requests 数组");
        }

        int total = requests.size();
        log.info("批量更新表格（batchUpdate）- fileId: {}, 总操作数: {}（每批最多 {} 条）",
            fileId, total, TENCENT_BATCH_UPDATE_MAX_OPERATIONS);

        JSONObject lastResult = null;
        for (int start = 0; start < total; start += TENCENT_BATCH_UPDATE_MAX_OPERATIONS) {
            int end = Math.min(start + TENCENT_BATCH_UPDATE_MAX_OPERATIONS, total);
            JSONArray chunk = new JSONArray();
            for (int i = start; i < end; i++) {
                chunk.add(requests.get(i));
            }
            JSONObject chunkBody = new JSONObject();
            chunkBody.put("requests", chunk);

            log.info("batchUpdate 分片 {}-{} / {}, 本批 {} 条", start + 1, end, total, chunk.size());
            log.debug("批量更新表格 - 分片请求体: {}", chunkBody.toJSONString());

            lastResult = callApi(accessToken, appId, openId, apiUrl, "POST", chunkBody.toJSONString());
            assertSpreadsheetBatchUpdateSuccess(lastResult);
        }
        return lastResult != null ? lastResult : new JSONObject();
    }

    /**
     * V3 表格接口在 HTTP 200 时仍可能在 body 内返回 code!=0（如单次 batch 超过 5 条）
     */
    private static void assertSpreadsheetBatchUpdateSuccess(JSONObject result) {
        if (result == null) {
            return;
        }
        if (result.containsKey("code")) {
            int code = result.getIntValue("code");
            if (code != 0) {
                String msg = result.getString("message");
                log.error("腾讯文档 batchUpdate 业务失败: code={}, message={}", code, msg);
                throw new RuntimeException("腾讯文档 batchUpdate 失败: " + msg + " (code=" + code + ")");
            }
        }
    }
    
    /**
     * 解析 A1 表示法为行列索引
     * 例如：
     *   A1 -> [0, 0]
     *   M3 -> [2, 12]
     *   Z100 -> [99, 25]
     * 
     * @param a1Notation A1 表示法字符串
     * @return [rowIndex, colIndex]（从0开始）
     */
    private static int[] parseA1Notation(String a1Notation) {
        if (a1Notation == null || a1Notation.isEmpty()) {
            throw new IllegalArgumentException("A1 表示法不能为空");
        }
        
        // 提取列字母和行号
        StringBuilder colLetters = new StringBuilder();
        StringBuilder rowNumber = new StringBuilder();
        
        for (char c : a1Notation.toCharArray()) {
            if (Character.isLetter(c)) {
                colLetters.append(Character.toUpperCase(c));
            } else if (Character.isDigit(c)) {
                rowNumber.append(c);
            }
        }
        
        if (colLetters.length() == 0 || rowNumber.length() == 0) {
            throw new IllegalArgumentException("无效的 A1 表示法: " + a1Notation);
        }
        
        // 列字母转索引（A=0, B=1, ..., Z=25, AA=26, ...）
        int colIndex = 0;
        for (int i = 0; i < colLetters.length(); i++) {
            colIndex = colIndex * 26 + (colLetters.charAt(i) - 'A' + 1);
        }
        colIndex -= 1; // 转为从0开始的索引
        
        // 行号转索引（1->0, 2->1, ...）
        int rowIndex = Integer.parseInt(rowNumber.toString()) - 1;
        
        return new int[]{rowIndex, colIndex};
    }
    
    /**
     * 追加表格数据（在最后一行追加）- V3 API
     * 
     * @param accessToken 访问令牌
     * @param appId 应用ID
     * @param openId 开放平台用户ID
     * @param fileId 文件ID（在线表格的唯一标识）
     * @param sheetId 工作表ID（可从表格链接中获取，如 ?tab=BB08J2 中的 BB08J2）
     * @param values 要追加的数据，二维数组格式，例如：[["值1", "值2"], ["值3", "值4"]]
     * @param apiBaseUrl API基础地址（默认：https://docs.qq.com/openapi/spreadsheet/v3）
     * @return 追加结果
     */
    public static JSONObject appendSheetData(String accessToken, String appId, String openId, String fileId, String sheetId, Object values, String apiBaseUrl) {
        try {
            // 先获取工作表信息，找到最后一行（V3版本路径）
            // V3版本API路径格式：/openapi/spreadsheet/v3/files/{fileId}
            String infoUrl = String.format("%s/files/%s", apiBaseUrl, fileId);
            log.info("获取工作表信息以追加数据 - apiUrl: {}", infoUrl);
            
            JSONObject sheetInfo = callApi(accessToken, appId, openId, infoUrl, "GET", null);
            
            // 获取行数（根据实际API响应调整）
            // V3 API可能返回的字段名：rowCount, row_count, properties.rowCount等
            // 根据腾讯文档V3 API文档，工作表信息返回格式：
            // { "properties": { "sheetId": "xxx", "rowCount": 100, "columnCount": 10, ... } }
            int rowCount = 0;
            if (sheetInfo.containsKey("properties")) {
                JSONObject properties = sheetInfo.getJSONObject("properties");
                if (properties != null && properties.containsKey("rowCount")) {
                    rowCount = properties.getIntValue("rowCount");
                }
            } else if (sheetInfo.containsKey("rowCount")) {
                rowCount = sheetInfo.getIntValue("rowCount");
            } else if (sheetInfo.containsKey("row_count")) {
                rowCount = sheetInfo.getIntValue("row_count");
            } else if (sheetInfo.containsKey("data")) {
                JSONObject data = sheetInfo.getJSONObject("data");
                if (data != null && data.containsKey("row_count")) {
                    rowCount = data.getIntValue("row_count");
                }
            }
            
            if (rowCount == 0) {
                log.warn("无法从API响应中获取行数，使用默认值1。响应数据: {}", sheetInfo);
                rowCount = 1; // 至少有一行（表头）
            }
            
            // 计算要写入的起始位置（在最后一行之后追加）
            String range = "A" + (rowCount + 1);
            
            log.info("追加数据到第 {} 行，range: {}", rowCount + 1, range);
            
            return writeSheetData(accessToken, appId, openId, fileId, sheetId, range, values, apiBaseUrl);
        } catch (Exception e) {
            log.error("追加表格数据失败 - fileId: {}, sheetId: {}", fileId, sheetId, e);
            throw new RuntimeException("追加表格数据失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 获取文件信息 - V3 API
     * 
     * @param accessToken 访问令牌
     * @param appId 应用ID
     * @param openId 开放平台用户ID
     * @param fileId 文件ID（在线表格的唯一标识）
     * @param apiBaseUrl API基础地址（默认：https://docs.qq.com/openapi/spreadsheet/v3）
     * @return 文件信息（JSON格式，包含metadata、sheets等信息）
     *         返回格式示例：{ "fileId": "xxx", "metadata": {...}, "sheets": [...] }
     */
    public static JSONObject getFileInfo(String accessToken, String appId, String openId, String fileId, String apiBaseUrl) {
        // V3版本API路径格式：/openapi/spreadsheet/v3/files/{fileId}
        String apiUrl = String.format("%s/files/%s", apiBaseUrl, fileId);
        log.info("获取文件信息 - fileId: {}, apiUrl: {}", fileId, apiUrl);
        return callApi(accessToken, appId, openId, apiUrl, "GET", null);
    }
    
    /**
     * 获取工作表列表 - V3 API
     * 
     * @param accessToken 访问令牌
     * @param appId 应用ID
     * @param openId 开放平台用户ID
     * @param fileId 文件ID（在线表格的唯一标识）
     * @param apiBaseUrl API基础地址（默认：https://docs.qq.com/openapi/spreadsheet/v3）
     * @return 工作表列表（JSON格式，包含所有sheet的properties信息）
     *         返回格式示例：{ "sheets": [{ "properties": { "sheetId": "xxx", "title": "工作表1", ... } }] }
     */
    public static JSONObject getSheetList(String accessToken, String appId, String openId, String fileId, String apiBaseUrl) {
        // V3版本API路径格式：/openapi/spreadsheet/v3/files/{fileId}（获取文件信息包含sheets列表）
        String apiUrl = String.format("%s/files/%s", apiBaseUrl, fileId);
        log.info("获取工作表列表 - fileId: {}, apiUrl: {}", fileId, apiUrl);
        return callApi(accessToken, appId, openId, apiUrl, "GET", null);
    }
    
    /**
     * 获取用户信息（包含Open-Id）
     * 根据官方文档：https://docs.qq.com/open/document/app/oauth2/user_info.html
     * 
     * @param accessToken 访问令牌
     * @return 用户信息
     *         响应格式：{ "ret": 0, "msg": "Succeed", "data": { "openID": "xxx", "nick": "xxx", "avatar": "xxx", "source": "wx", "unionID": "xxx" } }
     */
    public static JSONObject getUserInfo(String accessToken) {
        try {
            // 官方文档要求使用查询参数传递 access_token，而不是请求头
            String apiUrl = "https://docs.qq.com/oauth/v2/userinfo?access_token=" + accessToken;
            log.info("调用获取用户信息API: url={}", apiUrl);
            
            URL url = new URL(apiUrl);
            java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
            HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
            
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "application/json");
            conn.setDoInput(true);
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(30000);
            
            // 读取响应
            int responseCode = conn.getResponseCode();
            log.info("获取用户信息API响应状态码: {}", responseCode);
            
            java.io.InputStream inputStream = (responseCode >= 200 && responseCode < 300) 
                ? conn.getInputStream() 
                : conn.getErrorStream();
            
            StringBuilder response = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
            }
            
            String responseBody = response.toString();
            log.debug("获取用户信息API响应: {}", responseBody);
            
            if (responseCode >= 200 && responseCode < 300) {
                JSONObject result = JSONObject.parseObject(responseBody);
                // 检查业务返回码
                Integer ret = result.getInteger("ret");
                if (ret != null && ret == 0) {
                    return result;
                } else {
                    String msg = result.getString("msg");
                    throw new RuntimeException("获取用户信息失败: " + msg);
                }
            } else {
                throw new RuntimeException("获取用户信息失败，HTTP状态码: " + responseCode + ", 响应: " + responseBody);
            }
        } catch (Exception e) {
            log.error("获取用户信息失败", e);
            throw new RuntimeException("获取用户信息失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 调用腾讯文档API（简化方法，自动获取Open-Id）
     * 
     * @param accessToken 访问令牌
     * @param appId 应用ID
     * @param apiUrl API地址
     * @param method 请求方法
     * @param body 请求体
     * @return API响应
     */
    public static JSONObject callApiSimple(String accessToken, String appId, String apiUrl, String method, String body) {
        // 获取用户信息以获得 openId
        // 官方响应格式：{ "ret": 0, "msg": "Succeed", "data": { "openID": "xxx", ... } }
        JSONObject userInfo = getUserInfo(accessToken);
        JSONObject data = userInfo.getJSONObject("data");
        if (data == null) {
            throw new RuntimeException("无法获取用户数据，请检查 Access Token 是否有效");
        }
        
        String openId = data.getString("openID");  // 注意：官方返回的字段名是 openID（大写ID）
        if (openId == null || openId.isEmpty()) {
            throw new RuntimeException("无法获取 Open-Id，请检查 Access Token 是否有效");
        }
        
        return callApi(accessToken, appId, openId, apiUrl, method, body);
    }
}

