import { getChildLogger } from "../logging.js";
const logger = getChildLogger({ module: "feishu-docs" });
/**
 * Regex patterns to extract doc_token from various Feishu/Lark URLs
 *
 * Supported URL formats:
 * - https://xxx.feishu.cn/docx/xxxxx
 * - https://xxx.feishu.cn/wiki/xxxxx
 * - https://xxx.feishu.cn/sheets/xxxxx
 * - https://xxx.feishu.cn/base/xxxxx (bitable)
 * - https://xxx.larksuite.com/docx/xxxxx
 * etc.
 */
/* eslint-disable no-useless-escape */
const DOC_URL_PATTERNS = [
    // docx (new version document) - token is typically 22-27 chars
    /https?:\/\/[^\/]+\/(docx)\/([A-Za-z0-9_-]{15,35})/,
    // doc (legacy document)
    /https?:\/\/[^\/]+\/(doc)\/([A-Za-z0-9_-]{15,35})/,
    // wiki
    /https?:\/\/[^\/]+\/(wiki)\/([A-Za-z0-9_-]{15,35})/,
    // sheets
    /https?:\/\/[^\/]+\/(sheets?)\/([A-Za-z0-9_-]{15,35})/,
    // bitable (base)
    /https?:\/\/[^\/]+\/(base|bitable)\/([A-Za-z0-9_-]{15,35})/,
    // mindnote
    /https?:\/\/[^\/]+\/(mindnote)\/([A-Za-z0-9_-]{15,35})/,
    // file
    /https?:\/\/[^\/]+\/(file)\/([A-Za-z0-9_-]{15,35})/,
    // slide
    /https?:\/\/[^\/]+\/(slides?)\/([A-Za-z0-9_-]{15,35})/,
];
/* eslint-enable no-useless-escape */
/**
 * Extract document references from text content
 * Looks for Feishu/Lark document URLs and extracts doc tokens
 */
export function extractDocRefsFromText(text) {
    const refs = [];
    const seenTokens = new Set();
    for (const pattern of DOC_URL_PATTERNS) {
        const regex = new RegExp(pattern, "g");
        let match;
        while ((match = regex.exec(text)) !== null) {
            const [url, typeStr, token] = match;
            const docType = normalizeDocType(typeStr);
            if (!seenTokens.has(token)) {
                seenTokens.add(token);
                refs.push({
                    docToken: token,
                    docType,
                    url,
                });
            }
        }
    }
    return refs;
}
/**
 * Extract document references from a rich text (post) message content
 */
export function extractDocRefsFromPost(content) {
    const refs = [];
    const seenTokens = new Set();
    try {
        // Post content structure: { title, content: [[{tag, ...}]] }
        const postContent = typeof content === "string" ? JSON.parse(content) : content;
        // Check title for links
        if (postContent.title) {
            const titleRefs = extractDocRefsFromText(postContent.title);
            for (const ref of titleRefs) {
                if (!seenTokens.has(ref.docToken)) {
                    seenTokens.add(ref.docToken);
                    refs.push(ref);
                }
            }
        }
        // Check content elements
        if (Array.isArray(postContent.content)) {
            for (const line of postContent.content) {
                if (!Array.isArray(line))
                    continue;
                for (const element of line) {
                    // Check hyperlinks
                    if (element.tag === "a" && element.href) {
                        const linkRefs = extractDocRefsFromText(element.href);
                        for (const ref of linkRefs) {
                            if (!seenTokens.has(ref.docToken)) {
                                seenTokens.add(ref.docToken);
                                // Use the link text as title if available
                                ref.title = element.text || undefined;
                                refs.push(ref);
                            }
                        }
                    }
                    // Check text content for inline URLs
                    if (element.tag === "text" && element.text) {
                        const textRefs = extractDocRefsFromText(element.text);
                        for (const ref of textRefs) {
                            if (!seenTokens.has(ref.docToken)) {
                                seenTokens.add(ref.docToken);
                                refs.push(ref);
                            }
                        }
                    }
                }
            }
        }
    }
    catch (err) {
        logger.debug(`Failed to parse post content: ${String(err)}`);
    }
    return refs;
}
function normalizeDocType(typeStr) {
    switch (typeStr.toLowerCase()) {
        case "docx":
            return "docx";
        case "doc":
            return "doc";
        case "sheet":
        case "sheets":
            return "sheet";
        case "base":
        case "bitable":
            return "bitable";
        case "wiki":
            return "wiki";
        case "mindnote":
            return "mindnote";
        case "file":
            return "file";
        case "slide":
        case "slides":
            return "slide";
        default:
            return "docx";
    }
}
/**
 * Get wiki node info to resolve the actual document token
 *
 * Wiki documents have a node_token that needs to be resolved to the actual obj_token
 *
 * API: GET https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node
 * Required permission: wiki:wiki:readonly or wiki:wiki
 */
