/**
 * Document, folder, spreadsheet tools backed by local SQLite.
 */
import { getDb, genId } from "../db.js";
// ─── ProseMirror JSON → Markdown converter ──────────────────────────────────
function prosemirrorToMarkdown(jsonStr) {
    if (!jsonStr)
        return "";
    let doc;
    try {
        doc = JSON.parse(jsonStr);
    }
    catch {
        return jsonStr;
    }
    if (!doc || !doc.content)
        return jsonStr;
    return renderNodes(doc.content);
}
function renderNodes(nodes) {
    return (nodes || []).map(renderNode).join("");
}
function renderNode(node) {
    switch (node.type) {
        case "paragraph":
            return renderInline(node.content) + "\n\n";
        case "heading": {
            const level = node.attrs?.level ?? 1;
            return "#".repeat(level) + " " + renderInline(node.content) + "\n\n";
        }
        case "bulletList":
            return (node.content || []).map((li) => "- " + renderInline(li.content?.[0]?.content).trim()).join("\n") + "\n\n";
        case "orderedList":
            return (node.content || []).map((li, i) => `${i + 1}. ` + renderInline(li.content?.[0]?.content).trim()).join("\n") + "\n\n";
        case "codeBlock": {
            const lang = node.attrs?.language ?? "";
            return "```" + lang + "\n" + renderInline(node.content) + "\n```\n\n";
        }
        case "blockquote":
            return renderNodes(node.content || []).split("\n").map((l) => "> " + l).join("\n") + "\n\n";
        case "horizontalRule":
            return "---\n\n";
        case "text": {
            let t = node.text ?? "";
            for (const mark of node.marks || []) {
                if (mark.type === "bold")
                    t = `**${t}**`;
                else if (mark.type === "italic")
                    t = `*${t}*`;
                else if (mark.type === "code")
                    t = "`" + t + "`";
                else if (mark.type === "link")
                    t = `[${t}](${mark.attrs?.href ?? ""})`;
            }
            return t;
        }
        default:
            if (node.content)
                return renderNodes(node.content);
            if (node.text)
                return node.text;
            return "";
    }
}
function renderInline(content) {
    if (!content)
        return "";
    return content.map(renderNode).join("");
}
// ─── tool definitions ───────────────────────────────────────────────────────
export const documentTools = [
    // ── Document CRUD ─────────────────────────────────────────────
    {
        name: "createDocument",
        description: "Create a new rich-text document with a title and optional initial content blocks.",
        inputSchema: {
            type: "object",
            properties: {
                title: { type: "string", description: "Document title" },
                parentId: { type: "string", description: "Optional parent document ID for hierarchy" },
            },
            required: ["title"],
        },
        handler: async (args) => {
            const db = getDb();
            const id = genId("doc");
            db.prepare("INSERT INTO documents (id, title, parent_id) VALUES (?, ?, ?)").run(id, args.title, args.parentId ?? null);
            return { id, title: args.title };
        },
    },
    {
        name: "createDocumentWithContent",
        description: "Create a document with pre-built ProseMirror JSON content string.",
        inputSchema: {
            type: "object",
            properties: {
                title: { type: "string", description: "Document title" },
                content: { type: "string", description: "ProseMirror JSON content string" },
                parentId: { type: "string", description: "Optional parent document ID" },
            },
            required: ["title", "content"],
        },
        handler: async (args) => {
            const db = getDb();
            const id = genId("doc");
            db.prepare("INSERT INTO documents (id, title, content, parent_id) VALUES (?, ?, ?, ?)").run(id, args.title, args.content, args.parentId ?? null);
            return { id, title: args.title };
        },
    },
    {
        name: "getDocument",
        description: "Get a document by ID, including title, content, metadata, and file info.",
        inputSchema: {
            type: "object",
            properties: {
                documentId: { type: "string", description: "Document ID" },
            },
            required: ["documentId"],
        },
        handler: async (args) => {
            const db = getDb();
            const row = db.prepare("SELECT * FROM documents WHERE id = ? AND is_archived = 0").get(args.documentId);
            if (!row)
                throw new Error(`Document not found: ${args.documentId}`);
            return {
                id: row.id, title: row.title, content: row.content, icon: row.icon,
                parentId: row.parent_id, isArchived: !!row.is_archived, docType: row.doc_type,
                createdAt: row.created_at, updatedAt: row.updated_at,
            };
        },
    },
    {
        name: "updateDocument",
        description: "Update document fields: title, content, icon, visibility, favorite status.",
        inputSchema: {
            type: "object",
            properties: {
                id: { type: "string", description: "Document ID" },
                title: { type: "string", description: "New title" },
                content: { type: "string", description: "New ProseMirror JSON content string" },
                icon: { type: "string", description: "Emoji icon" },
                isPublic: { type: "boolean", description: "Public visibility" },
                isFavorite: { type: "boolean", description: "Favorite status" },
            },
            required: ["id"],
        },
        handler: async (args) => {
            const db = getDb();
            const sets = [];
            const vals = [];
            if (args.title !== undefined) {
                sets.push("title = ?");
                vals.push(args.title);
            }
            if (args.content !== undefined) {
                sets.push("content = ?");
                vals.push(args.content);
            }
            if (args.icon !== undefined) {
                sets.push("icon = ?");
                vals.push(args.icon);
            }
            sets.push("updated_at = ?");
            vals.push(new Date().toISOString());
            vals.push(args.id);
            if (sets.length <= 1)
                throw new Error("No fields to update");
            db.prepare(`UPDATE documents SET ${sets.join(", ")} WHERE id = ?`).run(...vals);
            return { success: true, id: args.id };
        },
    },
    {
        name: "archiveDocument",
        description: "Archive (soft-delete) a document and all its children recursively.",
        inputSchema: {
            type: "object",
            properties: {
                id: { type: "string", description: "Document ID" },
            },
            required: ["id"],
        },
        handler: async (args) => {
            const db = getDb();
            const now = new Date().toISOString();
            db.prepare(`
        WITH RECURSIVE children(id) AS (
          SELECT id FROM documents WHERE id = ?
          UNION ALL
          SELECT d.id FROM documents d JOIN children c ON d.parent_id = c.id
        )
        UPDATE documents SET is_archived = 1, updated_at = ?
        WHERE id IN (SELECT id FROM children)
      `).run(args.id, now);
            return { success: true, id: args.id };
        },
    },
    {
        name: "restoreDocument",
        description: "Restore an archived document and its children.",
        inputSchema: {
            type: "object",
            properties: {
                id: { type: "string", description: "Document ID" },
            },
            required: ["id"],
        },
        handler: async (args) => {
            const db = getDb();
            const now = new Date().toISOString();
            db.prepare(`
        WITH RECURSIVE children(id) AS (
          SELECT id FROM documents WHERE id = ?
          UNION ALL
          SELECT d.id FROM documents d JOIN children c ON d.parent_id = c.id
        )
        UPDATE documents SET is_archived = 0, updated_at = ?
        WHERE id IN (SELECT id FROM children)
      `).run(args.id, now);
            return { success: true, id: args.id };
        },
    },
    {
        name: "searchDocuments",
        description: "Search documents by title and content (full-text search).",
        inputSchema: {
            type: "object",
            properties: {
                query: { type: "string", description: "Search query" },
            },
            required: ["query"],
        },
        handler: async (args) => {
            const db = getDb();
            const rows = db.prepare(`
        SELECT d.id, d.title, snippet(documents_fts, 1, '**', '**', '...', 48) as snippet
        FROM documents_fts
        JOIN documents d ON d.rowid = documents_fts.rowid
        WHERE documents_fts MATCH ?
          AND d.is_archived = 0
        ORDER BY rank
        LIMIT 50
      `).all(args.query);
            return {
                count: rows.length,
                results: rows.map((r) => ({ id: r.id, title: r.title, snippet: r.snippet })),
            };
        },
    },
    {
        name: "listDocuments",
        description: "List all user documents sorted by last modified (most recent first, up to 200).",
        inputSchema: {
            type: "object",
            properties: {
                limit: { type: "number", description: "Max results (default 100)" },
            },
        },
        handler: async (args) => {
            const db = getDb();
            const limit = Math.min(args.limit ?? 100, 200);
            const rows = db.prepare("SELECT id, title, doc_type, icon, parent_id, created_at, updated_at FROM documents WHERE is_archived = 0 ORDER BY updated_at DESC LIMIT ?").all(limit);
            return {
                count: rows.length,
                documents: rows.map((r) => ({
                    id: r.id, title: r.title, docType: r.doc_type, icon: r.icon,
                    parentId: r.parent_id, createdAt: r.created_at, updatedAt: r.updated_at,
                })),
            };
        },
    },
    {
        name: "exportDocumentToMarkdown",
        description: "Export a document to Markdown format. Returns { title, markdown }.",
        inputSchema: {
            type: "object",
            properties: {
                documentId: { type: "string", description: "Document ID" },
            },
            required: ["documentId"],
        },
        handler: async (args) => {
            const db = getDb();
            const row = db.prepare("SELECT title, content FROM documents WHERE id = ?").get(args.documentId);
            if (!row)
                throw new Error(`Document not found: ${args.documentId}`);
            return {
                title: row.title,
                markdown: `# ${row.title}\n\n${prosemirrorToMarkdown(row.content)}`,
            };
        },
    },
    {
        name: "duplicateDocument",
        description: "Duplicate a document. Clones content, icon, type. Resets visibility and favorite.",
        inputSchema: {
            type: "object",
            properties: {
                documentId: { type: "string", description: "Document ID to clone" },
                title: { type: "string", description: "Override title (default: original + ' (Copy)')" },
            },
            required: ["documentId"],
        },
        handler: async (args) => {
            const db = getDb();
            const original = db.prepare("SELECT * FROM documents WHERE id = ?").get(args.documentId);
            if (!original)
                throw new Error(`Document not found: ${args.documentId}`);
            const newId = genId("doc");
            const newTitle = args.title ?? `${original.title} (Copy)`;
            db.prepare("INSERT INTO documents (id, title, content, icon, doc_type) VALUES (?, ?, ?, ?, ?)").run(newId, newTitle, original.content, original.icon, original.doc_type);
            return { id: newId, title: newTitle };
        },
    },
    // ── Folder Management ─────────────────────────────────────────
    {
        name: "createFolder",
        description: "Create a new folder for organizing documents.",
        inputSchema: {
            type: "object",
            properties: {
                name: { type: "string", description: "Folder name" },
                color: { type: "string", description: "CSS color class (e.g. 'blue', 'red', 'green')" },
            },
            required: ["name", "color"],
        },
        handler: async (args) => {
            const db = getDb();
            const id = genId("folder");
            db.prepare("INSERT INTO folders (id, name, color) VALUES (?, ?, ?)").run(id, args.name, args.color);
            return { id, name: args.name, color: args.color };
        },
    },
    {
        name: "listFolders",
        description: "List all user folders.",
        inputSchema: { type: "object", properties: {} },
        handler: async () => {
            const db = getDb();
            const rows = db.prepare("SELECT * FROM folders ORDER BY created_at DESC").all();
            return {
                count: rows.length,
                folders: rows.map((r) => ({ id: r.id, name: r.name, color: r.color, createdAt: r.created_at })),
            };
        },
    },
    {
        name: "getFolderWithDocuments",
        description: "Get a folder and all documents inside it.",
        inputSchema: {
            type: "object",
            properties: {
                folderId: { type: "string", description: "Folder ID" },
            },
            required: ["folderId"],
        },
        handler: async (args) => {
            const db = getDb();
            const folder = db.prepare("SELECT * FROM folders WHERE id = ?").get(args.folderId);
            if (!folder)
                throw new Error(`Folder not found: ${args.folderId}`);
            const docs = db.prepare(`
        SELECT d.id, d.title, d.doc_type, d.icon, d.updated_at
        FROM folder_documents fd
        JOIN documents d ON d.id = fd.document_id
        WHERE fd.folder_id = ? AND d.is_archived = 0
        ORDER BY d.updated_at DESC
      `).all(args.folderId);
            return {
                folder: { id: folder.id, name: folder.name, color: folder.color },
                documents: docs.map((d) => ({
                    id: d.id, title: d.title, docType: d.doc_type, icon: d.icon, updatedAt: d.updated_at,
                })),
            };
        },
    },
    {
        name: "addDocumentToFolder",
        description: "Add a document to a folder.",
        inputSchema: {
            type: "object",
            properties: {
                documentId: { type: "string", description: "Document ID" },
                folderId: { type: "string", description: "Folder ID" },
            },
            required: ["documentId", "folderId"],
        },
        handler: async (args) => {
            const db = getDb();
            db.prepare("INSERT OR IGNORE INTO folder_documents (folder_id, document_id) VALUES (?, ?)").run(args.folderId, args.documentId);
            return { success: true };
        },
    },
    {
        name: "removeDocumentFromFolder",
        description: "Remove a document from a folder.",
        inputSchema: {
            type: "object",
            properties: {
                documentId: { type: "string", description: "Document ID" },
                folderId: { type: "string", description: "Folder ID" },
            },
            required: ["documentId", "folderId"],
        },
        handler: async (args) => {
            const db = getDb();
            db.prepare("DELETE FROM folder_documents WHERE folder_id = ? AND document_id = ?").run(args.folderId, args.documentId);
            return { success: true };
        },
    },
    // ── Spreadsheet Operations ────────────────────────────────────
    {
        name: "createSpreadsheet",
        description: "Create a new spreadsheet with a name.",
        inputSchema: {
            type: "object",
            properties: {
                name: { type: "string", description: "Spreadsheet name" },
            },
            required: ["name"],
        },
        handler: async (args) => {
            const db = getDb();
            const id = genId("sheet");
            db.prepare("INSERT INTO documents (id, title, doc_type) VALUES (?, ?, 'spreadsheet')").run(id, args.name);
            return { id, name: args.name };
        },
    },
    {
        name: "listSpreadsheets",
        description: "List user spreadsheets sorted by last updated.",
        inputSchema: {
            type: "object",
            properties: {
                limit: { type: "number", description: "Max results (default 25)" },
            },
        },
        handler: async (args) => {
            const db = getDb();
            const limit = args.limit ?? 25;
            const rows = db.prepare("SELECT id, title, created_at, updated_at FROM documents WHERE doc_type = 'spreadsheet' AND is_archived = 0 ORDER BY updated_at DESC LIMIT ?").all(limit);
            return {
                count: rows.length,
                spreadsheets: rows.map((r) => ({
                    id: r.id, name: r.title, createdAt: r.created_at, updatedAt: r.updated_at,
                })),
            };
        },
    },
    {
        name: "getSpreadsheetRange",
        description: "Get cells within a range from a spreadsheet (row/col coordinates).",
        inputSchema: {
            type: "object",
            properties: {
                sheetId: { type: "string", description: "Spreadsheet ID" },
                startRow: { type: "number", description: "Start row (0-based)" },
                endRow: { type: "number", description: "End row (inclusive)" },
                startCol: { type: "number", description: "Start column (0-based)" },
                endCol: { type: "number", description: "End column (inclusive)" },
            },
            required: ["sheetId", "startRow", "endRow", "startCol", "endCol"],
        },
        handler: async (args) => {
            const db = getDb();
            const rows = db.prepare(`
        SELECT row, col, value, cell_type
        FROM spreadsheet_cells
        WHERE sheet_id = ? AND row BETWEEN ? AND ? AND col BETWEEN ? AND ?
        ORDER BY row, col
      `).all(args.sheetId, args.startRow, args.endRow, args.startCol, args.endCol);
            return {
                sheetId: args.sheetId,
                range: { startRow: args.startRow, endRow: args.endRow, startCol: args.startCol, endCol: args.endCol },
                cells: rows.map((r) => ({ row: r.row, col: r.col, value: r.value, type: r.cell_type })),
            };
        },
    },
    {
        name: "applySpreadsheetOperations",
        description: 'Apply batch cell operations to a spreadsheet. Operations: setCell ({op:"setCell", row, col, value, type?}), clearCell ({op:"clearCell", row, col}).',
        inputSchema: {
            type: "object",
            properties: {
                sheetId: { type: "string", description: "Spreadsheet ID" },
                operations: {
                    type: "array",
                    description: "Array of cell operations",
                    items: {
                        type: "object",
                        properties: {
                            op: { type: "string", enum: ["setCell", "clearCell"] },
                            row: { type: "number" },
                            col: { type: "number" },
                            value: { type: "string" },
                            type: { type: "string" },
                        },
                        required: ["op"],
                    },
                },
            },
            required: ["sheetId", "operations"],
        },
        handler: async (args) => {
            const db = getDb();
            const setStmt = db.prepare("INSERT OR REPLACE INTO spreadsheet_cells (sheet_id, row, col, value, cell_type) VALUES (?, ?, ?, ?, ?)");
            const clearStmt = db.prepare("DELETE FROM spreadsheet_cells WHERE sheet_id = ? AND row = ? AND col = ?");
            const tx = db.transaction(() => {
                for (const op of args.operations) {
                    if (op.op === "setCell") {
                        setStmt.run(args.sheetId, op.row, op.col, op.value ?? "", op.type ?? "text");
                    }
                    else if (op.op === "clearCell") {
                        clearStmt.run(args.sheetId, op.row, op.col);
                    }
                }
            });
            tx();
            // Update the document's updated_at
            db.prepare("UPDATE documents SET updated_at = ? WHERE id = ?").run(new Date().toISOString(), args.sheetId);
            return { success: true, operationsApplied: args.operations.length };
        },
    },
];
//# sourceMappingURL=documentTools.js.map