import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
export const CONTEXT_FILE_NAME = "CONTEXT.md";
const SECTION_HEADINGS = [
    "## Project",
    "## Purpose",
    "## Terms",
    "## Conventions",
    "## Open Questions",
    "## Not For This File",
];
export function createContextScaffold(projectName) {
    return `# ${projectName} Context

## Project

${projectName}

## Purpose

Describe the stable project purpose, domain, and mental model here.

## Terms

<!-- Threadwell writes accepted stable vocabulary here. Keep task history out. -->

- **Example term**: Replace with project-specific language.
  - Aliases: example alias
  - Related: related concept

## Conventions

- **Naming**: Record durable naming and architecture conventions here.

## Open Questions

- Record durable semantic ambiguities that need user clarification.

## Not For This File

Do not store raw logs, transient task history, secrets, validation output, or ephemeral progress here.
`;
}
function sectionBody(markdown, heading) {
    const start = markdown.indexOf(heading);
    if (start === -1)
        return "";
    const bodyStart = start + heading.length;
    const next = SECTION_HEADINGS.map((candidate) => markdown.indexOf(candidate, bodyStart))
        .filter((index) => index !== -1)
        .sort((left, right) => left - right)[0];
    return markdown.slice(bodyStart, next ?? markdown.length).trim();
}
function parseTerms(body) {
    const terms = [];
    const blocks = body.split(/\n(?=- \*\*)/g);
    for (const block of blocks) {
        const firstLine = block.split(/\r?\n/, 1)[0]?.trim();
        const match = firstLine?.match(/^- \*\*(.+?)\*\*:\s*(.+)$/);
        if (!match)
            continue;
        const aliases = block
            .match(/Aliases:\s*(.+)/)?.[1]
            .split(",")
            .map((entry) => entry.trim())
            .filter(Boolean);
        const related = block
            .match(/Related:\s*(.+)/)?.[1]
            .split(",")
            .map((entry) => entry.trim())
            .filter(Boolean);
        terms.push({
            name: match[1].trim(),
            meaning: match[2].trim(),
            aliases: aliases && aliases.length > 0 ? aliases : undefined,
            related: related && related.length > 0 ? related : undefined,
        });
    }
    return terms;
}
function parseConventions(body) {
    return body
        .split(/\r?\n/)
        .map((line) => line.trim())
        .flatMap((line) => {
        const match = line.match(/^- \*\*(.+?)\*\*:\s*(.+)$/);
        return match ? [{ name: match[1].trim(), description: match[2].trim() }] : [];
    });
}
function parseList(body) {
    return body
        .split(/\r?\n/)
        .map((line) => line.trim())
        .filter((line) => line.startsWith("- "))
        .map((line) => line.slice(2).trim())
        .filter(Boolean);
}
export function parseContextDocument(markdown) {
    return {
        project: sectionBody(markdown, "## Project") || "Unknown project",
        purpose: sectionBody(markdown, "## Purpose"),
        terms: parseTerms(sectionBody(markdown, "## Terms")),
        conventions: parseConventions(sectionBody(markdown, "## Conventions")),
        openQuestions: parseList(sectionBody(markdown, "## Open Questions")),
    };
}
export function getContextPath(projectRoot) {
    return join(projectRoot, CONTEXT_FILE_NAME);
}
export function ensureContextScaffold(projectRoot, projectName) {
    const path = getContextPath(projectRoot);
    if (existsSync(path))
        return { path, created: false };
    mkdirSync(dirname(path), { recursive: true });
    writeFileSync(path, createContextScaffold(projectName), "utf-8");
    return { path, created: true };
}
function formatContextTerm(term) {
    const aliases = term.aliases && term.aliases.length > 0 ? `\n  - Aliases: ${term.aliases.join(", ")}` : "";
    const related = term.related && term.related.length > 0 ? `\n  - Related: ${term.related.join(", ")}` : "";
    return `- **${term.name.trim()}**: ${term.meaning.trim()}${aliases}${related}`;
}
function isEphemeralContextTerm(term) {
    const text = `${term.name} ${term.meaning}`.toLowerCase();
    return /\b(task|todo|log|today|yesterday|temporary|validation output|stack trace|raw output|secret|token|password)\b/.test(text);
}
function insertBeforeNextSection(markdown, heading, content) {
    const start = markdown.indexOf(heading);
    if (start === -1)
        return `${markdown.trimEnd()}\n\n${heading}\n\n${content}\n`;
    const bodyStart = start + heading.length;
    const next = SECTION_HEADINGS.map((candidate) => markdown.indexOf(candidate, bodyStart))
        .filter((index) => index !== -1)
        .sort((left, right) => left - right)[0];
    if (next === undefined)
        return `${markdown.trimEnd()}\n${content}\n`;
    return `${markdown.slice(0, next).trimEnd()}\n${content}\n\n${markdown.slice(next).trimStart()}`;
}
export function mergeAcceptedContextTerms(markdown, terms) {
    const document = parseContextDocument(markdown);
    const existing = new Set(document.terms.map((term) => term.name.toLowerCase()));
    const accepted = terms.filter((term) => term.name.trim() && term.meaning.trim() && !isEphemeralContextTerm(term));
    const added = accepted.filter((term) => !existing.has(term.name.toLowerCase()));
    const skippedTerms = terms
        .filter((term) => !added.includes(term))
        .map((term) => term.name.trim())
        .filter(Boolean);
    if (added.length === 0)
        return { markdown, addedTerms: [], skippedTerms };
    const nextMarkdown = insertBeforeNextSection(markdown, "## Terms", `\n${added.map(formatContextTerm).join("\n")}`);
    return { markdown: nextMarkdown, addedTerms: added.map((term) => term.name), skippedTerms };
}
export function writeAcceptedContextTerms(projectRoot, projectName, terms) {
    const scaffold = ensureContextScaffold(projectRoot, projectName);
    const current = readFileSync(scaffold.path, "utf-8");
    const merged = mergeAcceptedContextTerms(current, terms);
    if (merged.markdown !== current) {
        writeFileSync(scaffold.path, merged.markdown, "utf-8");
    }
    return {
        path: scaffold.path,
        changed: merged.markdown !== current,
        addedTerms: merged.addedTerms,
        skippedTerms: merged.skippedTerms,
    };
}
export function findRelevantContextTerms(document, query, limit = 5) {
    const tokens = new Set(query
        .toLowerCase()
        .split(/[^a-z0-9_-]+/i)
        .map((token) => token.trim())
        .filter((token) => token.length >= 3));
    if (tokens.size === 0)
        return [];
    return document.terms
        .map((term) => {
        const haystack = [term.name, term.meaning, ...(term.aliases ?? []), ...(term.related ?? [])]
            .join(" ")
            .toLowerCase();
        const score = [...tokens].filter((token) => haystack.includes(token)).length;
        return { term, score };
    })
        .filter((entry) => entry.score > 0)
        .sort((left, right) => right.score - left.score)
        .slice(0, limit)
        .map((entry) => entry.term);
}
export function renderContextTerms(terms) {
    if (terms.length === 0)
        return undefined;
    return ["Relevant CONTEXT.md terms:", ...terms.map((term) => `- ${term.name}: ${term.meaning}`)].join("\n");
}
export function readContextDocument(projectRoot) {
    const path = getContextPath(projectRoot);
    if (!existsSync(path))
        return undefined;
    return parseContextDocument(readFileSync(path, "utf-8"));
}
//# sourceMappingURL=context-format.js.map