import path from "node:path";
import { promises as fs } from "node:fs";
import { buildBootstrapNextAction, buildBootstrapNextFileToRead, buildBootstrapNextSuggestedCommand, buildChecksToRun, buildSearchGuidance, buildStopConditions } from "./guidance.js";
import { loadCurrentFocus, syncCurrentFocusFromActiveHints } from "./memory.js";
import { normalizeMcpPack } from "./recommendations.js";
import { ensureDir, isIgnoredArtifactName, pathExists, readJson, relativeFrom, slugify, writeText } from "../utils/fs.js";
export function getStatePaths(targetRoot) {
    const root = path.join(targetRoot, ".agent", "state");
    return {
        root,
        currentPhase: path.join(root, "current-phase.json"),
        activeRoleHints: path.join(root, "active-role-hints.json"),
        historyDir: path.join(root, "history"),
        checkpointsDir: path.join(root, "checkpoints"),
        latestCheckpointJson: path.join(root, "checkpoints", "latest.json"),
        latestCheckpointMarkdown: path.join(root, "checkpoints", "latest.md"),
        handoffDir: path.join(root, "handoff"),
        latestTaskPackets: path.join(root, "latest-task-packets.json")
    };
}
export async function ensureStateLayout(targetRoot) {
    const paths = getStatePaths(targetRoot);
    await ensureDir(paths.historyDir);
    await ensureDir(paths.checkpointsDir);
    await ensureDir(paths.handoffDir);
    return paths;
}
export function buildPhaseId(label, timestamp) {
    const compactTimestamp = timestamp.replace(/[-:]/g, "").replace(/\..+$/, "").replace("T", "-");
    return `${compactTimestamp}-${slugify(label)}`;
}
export async function writePhaseRecord(targetRoot, record) {
    const paths = await ensureStateLayout(targetRoot);
    const payload = `${JSON.stringify(record, null, 2)}\n`;
    const historyPath = path.join(paths.historyDir, `${record.phaseId}.json`);
    await writeText(paths.currentPhase, payload);
    await writeText(historyPath, payload);
    return {
        currentPhasePath: paths.currentPhase,
        historyPath
    };
}
export async function loadCurrentPhase(targetRoot) {
    const paths = getStatePaths(targetRoot);
    if (!(await pathExists(paths.currentPhase))) {
        return null;
    }
    return readJson(paths.currentPhase);
}
export async function loadActiveRoleHints(targetRoot) {
    const paths = getStatePaths(targetRoot);
    if (!(await pathExists(paths.activeRoleHints))) {
        return null;
    }
    return readJson(paths.activeRoleHints);
}
export async function writeActiveRoleHints(targetRoot, record) {
    const paths = await ensureStateLayout(targetRoot);
    await writeText(paths.activeRoleHints, `${JSON.stringify(record, null, 2)}\n`);
    await syncCurrentFocusFromActiveHints(targetRoot, record);
    return paths.activeRoleHints;
}
export async function updateActiveRoleHints(targetRoot, patch) {
    const current = (await loadActiveRoleHints(targetRoot)) ?? {
        artifactType: "shrey-junior/active-role-hints",
        version: 2,
        updatedAt: new Date().toISOString(),
        activeRole: patch.activeRole,
        supportingRoles: [],
        authoritySource: patch.authoritySource,
        projectType: patch.projectType,
        readNext: [
            ".agent/state/current-phase.json",
            ".agent/state/checkpoints/latest.json",
            ".agent/context/commands.md",
            ".agent/context/tool-capabilities.md",
            ".agent/context/mcp-capabilities.md",
            ".agent/context/context-tree.json",
            ".agent/checks.yaml",
            ".agent/project.yaml"
        ],
        nextFileToRead: buildBootstrapNextFileToRead(),
        nextSuggestedCommand: buildBootstrapNextSuggestedCommand(),
        writeTargets: [".agent/tasks/*", ".agent/state/current-phase.json", ".agent/state/handoff/*"],
        checksToRun: buildChecksToRun(),
        stopConditions: buildStopConditions(),
        nextAction: buildBootstrapNextAction(),
        nextRecommendedSpecialist: patch.activeRole,
        nextSuggestedMcpPack: "core-pack",
        searchGuidance: buildSearchGuidance({
            projectType: patch.projectType
        }),
        latestMemoryFocus: ".agent/memory/current-focus.json",
        latestCheckpoint: null,
        latestTaskPacket: null,
        latestHandoff: null,
        latestDispatchManifest: null,
        latestReconcile: null
    };
    const next = {
        ...current,
        ...patch,
        updatedAt: patch.updatedAt ?? new Date().toISOString(),
        supportingRoles: patch.supportingRoles ?? current.supportingRoles,
        readNext: patch.readNext ?? current.readNext,
        nextFileToRead: patch.nextFileToRead ?? current.nextFileToRead,
        nextSuggestedCommand: patch.nextSuggestedCommand ?? current.nextSuggestedCommand,
        writeTargets: patch.writeTargets ?? current.writeTargets,
        checksToRun: patch.checksToRun ?? current.checksToRun,
        stopConditions: patch.stopConditions ?? current.stopConditions,
        nextAction: patch.nextAction ?? current.nextAction,
        nextRecommendedSpecialist: patch.nextRecommendedSpecialist ?? current.nextRecommendedSpecialist,
        nextSuggestedMcpPack: patch.nextSuggestedMcpPack ? normalizeMcpPack(patch.nextSuggestedMcpPack) : current.nextSuggestedMcpPack,
        searchGuidance: patch.searchGuidance ?? current.searchGuidance,
        latestMemoryFocus: patch.latestMemoryFocus ?? current.latestMemoryFocus ?? ".agent/memory/current-focus.json",
        latestCheckpoint: patch.latestCheckpoint ?? current.latestCheckpoint
    };
    return writeActiveRoleHints(targetRoot, next);
}
export async function loadLatestCheckpoint(targetRoot) {
    const paths = getStatePaths(targetRoot);
    if (!(await pathExists(paths.latestCheckpointJson))) {
        return null;
    }
    return readJson(paths.latestCheckpointJson);
}
export async function writeCheckpointArtifacts(targetRoot, record) {
    const paths = await ensureStateLayout(targetRoot);
    const jsonPath = path.join(paths.checkpointsDir, `${record.checkpointId}.json`);
    const latestJsonPath = paths.latestCheckpointJson;
    const latestMarkdownPath = paths.latestCheckpointMarkdown;
    const jsonPayload = `${JSON.stringify(record, null, 2)}\n`;
    const markdown = renderCheckpointMarkdown(record);
    await writeText(jsonPath, jsonPayload);
    await writeText(latestJsonPath, jsonPayload);
    await writeText(latestMarkdownPath, markdown);
    const activeRoleHints = await loadActiveRoleHints(targetRoot);
    if (activeRoleHints) {
        await updateActiveRoleHints(targetRoot, {
            activeRole: activeRoleHints.activeRole,
            authoritySource: activeRoleHints.authoritySource,
            projectType: activeRoleHints.projectType,
            supportingRoles: activeRoleHints.supportingRoles,
            latestCheckpoint: relativeFrom(targetRoot, latestJsonPath)
        });
    }
    return {
        jsonPath,
        latestJsonPath,
        latestMarkdownPath
    };
}
export async function loadLatestHandoff(targetRoot, toTool) {
    const paths = getStatePaths(targetRoot);
    if (!(await pathExists(paths.handoffDir))) {
        return null;
    }
    const latestPath = path.join(paths.handoffDir, "latest.json");
    if (!toTool && (await pathExists(latestPath))) {
        return readJson(latestPath);
    }
    const entries = await fs.readdir(paths.handoffDir);
    const candidates = entries
        .filter((entry) => entry.endsWith(".json") && !isIgnoredArtifactName(entry))
        .sort()
        .reverse();
    for (const entry of candidates) {
        const fullPath = path.join(paths.handoffDir, entry);
        const record = await readJson(fullPath);
        if (!toTool || record.toTool === toTool) {
            return record;
        }
    }
    return null;
}
export async function writeHandoffArtifacts(targetRoot, baseName, handoff, markdown, brief) {
    const paths = await ensureStateLayout(targetRoot);
    const markdownPath = path.join(paths.handoffDir, `${baseName}.md`);
    const jsonPath = path.join(paths.handoffDir, `${baseName}.json`);
    const briefPath = path.join(paths.handoffDir, `${baseName}.brief.md`);
    const latestMarkdownPath = path.join(paths.handoffDir, "latest.md");
    const latestJsonPath = path.join(paths.handoffDir, "latest.json");
    const latestBriefPath = path.join(paths.handoffDir, "latest.brief.md");
    await writeText(markdownPath, markdown);
    await writeText(jsonPath, `${JSON.stringify(handoff, null, 2)}\n`);
    await writeText(briefPath, brief);
    await writeText(latestMarkdownPath, markdown);
    await writeText(latestJsonPath, `${JSON.stringify(handoff, null, 2)}\n`);
    await writeText(latestBriefPath, brief);
    const activeRoleHints = await loadActiveRoleHints(targetRoot);
    if (activeRoleHints) {
        await updateActiveRoleHints(targetRoot, {
            activeRole: activeRoleHints.activeRole,
            authoritySource: activeRoleHints.authoritySource,
            projectType: activeRoleHints.projectType,
            supportingRoles: activeRoleHints.supportingRoles,
            latestHandoff: relativeFrom(targetRoot, latestJsonPath)
        });
    }
    return { markdownPath, jsonPath, briefPath };
}
export async function writeLatestTaskPacketSet(targetRoot, files) {
    if (files.length === 0) {
        return null;
    }
    const paths = await ensureStateLayout(targetRoot);
    const packetSet = path.dirname(files[0] ?? ".agent/tasks");
    const payload = {
        artifactType: "shrey-junior/latest-task-packets",
        version: 1,
        createdAt: new Date().toISOString(),
        packetSet,
        files
    };
    await writeText(paths.latestTaskPackets, `${JSON.stringify(payload, null, 2)}\n`);
    const activeRoleHints = await loadActiveRoleHints(targetRoot);
    if (activeRoleHints) {
        await updateActiveRoleHints(targetRoot, {
            activeRole: activeRoleHints.activeRole,
            authoritySource: activeRoleHints.authoritySource,
            projectType: activeRoleHints.projectType,
            supportingRoles: activeRoleHints.supportingRoles,
            latestTaskPacket: relativeFrom(targetRoot, paths.latestTaskPackets)
        });
    }
    return paths.latestTaskPackets;
}
export async function listTaskPacketDirectories(targetRoot) {
    const taskRoot = path.join(targetRoot, ".agent", "tasks");
    if (!(await pathExists(taskRoot))) {
        return [];
    }
    const entries = await fs.readdir(taskRoot, { withFileTypes: true });
    const directories = [];
    for (const entry of entries) {
        if (!entry.isDirectory() || isIgnoredArtifactName(entry.name)) {
            continue;
        }
        const fullPath = path.join(taskRoot, entry.name);
        const fileEntries = await fs.readdir(fullPath, { withFileTypes: true });
        directories.push({
            name: entry.name,
            relativePath: relativeFrom(targetRoot, fullPath),
            fileCount: fileEntries.filter((fileEntry) => fileEntry.isFile() && fileEntry.name.endsWith(".md") && !isIgnoredArtifactName(fileEntry.name)).length
        });
    }
    return directories.sort((left, right) => right.name.localeCompare(left.name));
}
export async function loadLatestTaskPacketSet(targetRoot) {
    const paths = getStatePaths(targetRoot);
    if (!(await pathExists(paths.latestTaskPackets))) {
        return null;
    }
    return readJson(paths.latestTaskPackets);
}
export async function loadContinuitySnapshot(targetRoot, toTool) {
    return {
        latestPhase: await loadCurrentPhase(targetRoot),
        latestHandoff: await loadLatestHandoff(targetRoot, toTool),
        latestCheckpoint: await loadLatestCheckpoint(targetRoot),
        currentFocus: await loadCurrentFocus(targetRoot)
    };
}
export function parseCsvFlag(value) {
    if (!value) {
        return [];
    }
    return value
        .split(",")
        .map((item) => item.trim())
        .filter((item) => Boolean(item));
}
export function renderCheckpointMarkdown(record) {
    const lines = [
        `# Checkpoint ${record.phase}`,
        "",
        `Created: ${record.createdAt}`,
        `Active role: \`${record.activeRole}\``,
        `Authority source: \`${record.authoritySource}\``,
        ...(record.gitBranch ? [`Git branch: \`${record.gitBranch}\``] : ["Git branch: none"]),
        ...(record.gitCommitAfter ? [`Git commit: \`${record.gitCommitAfter}\``] : ["Git commit: none"]),
        "",
        "## Summary",
        "",
        `- ${record.summary}`,
        "",
        "## Task Context",
        "",
        `- goal: ${record.taskContext.goal}`,
        `- task type: \`${record.taskContext.taskType}\``,
        `- file area: \`${record.taskContext.fileArea}\``,
        `- change size: \`${record.taskContext.changeSize}\``,
        `- risk: \`${record.taskContext.riskLevel}\``,
        ...(record.taskContext.primaryTool ? [`- primary tool: \`${record.taskContext.primaryTool}\``] : []),
        ...(record.taskContext.reviewTool ? [`- review tool: \`${record.taskContext.reviewTool}\``] : []),
        "",
        "## Files",
        "",
        ...(record.filesTouched.length > 0 ? record.filesTouched.map((item) => `- touched: \`${item}\``) : ["- touched: none recorded"]),
        ...(record.filesCreated.length > 0 ? record.filesCreated.map((item) => `- created: \`${item}\``) : []),
        ...(record.filesDeleted.length > 0 ? record.filesDeleted.map((item) => `- deleted: \`${item}\``) : []),
        "",
        "## Checks",
        "",
        ...(record.checksRun.length > 0 ? record.checksRun.map((item) => `- ran: ${item}`) : ["- ran: none recorded"]),
        ...(record.checksPassed.length > 0 ? record.checksPassed.map((item) => `- passed: ${item}`) : []),
        ...(record.checksFailed.length > 0 ? record.checksFailed.map((item) => `- failed: ${item}`) : []),
        "",
        "## Next",
        "",
        `- next specialist: ${record.nextRecommendedSpecialist}`,
        `- suggested MCP pack: ${record.nextSuggestedMcpPack}`,
        ...(record.latestMemoryFocus ? [`- latest memory focus: \`${record.latestMemoryFocus}\``] : []),
        `- next action: ${record.nextRecommendedAction}`,
        `- next command: ${record.nextSuggestedCommand}`
    ];
    return `${lines.join("\n")}\n`;
}