async function resolveWikiNode(client, nodeToken) {
    try {
        logger.debug(`Resolving wiki node: ${nodeToken}`);
        const response = await client.request({
            method: "GET",
            url: "https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node",
            params: {
                token: nodeToken,
                obj_type: "wiki",
            },
        });
        if (response?.code !== 0) {
            const errMsg = response?.msg || "Unknown error";
            logger.warn(`Failed to resolve wiki node: ${errMsg} (code: ${response?.code})`);
            return null;
        }
        const node = response.data?.node;
        if (!node?.obj_token || !node?.obj_type) {
            logger.warn(`Wiki node response missing obj_token or obj_type`);
            return null;
        }
        return {
            objToken: node.obj_token,
            objType: node.obj_type,
            title: node.title,
        };
    }
    catch (err) {
        logger.error(`Error resolving wiki node: ${String(err)}`);
        return null;
    }
}
/**
 * Fetch the content of a Feishu document
 *
 * Supports:
 * - docx (new version documents) - direct content fetch
 * - wiki (knowledge base nodes) - first resolve to actual document, then fetch
 *
 * Other document types return a placeholder message.
 *
 * API: GET https://open.feishu.cn/open-apis/docs/v1/content
 * Docs: https://open.feishu.cn/document/server-docs/docs/content/get
 *
 * Required permissions:
 * - docs:document.content:read (for docx)
 * - wiki:wiki:readonly or wiki:wiki (for wiki)
 */
