/**
 * 输入材料提取器 — 从用户意图文本中抽取路径、URL、疑似材料。
 *
 * 支持：
 * - 中文/英文路径、绝对/相对路径、~/、./、../
 * - Windows 路径 (C:\xxx, C:/xxx)
 * - URL
 * - 引号/反引号包裹路径（允许内部空格）
 * - 路径后粘连中文动作词时正确截断
 * - 多输入材料数组
 * - 确定路径和疑似路径分开返回
 */
import { classifyIngestionStatus } from "./input_material_contract_registry.js";
import { safeMatchIndex } from "../core/helpers.js";
// ── 内部工具 ──
const FILE_EXT_LIST = [
    "tar\\.gz", "zip", "gz", "rar", "7z", "jar", "war",
    "log", "md", "txt", "doc", "docx", "pdf",
    "java", "py", "ts", "tsx", "js", "jsx", "go", "rs",
    "json", "yaml", "yml", "toml",
    "pem", "key", "rsa",
    "png", "jpg", "jpeg", "gif", "webp", "svg",
    "csv", "xlsx", "xls",
];
const FILE_EXT_PATTERN = FILE_EXT_LIST.join("|");
const PATH_CHAR_CLASS = String.raw `[\p{L}\p{N}_@+~.:\\\/=-]`;
const PATH_START_BOUNDARY = String.raw `(?:(?<![\p{L}\p{N}_\/\\])|(?<=[从将把拿用和取描析于据from ]))`;
function inferKindFromPath(p) {
    if (/^https?:\/\//i.test(p))
        return "url";
    if (/\.(zip|tar\.gz|gz|rar|7z|jar|war)$/i.test(p))
        return "archive";
    if (/\.log$/i.test(p) || /(^|[\\/])logs?([\\/]|$)/i.test(p))
        return "log";
    if (/\.(md|txt|doc|pdf|docx)$/i.test(p))
        return "doc";
    if (/\.[\p{L}\p{N}_.-]+$/u.test(p))
        return "file";
    return "directory";
}
export function normalizePathCandidate(raw) {
    return raw
        .trim()
        .replace(/^[`"'“”‘’]+|[`"'“”‘’]+$/g, "")
        .replace(/^(?:从|将|把|拿|用|按|参考|参照|依据|请(?:基于|根据)?|基于|根据|读取|扫描|分析)(?=(?:~\/|\.{1,2}\/|\/|[A-Za-z]:[\\/]|[\p{L}\p{N}_@+.-]+[\\/]))/u, "")
        .replace(new RegExp(`^(?:从|将|把|拿|用|按|参考|参照|依据|请(?:基于|根据)?|基于|根据|读取|扫描|分析)(?=[\\p{L}\\p{N}_@+.-]+\\.(${FILE_EXT_PATTERN}))`, "iu"), "")
        .replace(/(?:下|中|里)(?=(?:生成|提取|抽取|输出|抓取|整理|形成|制作|写成|做成)).*$/u, "")
        .replace(/[。；;，,、：:）)\]】}]+$/u, "")
        .replace(new RegExp(`^(.+\\.(${FILE_EXT_PATTERN}))(?:[^\\/\\\\s.].*)$`, "iu"), "$1");
}
// ── 主提取函数 ──
export function extractInputMaterials(intent) {
    const materials = [];
    const seen = new Set();
    const spans = [];
    function overlapsRecorded(start, _end) {
        return spans.some((s) => start >= s.start && start < s.end);
    }
    function addMaterial(p, kind, span) {
        if (!p || p === "undefined" || p === "null")
            return;
        if (seen.has(p))
            return;
        seen.add(p);
        const normalizedForPolicy = p.replace(/^~\//, (process.env.HOME || "/") + "/");
        materials.push({
            kind,
            path: p,
            required: true,
            ingestion_status: classifyIngestionStatus(normalizedForPolicy),
            source_span: span,
            confidence: 0.9,
            evidence: `path_extracted:${kind}`,
        });
        if (span)
            spans.push(span);
    }
    // 按连词（和/、/,/）拆分复合路径
    function addCandidateWithSplit(raw, start, end) {
        const p = normalizePathCandidate(raw);
        if (!p)
            return;
        // 按和/、拆分两个路径
        const parts = p.split(/[和](?=[\p{L}\p{N}_@+.-]+\.)/u);
        for (const part of parts) {
            const trimmed = part.trim();
            if (!trimmed || seen.has(trimmed))
                continue;
            addMaterial(trimmed, inferKindFromPath(trimmed), { start, end });
        }
    }
    function addCandidate(raw, start, end) {
        const p = normalizePathCandidate(raw);
        if (!p || seen.has(p))
            return;
        addMaterial(p, inferKindFromPath(p), { start, end });
    }
    // 0. 引号/反引号包裹路径
    for (const m of intent.matchAll(/[`"'“‘]([^`"'“”‘’]+(?:\/|\\)[^`"'“”‘’]+)[`"'”’]/gu)) {
        const p = normalizePathCandidate(m[1]);
        if (!p || !/(?:\/|\\)/.test(p))
            continue;
        addMaterial(p, inferKindFromPath(p), { start: safeMatchIndex(m), end: safeMatchIndex(m) + m[0].length });
    }
    // 1. URL 提取
    for (const m of intent.matchAll(/https?:\/\/[^\s"'“”‘’<>()（）\]【】]+/giu)) {
        if (overlapsRecorded(safeMatchIndex(m), safeMatchIndex(m) + m[0].length))
            continue;
        addCandidate(m[0], safeMatchIndex(m), safeMatchIndex(m) + m[0].length);
    }
    // 2. 文件路径（有扩展名）
    const filePathRe = new RegExp(`${PATH_START_BOUNDARY}((?:[A-Za-z]:[/\\\\]|~/\\.{1,2}/|/|[\\p{L}\\p{N}_@+.-]+[/\\\\])(?:${PATH_CHAR_CLASS}+[/\\\\])*${PATH_CHAR_CLASS}+\\.(${FILE_EXT_PATTERN}))`, "giu");
    for (const m of intent.matchAll(filePathRe)) {
        if (overlapsRecorded(safeMatchIndex(m), safeMatchIndex(m) + m[0].length))
            continue;
        addCandidateWithSplit(m[1], safeMatchIndex(m), safeMatchIndex(m) + m[0].length);
    }
    // 3. 裸文件名：当前目录下的明确文件材料，如 portal-ui.zip、README.md。
    const bareFileRe = new RegExp(`${PATH_START_BOUNDARY}([\\p{L}\\p{N}_@+-]+?\\.(${FILE_EXT_PATTERN}))`, "giu");
    for (const m of intent.matchAll(bareFileRe)) {
        if (overlapsRecorded(safeMatchIndex(m), safeMatchIndex(m) + m[0].length))
            continue;
        addCandidateWithSplit(m[1], safeMatchIndex(m), safeMatchIndex(m) + m[0].length);
    }
    // 4. 目录路径（无扩展名）
    const dirPathRe = new RegExp(`${PATH_START_BOUNDARY}((?:[A-Za-z]:[/\\\\]|~/\\.{1,2}/|/|[\\p{L}\\p{N}_@+.-]+[/\\\\])(?:${PATH_CHAR_CLASS}+[/\\\\])*${PATH_CHAR_CLASS}+)(?=\\/?(?:\\s|$|,|，|、|。|;|；|\\)|）|\\]|】|的|下|中|里))`, "giu");
    for (const m of intent.matchAll(dirPathRe)) {
        if (overlapsRecorded(safeMatchIndex(m), safeMatchIndex(m) + m[0].length))
            continue;
        addCandidate(m[1], safeMatchIndex(m), safeMatchIndex(m) + m[0].length);
    }
    // 推断缺失输入
    const hasPaths = materials.length > 0;
    const { inferred, missing } = inferMissingMaterials(intent, hasPaths);
    return { materials, inferred, missing };
}
function inferMissingMaterials(intent, hasPaths) {
    if (hasPaths)
        return { inferred: [], missing: [] };
    const inferred = [];
    const missing = [];
    if (/源码包|压缩包|zip包|包文件|源码/.test(intent)) {
        inferred.push({ kind: "archive", required: true, clarification: "请提供源码包 zip 或已解压源码目录路径" });
        missing.push("请提供源码包 zip 或已解压源码目录路径");
    }
    else if (/报错日志|日志/.test(intent)) {
        inferred.push({ kind: "log", required: true, clarification: "请提供日志文件路径或内容" });
        missing.push("请提供日志文件路径或内容");
    }
    else {
        inferred.push({ kind: "archive", required: true, clarification: "请提供输入材料路径" });
        missing.push("请提供输入材料路径");
    }
    return { inferred, missing };
}
//# sourceMappingURL=input_material_extractor.js.map