import { readFile, stat } from "node:fs/promises";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { withWriteEvent } from "../lib/log-ingest.js";
const execFileAsync = promisify(execFile);
// ---------------------------------------------------------------------------
// Text extraction for file attachments (chunker removed).
//
// FILE ON DISK --> MIME CHECK --> EXTRACT TEXT --> CACHE --> RETURN
//       │               │              │             │
//       v               v              v             v
//   [missing?]     [CSV? image?]   [pdftotext     Map keyed by attachmentId
//   [perm denied?]  → reject       not found?]    (consumed by memory-classify
//   [empty?]        with guidance   [corrupt PDF?]  + memory-ingest)
//
// Old behaviour pre-737: this tool also chunked the extracted text via
// embedding-cosine valley detection (semantic-chunker.ts) and cached
// `sections: ChunkedSection[]`., chunking moved upstream
// to LLM-driven section classification (memory-classify), so this tool
// is now a pure text extractor. The cache holds only the raw text + the
// file's metadata; downstream callers decide how to chunk.
// ---------------------------------------------------------------------------
const REJECTED_MIME_TYPES = {
    "text/csv": "CSV files contain structured data. Use memory-write to create graph nodes from each row instead of ingesting as a knowledge document.",
};
const IMAGE_PREFIXES = ["image/"];
async function extractPdfText(storagePath) {
    try {
        const { stdout } = await execFileAsync("pdftotext", [storagePath, "-"], {
            maxBuffer: 10 * 1024 * 1024,
        });
        return stdout.trim();
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (msg.includes("ENOENT") || msg.includes("not found")) {
            throw new Error("pdftotext not found. Install poppler-utils: sudo apt-get install -y poppler-utils");
        }
        throw new Error(`PDF extraction failed: ${msg}`);
    }
}
export const extractCache = new Map();
/**
 * Extract text from a file attachment. Caches the full text by attachmentId
 * for memory-classify and memory-ingest to consume.
 *
 * Supports: application/pdf, text/plain, text/markdown.
 * Rejects: text/csv (structured data → memory-write), images (metadata-only).
 */
export async function memoryIngestExtract(opts) {
    return withWriteEvent({ tool: "memory-ingest-extract", name: opts.filename }, () => memoryIngestExtractImpl(opts), (r) => ({ elementId: opts.attachmentId, name: r.filename }));
}
async function memoryIngestExtractImpl(opts) {
    const { storagePath, filename, mimeType, attachmentId } = opts;
    if (!attachmentId) {
        throw new Error("memory-ingest-extract requires attachmentId (the UUID of the file attachment). " +
            "Without it, cached content cannot be retrieved by memory-classify or memory-ingest.");
    }
    const t0 = Date.now();
    const log = (stage, detail) => console.error(`[memory-ingest-extract] [${filename}] ${stage}${detail ? ` — ${detail}` : ""} (${Date.now() - t0}ms)`);
    log("start", `mime=${mimeType}`);
    const rejection = REJECTED_MIME_TYPES[mimeType];
    if (rejection)
        throw new Error(rejection);
    if (IMAGE_PREFIXES.some((p) => mimeType.startsWith(p))) {
        throw new Error(`Images are stored as metadata only (DigitalDocument). No knowledge hierarchy created for "${filename}".`);
    }
    let text;
    if (mimeType === "application/pdf") {
        log("extracting", "pdftotext");
        text = await extractPdfText(storagePath);
        log("extracted", `${text.length} chars`);
    }
    else if (mimeType === "text/plain" || mimeType === "text/markdown") {
        text = await readFile(storagePath, "utf-8");
        log("read", `${text.length} chars`);
    }
    else {
        throw new Error(`Unsupported MIME type "${mimeType}" for knowledge document ingestion. ` +
            "Supported: application/pdf, text/plain, text/markdown.");
    }
    const trimmedText = text.trim();
    if (trimmedText.length === 0) {
        throw new Error(`File "${filename}" contains no extractable text.`);
    }
    const fileStat = await stat(storagePath);
    extractCache.set(attachmentId, { filename, mimeType, text: trimmedText, storagePath });
    log("cached", `attachmentId=${attachmentId}, ${trimmedText.length} chars`);
    return {
        filename,
        mimeType,
        sizeBytes: fileStat.size,
        textLength: trimmedText.length,
        preview: trimmedText.slice(0, 240),
    };
}
//# sourceMappingURL=memory-ingest-extract.js.map