import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join, dirname } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { debugTime } from "../debug-logger.js";
import { loadPrompt } from "../prompt-loader.js";
import { resolveAllSkillReferences, renderPreferencesForSystemPrompt, loadEffectiveNerfPreferences } from "../preferences.js";
import { resolveNerfRootFile, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile } from "../paths.js";
import { hasSkillSnapshot, detectNewSkills, formatSkillsXml } from "../skill-discovery.js";
import { getActiveAutoWorktreeContext } from "../auto-worktree.js";
import { getActiveWorktreeName, getWorktreeOriginalCwd } from "../worktree-command.js";
import { deriveState } from "../state.js";
import { formatOverridesSection, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
import { toPosixPath } from "../../shared/mod.js";
import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../../cmux/index.js";
const nerfHome = process.env.NERF_HOME || join(homedir(), ".nerf");
// ─── Debug Logger ───────────────────────────────────────────────────────────
const debugCtx = process.env.NERF_DEBUG === "1"
    ? (msg) => process.stderr.write(`[nerf] system-context: ${msg}\n`)
    : () => { };
// ─── Legacy CLI memory module paths ─────────────────────────────────────────
// Uses absolute file:// URLs to avoid test hook .js→.ts rewriting (same as memory-bridge.ts)
const _contextDir = dirname(fileURLToPath(import.meta.url));
const _projectRoot = join(_contextDir, "..", "..", "..", "..", "..");
const _retrieverPath = pathToFileURL(join(_projectRoot, "cli", "lib", "memory", "retriever.js")).href;
const _sanitizerPath = pathToFileURL(join(_projectRoot, "cli", "lib", "memory", "sanitizer.js")).href;
function warnDeprecatedAgentInstructions() {
    const paths = [
        join(nerfHome, "agent-instructions.md"),
        join(process.cwd(), ".nerf", "agent-instructions.md"),
    ];
    for (const path of paths) {
        if (existsSync(path)) {
            console.warn(`[NERF] DEPRECATED: ${path} is no longer loaded. ` +
                `Migrate your instructions to AGENTS.md (or CLAUDE.md) in the same directory. ` +
                `See https://github.com/nerf-build/NERF-2/issues/1492`);
        }
    }
}
export async function buildBeforeAgentStartResult(event, ctx) {
    if (!existsSync(join(process.cwd(), ".nerf")))
        return undefined;
    const stopContextTimer = debugTime("context-inject");
    const systemContent = loadPrompt("system");
    const loadedPreferences = loadEffectiveNerfPreferences();
    if (shouldPromptToEnableCmux(loadedPreferences?.preferences)) {
        markCmuxPromptShown();
        ctx.ui.notify("cmux detected. Run /nerf cmux on to enable sidebar metadata, notifications, and visual subagent splits for this project.", "info");
    }
    let preferenceBlock = "";
    if (loadedPreferences) {
        const cwd = process.cwd();
        const report = resolveAllSkillReferences(loadedPreferences.preferences, cwd);
        preferenceBlock = `\n\n${renderPreferencesForSystemPrompt(loadedPreferences.preferences, report.resolutions)}`;
        if (report.warnings.length > 0) {
            ctx.ui.notify(`NERF skill preferences: ${report.warnings.length} unresolved skill${report.warnings.length === 1 ? "" : "s"}: ${report.warnings.join(", ")}`, "warning");
        }
    }
    const { block: knowledgeBlock, globalSizeKb } = loadKnowledgeBlock(nerfHome, process.cwd());
    if (globalSizeKb > 4) {
        ctx.ui.notify(`NERF: ~/.nerf/agent/KNOWLEDGE.md is ${globalSizeKb.toFixed(1)}KB — consider trimming to keep system prompt lean.`, "warning");
    }
    let memoryBlock = "";
    try {
        const { formatMemoriesForPrompt, getActiveMemoriesRanked } = await import("../memory-store.js");
        const memories = getActiveMemoriesRanked(30);
        if (memories.length > 0) {
            const formatted = formatMemoriesForPrompt(memories, 2000);
            if (formatted) {
                memoryBlock = `\n\n${formatted}`;
            }
        }
    }
    catch {
        // non-fatal
    }
    // ─── Security Engagement Memory (legacy NerfMemory via bridge) ──────────
    let securityMemoryBlock = "";
    try {
        const enginePath = pathToFileURL(join(_projectRoot, "cli", "lib", "memory", "engine.js")).href;
        const engineMod = await import(enginePath);
        const retrieverMod = await import(_retrieverPath);
        const sanitizerMod = await import(_sanitizerPath);
        const { NerfMemory } = engineMod;
        const homeDir = process.env.HOME || process.env.USERPROFILE || "";
        const memoryDir = `${homeDir}/.nerf/memory/default`;
        if (homeDir && existsSync(memoryDir)) {
            const memory = new NerfMemory(memoryDir);
            try {
                const { AdaptiveRetriever, ContextBuilder } = retrieverMod;
                const { wrapForPrompt: wrapMem } = sanitizerMod;
                const retriever = new AdaptiveRetriever(memory, { enableReflection: false });
                const results = retriever.retrieve("security engagement findings IOCs TTPs vulnerabilities", "", 10);
                if (results && results.length > 0) {
                    const builder = new ContextBuilder({ maxTokens: 2000 });
                    const contextBlock = builder.build(results, "security engagement context");
                    if (contextBlock) {
                        // Wrap entire block in sanitizer structural isolation
                        const wrapped = wrapMem(contextBlock, "security-engagement-context");
                        securityMemoryBlock = `\n\n[SECURITY ENGAGEMENT MEMORY — Cross-session findings]\n\n${wrapped}`;
                        debugCtx(`injected ${results.length} security memories (${securityMemoryBlock.length} chars)`);
                    }
                }
                else {
                    debugCtx("no security memories found in legacy store");
                }
            }
            finally {
                memory.close();
            }
        }
    }
    catch {
        // Non-fatal: legacy memory retrieval failure should never block system prompt assembly
        debugCtx("security memory retrieval failed, skipping");
    }
    let newSkillsBlock = "";
    if (hasSkillSnapshot()) {
        const newSkills = detectNewSkills();
        if (newSkills.length > 0) {
            newSkillsBlock = formatSkillsXml(newSkills);
        }
    }
    warnDeprecatedAgentInstructions();
    const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
    const worktreeBlock = buildWorktreeContextBlock();
    const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — NERF]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${memoryBlock}${securityMemoryBlock}${newSkillsBlock}${worktreeBlock}`;
    stopContextTimer({
        systemPromptSize: fullSystem.length,
        injectionSize: injection?.length ?? 0,
        hasPreferences: preferenceBlock.length > 0,
        hasNewSkills: newSkillsBlock.length > 0,
    });
    return {
        systemPrompt: fullSystem,
        ...(injection
            ? {
                message: {
                    customType: "nerf-guided-context",
                    content: injection,
                    display: false,
                },
            }
            : {}),
    };
}
export function loadKnowledgeBlock(nerfHomeDir, cwd) {
    // 1. Global knowledge (~/.nerf/agent/KNOWLEDGE.md) — cross-project, user-maintained
    let globalKnowledge = "";
    let globalSizeKb = 0;
    const globalKnowledgePath = join(nerfHomeDir, "agent", "KNOWLEDGE.md");
    if (existsSync(globalKnowledgePath)) {
        try {
            const content = readFileSync(globalKnowledgePath, "utf-8").trim();
            if (content) {
                globalSizeKb = Buffer.byteLength(content, "utf-8") / 1024;
                globalKnowledge = content;
            }
        }
        catch {
            // skip
        }
    }
    // 2. Project knowledge (.nerf/KNOWLEDGE.md) — project-specific
    let projectKnowledge = "";
    const knowledgePath = resolveNerfRootFile(cwd, "KNOWLEDGE");
    if (existsSync(knowledgePath)) {
        try {
            const content = readFileSync(knowledgePath, "utf-8").trim();
            if (content)
                projectKnowledge = content;
        }
        catch {
            // skip
        }
    }
    if (!globalKnowledge && !projectKnowledge) {
        return { block: "", globalSizeKb: 0 };
    }
    const parts = [];
    if (globalKnowledge)
        parts.push(`## Global Knowledge\n\n${globalKnowledge}`);
    if (projectKnowledge)
        parts.push(`## Project Knowledge\n\n${projectKnowledge}`);
    return {
        block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${parts.join("\n\n")}`,
        globalSizeKb,
    };
}
function buildWorktreeContextBlock() {
    const worktreeName = getActiveWorktreeName();
    const worktreeMainCwd = getWorktreeOriginalCwd();
    const autoWorktree = getActiveAutoWorktreeContext();
    if (worktreeName && worktreeMainCwd) {
        return [
            "",
            "",
            "[WORKTREE CONTEXT — OVERRIDES CURRENT WORKING DIRECTORY ABOVE]",
            `IMPORTANT: Ignore the "Current working directory" shown earlier in this prompt.`,
            `The actual current working directory is: ${toPosixPath(process.cwd())}`,
            "",
            `You are working inside a NERF worktree.`,
            `- Worktree name: ${worktreeName}`,
            `- Worktree path (this is the real cwd): ${toPosixPath(process.cwd())}`,
            `- Main project: ${toPosixPath(worktreeMainCwd)}`,
            `- Branch: worktree/${worktreeName}`,
            "",
            "All file operations, bash commands, and NERF state resolve against the worktree path above.",
            "Use /worktree merge to merge changes back. Use /worktree return to switch back to the main tree.",
        ].join("\n");
    }
    if (autoWorktree) {
        return [
            "",
            "",
            "[WORKTREE CONTEXT — OVERRIDES CURRENT WORKING DIRECTORY ABOVE]",
            `IMPORTANT: Ignore the "Current working directory" shown earlier in this prompt.`,
            `The actual current working directory is: ${toPosixPath(process.cwd())}`,
            "",
            "You are working inside a NERF auto-worktree.",
            `- Milestone worktree: ${autoWorktree.worktreeName}`,
            `- Worktree path (this is the real cwd): ${toPosixPath(process.cwd())}`,
            `- Main project: ${toPosixPath(autoWorktree.originalBase)}`,
            `- Branch: ${autoWorktree.branch}`,
            "",
            "All file operations, bash commands, and NERF state resolve against the worktree path above.",
            "Write every .nerf artifact in the worktree path above, never in the main project tree.",
        ].join("\n");
    }
    return "";
}
async function buildGuidedExecuteContextInjection(prompt, basePath) {
    const executeMatch = prompt.match(/Execute the next task:\s+(T\d+)\s+\("([^"]+)"\)\s+in slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
    if (executeMatch) {
        const [, taskId, taskTitle, sliceId, milestoneId] = executeMatch;
        return buildTaskExecutionContextInjection(basePath, milestoneId, sliceId, taskId, taskTitle);
    }
    const resumeMatch = prompt.match(/Resume interrupted work\.[\s\S]*?slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
    if (resumeMatch) {
        const [, sliceId, milestoneId] = resumeMatch;
        const state = await deriveState(basePath);
        if (state.activeMilestone?.id === milestoneId && state.activeSlice?.id === sliceId && state.activeTask) {
            return buildTaskExecutionContextInjection(basePath, milestoneId, sliceId, state.activeTask.id, state.activeTask.title);
        }
    }
    return null;
}
async function buildTaskExecutionContextInjection(basePath, milestoneId, sliceId, taskId, taskTitle) {
    const taskPlanPath = resolveTaskFile(basePath, milestoneId, sliceId, taskId, "PLAN");
    const taskPlanRelPath = relTaskFile(basePath, milestoneId, sliceId, taskId, "PLAN");
    const taskPlanContent = taskPlanPath ? await loadFile(taskPlanPath) : null;
    const taskPlanInline = taskPlanContent
        ? ["## Inlined Task Plan (authoritative local execution contract)", `Source: \`${taskPlanRelPath}\``, "", taskPlanContent.trim()].join("\n")
        : ["## Inlined Task Plan (authoritative local execution contract)", `Task plan not found at dispatch time. Read \`${taskPlanRelPath}\` before executing.`].join("\n");
    const slicePlanPath = resolveSliceFile(basePath, milestoneId, sliceId, "PLAN");
    const slicePlanRelPath = relSliceFile(basePath, milestoneId, sliceId, "PLAN");
    const slicePlanContent = slicePlanPath ? await loadFile(slicePlanPath) : null;
    const slicePlanExcerpt = extractSliceExecutionExcerpt(slicePlanContent, slicePlanRelPath);
    const priorTaskLines = await buildCarryForwardLines(basePath, milestoneId, sliceId, taskId);
    const resumeSection = await buildResumeSection(basePath, milestoneId, sliceId);
    const activeOverrides = await loadActiveOverrides(basePath);
    const overridesSection = formatOverridesSection(activeOverrides);
    return [
        "[NERF Guided Execute Context]",
        "Use this injected context as startup context for guided task execution. Treat the inlined task plan as the authoritative local execution contract. Use source artifacts to verify details and run checks.",
        overridesSection, "",
        "",
        resumeSection,
        "",
        "## Carry-Forward Context",
        ...priorTaskLines,
        "",
        taskPlanInline,
        "",
        slicePlanExcerpt,
        "",
        "## Backing Source Artifacts",
        `- Slice plan: \`${slicePlanRelPath}\``,
        `- Task plan source: \`${taskPlanRelPath}\``,
    ].join("\n");
}
async function buildCarryForwardLines(basePath, milestoneId, sliceId, taskId) {
    const tasksDir = resolveTasksDir(basePath, milestoneId, sliceId);
    if (!tasksDir)
        return ["- No prior task summaries in this slice."];
    const currentNum = parseInt(taskId.replace(/^T/, ""), 10);
    const sliceRel = relSlicePath(basePath, milestoneId, sliceId);
    const summaryFiles = resolveTaskFiles(tasksDir, "SUMMARY")
        .filter((file) => parseInt(file.replace(/^T/, ""), 10) < currentNum)
        .sort();
    if (summaryFiles.length === 0)
        return ["- No prior task summaries in this slice."];
    return Promise.all(summaryFiles.map(async (file) => {
        const absPath = join(tasksDir, file);
        const content = await loadFile(absPath);
        const relPath = `${sliceRel}/tasks/${file}`;
        if (!content)
            return `- \`${relPath}\``;
        const summary = parseSummary(content);
        const provided = summary.frontmatter.provides.slice(0, 2).join("; ");
        const decisions = summary.frontmatter.key_decisions.slice(0, 2).join("; ");
        const patterns = summary.frontmatter.patterns_established.slice(0, 2).join("; ");
        const diagnostics = extractMarkdownSection(content, "Diagnostics");
        const parts = [summary.title || relPath];
        if (summary.oneLiner)
            parts.push(summary.oneLiner);
        if (provided)
            parts.push(`provides: ${provided}`);
        if (decisions)
            parts.push(`decisions: ${decisions}`);
        if (patterns)
            parts.push(`patterns: ${patterns}`);
        if (diagnostics)
            parts.push(`diagnostics: ${oneLine(diagnostics)}`);
        return `- \`${relPath}\` — ${parts.join(" | ")}`;
    }));
}
async function buildResumeSection(basePath, milestoneId, sliceId) {
    const continueFile = resolveSliceFile(basePath, milestoneId, sliceId, "CONTINUE");
    const legacyDir = resolveSlicePath(basePath, milestoneId, sliceId);
    const legacyPath = legacyDir ? join(legacyDir, "continue.md") : null;
    const continueContent = continueFile ? await loadFile(continueFile) : null;
    const legacyContent = !continueContent && legacyPath ? await loadFile(legacyPath) : null;
    const resolvedContent = continueContent ?? legacyContent;
    const resolvedRelPath = continueContent
        ? relSliceFile(basePath, milestoneId, sliceId, "CONTINUE")
        : (legacyPath ? `${relSlicePath(basePath, milestoneId, sliceId)}/continue.md` : null);
    if (!resolvedContent || !resolvedRelPath) {
        return ["## Resume State", "- No continue file present. Start from the top of the task plan."].join("\n");
    }
    const cont = parseContinue(resolvedContent);
    const lines = [
        "## Resume State",
        `Source: \`${resolvedRelPath}\``,
        `- Status: ${cont.frontmatter.status || "in_progress"}`,
    ];
    if (cont.frontmatter.step && cont.frontmatter.totalSteps) {
        lines.push(`- Progress: step ${cont.frontmatter.step} of ${cont.frontmatter.totalSteps}`);
    }
    if (cont.completedWork)
        lines.push(`- Completed: ${oneLine(cont.completedWork)}`);
    if (cont.remainingWork)
        lines.push(`- Remaining: ${oneLine(cont.remainingWork)}`);
    if (cont.decisions)
        lines.push(`- Decisions: ${oneLine(cont.decisions)}`);
    if (cont.nextAction)
        lines.push(`- Next action: ${oneLine(cont.nextAction)}`);
    return lines.join("\n");
}
function extractSliceExecutionExcerpt(content, relPath) {
    if (!content) {
        return ["## Slice Plan Excerpt", `Slice plan not found at dispatch time. Read \`${relPath}\` before running slice-level verification.`].join("\n");
    }
    const lines = content.split("\n");
    const goalLine = lines.find((line) => line.startsWith("**Goal:**"))?.trim();
    const demoLine = lines.find((line) => line.startsWith("**Demo:**"))?.trim();
    const verification = extractMarkdownSection(content, "Verification");
    const observability = extractMarkdownSection(content, "Observability / Diagnostics");
    const parts = ["## Slice Plan Excerpt", `Source: \`${relPath}\``];
    if (goalLine)
        parts.push(goalLine);
    if (demoLine)
        parts.push(demoLine);
    if (verification)
        parts.push("", "### Slice Verification", verification.trim());
    if (observability)
        parts.push("", "### Slice Observability / Diagnostics", observability.trim());
    return parts.join("\n");
}
function extractMarkdownSection(content, heading) {
    const match = new RegExp(`^## ${escapeRegExp(heading)}\\s*$`, "m").exec(content);
    if (!match)
        return null;
    const start = match.index + match[0].length;
    const rest = content.slice(start);
    const nextHeading = rest.match(/^##\s+/m);
    const end = nextHeading?.index ?? rest.length;
    return rest.slice(0, end).trim();
}
function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function oneLine(text) {
    return text.replace(/\s+/g, " ").trim();
}