export async function fetchFeishuDocContent(client, docRef, options = {}) {
    const { maxLength = 50000, lang = "zh" } = options;
    // For wiki type, first resolve the node to get the actual document token
    let targetToken = docRef.docToken;
    let targetType = docRef.docType;
    let resolvedTitle = docRef.title;
    if (docRef.docType === "wiki") {
        const wikiNode = await resolveWikiNode(client, docRef.docToken);
        if (!wikiNode) {
            return {
                content: `[飞书知识库文档: ${docRef.title || docRef.docToken}]\n链接: ${docRef.url}\n\n(无法获取知识库节点信息，请确保机器人已被添加为知识库成员)`,
                truncated: false,
            };
        }
        targetToken = wikiNode.objToken;
        targetType = wikiNode.objType;
        resolvedTitle = wikiNode.title || docRef.title;
        logger.debug(`Wiki node resolved: ${docRef.docToken} -> ${targetToken} (${targetType})`);
    }
    // Only docx is supported for content fetching
    if (targetType !== "docx") {
        logger.debug(`Document type ${targetType} is not supported for content fetching`);
        return {
            content: `[飞书${getDocTypeName(targetType)}文档: ${resolvedTitle || targetToken}]\n链接: ${docRef.url}\n\n(该文档类型暂不支持内容获取，请直接访问链接查看)`,
            truncated: false,
        };
    }
    try {
        logger.debug(`Fetching document content: ${targetToken} (${targetType})`);
        // Use native HTTP request since SDK may not have this endpoint
        // The API endpoint is: GET /open-apis/docs/v1/content
        const response = await client.request({
            method: "GET",
            url: "https://open.feishu.cn/open-apis/docs/v1/content",
            params: {
                doc_token: targetToken,
                doc_type: "docx",
                content_type: "markdown",
                lang,
            },
        });
        if (response?.code !== 0) {
            const errMsg = response?.msg || "Unknown error";
            logger.warn(`Failed to fetch document content: ${errMsg} (code: ${response?.code})`);
            // Check for common errors
            if (response?.code === 2889902) {
                return {
                    content: `[飞书文档: ${resolvedTitle || targetToken}]\n链接: ${docRef.url}\n\n(无权限访问该文档，请确保机器人已被添加为文档协作者)`,
                    truncated: false,
                };
            }
            return {
                content: `[飞书文档: ${resolvedTitle || targetToken}]\n链接: ${docRef.url}\n\n(获取文档内容失败: ${errMsg})`,
                truncated: false,
            };
        }
        let content = response.data?.content || "";
        let truncated = false;
        // Truncate if too long
        if (content.length > maxLength) {
            content = content.substring(0, maxLength) + "\n\n... (内容过长，已截断)";
            truncated = true;
        }
        // Add document header
        const header = resolvedTitle
            ? `[飞书文档: ${resolvedTitle}]\n链接: ${docRef.url}\n\n---\n\n`
            : `[飞书文档]\n链接: ${docRef.url}\n\n---\n\n`;
        return {
            content: header + content,
            truncated,
        };
    }
    catch (err) {
        logger.error(`Error fetching document content: ${String(err)}`);
        return {
            content: `[飞书文档: ${resolvedTitle || targetToken}]\n链接: ${docRef.url}\n\n(获取文档内容时发生错误)`,
            truncated: false,
        };
    }
}
function getDocTypeName(docType) {
    switch (docType) {
        case "docx":
        case "doc":
            return "";
        case "sheet":
            return "表格";
        case "bitable":
            return "多维表格";
        case "wiki":
            return "知识库";
        case "mindnote":
            return "思维笔记";
        case "file":
            return "文件";
        case "slide":
            return "幻灯片";
        default:
            return "";
    }
}
/**
 * Resolve document content from a message
 * Extracts document links and fetches their content
 *
 * @returns Combined document content string, or null if no documents found
 */
export async function resolveFeishuDocsFromMessage(client, message, options = {}) {
    const { maxDocsPerMessage = 3, maxTotalLength = 100000 } = options;
    const msgType = message.message_type;
    let docRefs = [];
    try {
        const content = JSON.parse(message.content);
        if (msgType === "text" && content.text) {
            // Extract from plain text
            docRefs = extractDocRefsFromText(content.text);
        }
        else if (msgType === "post") {
            // Extract from rich text
            docRefs = extractDocRefsFromPost(content);
        }
        // TODO: Handle interactive (card) messages with document links
    }
    catch (err) {
        logger.debug(`Failed to parse message content for document extraction: ${String(err)}`);
        return null;
    }
    if (docRefs.length === 0) {
        return null;
    }
    // Limit number of documents to process
    const refsToProcess = docRefs.slice(0, maxDocsPerMessage);
    logger.debug(`Found ${docRefs.length} document(s), processing ${refsToProcess.length}`);
    const contents = [];
    let totalLength = 0;
    for (const ref of refsToProcess) {
        const result = await fetchFeishuDocContent(client, ref, {
            maxLength: Math.min(50000, maxTotalLength - totalLength),
        });
        if (result) {
            contents.push(result.content);
            totalLength += result.content.length;
            if (totalLength >= maxTotalLength) {
                break;
            }
        }
    }
    if (contents.length === 0) {
        return null;
    }
    return contents.join("\n\n---\n\n");
}
