"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OrderedRootIdentityError = void 0;
exports.parseOrderedDirectoryIdentity = parseOrderedDirectoryIdentity;
exports.composeOrderedDirectoryName = composeOrderedDirectoryName;
exports.normalizeImplementationOrder = normalizeImplementationOrder;
exports.allocateOrderedRoot = allocateOrderedRoot;
exports.allocateStructuredTaskSequences = allocateStructuredTaskSequences;
exports.inspectOrderedRootCollectionSync = inspectOrderedRootCollectionSync;
exports.inspectStructuredTaskCollectionSync = inspectStructuredTaskCollectionSync;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const paths_1 = require("../paths");
const canonical_write_target_1 = require("../paths/canonical-write-target");
const planning_document_utils_1 = require("./planning-document-utils");
class OrderedRootIdentityError extends Error {
    code;
    constructor(code, message) {
        super(message);
        this.name = "OrderedRootIdentityError";
        this.code = code;
    }
}
exports.OrderedRootIdentityError = OrderedRootIdentityError;
const STABLE_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
const ORDERED_BASENAME_PATTERN = /^(\d{2,})-([a-z0-9]+(?:-[a-z0-9]+)*)$/u;
const IMPLEMENTATION_ORDER_PATTERN = /^(?:0[1-9]|[1-9]\d+)$/u;
/** Parses one basename without treating a numeric-looking semantic slug as an order implicitly. */
function parseOrderedDirectoryIdentity(context, basename) {
    const normalized = requireStableBasename(basename);
    const orderedMatch = ORDERED_BASENAME_PATTERN.exec(normalized);
    if (orderedMatch !== null) {
        const orderPrefix = normalizeImplementationOrder(orderedMatch[1], `${context} prefix`);
        return {
            context,
            basename: normalized,
            stableId: orderedMatch[2],
            order: Number.parseInt(orderPrefix, 10),
            orderPrefix,
            shape: "canonical"
        };
    }
    if (!STABLE_ID_PATTERN.test(normalized)) {
        throw new OrderedRootIdentityError("invalid-directory-name", `Directory '${basename}' is not a valid ${context} identity.`);
    }
    return {
        context,
        basename: normalized,
        stableId: normalized,
        order: null,
        orderPrefix: null,
        shape: "legacy"
    };
}
function composeOrderedDirectoryName(context, stableId, order) {
    if (!STABLE_ID_PATTERN.test(stableId)) {
        throw new OrderedRootIdentityError("invalid-directory-name", `${context} stable identity '${stableId}' must be lowercase kebab-case.`);
    }
    return `${normalizeImplementationOrder(order, `${context} order`)}-${stableId}`;
}
function normalizeImplementationOrder(value, label = "implementation_order") {
    const normalized = typeof value === "number" && Number.isSafeInteger(value)
        ? String(value).padStart(2, "0")
        : typeof value === "string"
            ? value.trim()
            : "";
    if (!IMPLEMENTATION_ORDER_PATTERN.test(normalized)) {
        throw new OrderedRootIdentityError("invalid-implementation-order", `${label} must be a positive whole number rendered with at least two digits.`);
    }
    return normalized;
}
function allocateOrderedRoot(input) {
    const occupied = new Set(input.inventory.occupiedOrders);
    let implementationOrder;
    let implementationOrderSource;
    if (input.requestedOrder === "next") {
        implementationOrder = nextAvailableOrder(occupied);
        implementationOrderSource = "next";
    }
    else if (input.requestedOrder !== undefined && input.requestedOrder !== null) {
        implementationOrder = normalizeImplementationOrder(input.requestedOrder);
        implementationOrderSource = "explicit";
    }
    else {
        if (input.context === "basic-root") {
            throw new OrderedRootIdentityError("missing-implementation-order", "Basic creation requires an explicit implementation order or 'next'.");
        }
        const inherited = [...new Set((input.inheritedOrders ?? []).map((entry) => normalizeImplementationOrder(entry, "source specification implementation_order")))];
        if (inherited.length === 0) {
            throw new OrderedRootIdentityError("missing-implementation-order", "Structured creation requires an explicit order, 'next', or one source-spec implementation order.");
        }
        if (inherited.length !== 1) {
            throw new OrderedRootIdentityError("conflicting-implementation-order", `Structured source specifications disagree on implementation order: ${inherited.join(", ")}.`);
        }
        implementationOrder = inherited[0];
        implementationOrderSource = "source-spec";
    }
    if (occupied.has(implementationOrder)) {
        throw new OrderedRootIdentityError("duplicate-implementation-order", `${input.context} implementation order '${implementationOrder}' is already occupied.`);
    }
    if (input.inventory.stableIds.includes(input.stableId)) {
        throw new OrderedRootIdentityError("duplicate-stable-identity", `${input.context} stable identity '${input.stableId}' is already active.`);
    }
    return {
        implementationOrder,
        implementationOrderSource,
        orderedRootName: composeOrderedDirectoryName(input.context, input.stableId, implementationOrder)
    };
}
function allocateStructuredTaskSequences(input) {
    const existingIds = new Set(input.inventory.entries.map((entry) => entry.taskId));
    const requestedIds = new Set();
    for (const taskId of input.taskIds) {
        if (ORDERED_BASENAME_PATTERN.test(taskId)) {
            throw new OrderedRootIdentityError("prefixed-semantic-identity", `Structured task identity '${taskId}' must be unprefixed. The sequence prefix is applied separately; retaining it would create a double-prefixed directory such as '01-${taskId}'.`);
        }
        if (!STABLE_ID_PATTERN.test(taskId)) {
            throw new OrderedRootIdentityError("invalid-directory-name", `Structured task identity '${taskId}' must be lowercase kebab-case.`);
        }
        if (existingIds.has(taskId) || requestedIds.has(taskId)) {
            throw new OrderedRootIdentityError("duplicate-stable-identity", `Structured task identity '${taskId}' is already active or repeated in the request.`);
        }
        requestedIds.add(taskId);
    }
    const highest = input.inventory.entries.reduce((maximum, entry) => Math.max(maximum, entry.sequence), 0);
    if (highest + input.taskIds.length > 99) {
        throw new OrderedRootIdentityError("order-exhausted", "Structured task sequence allocation exceeds the two-digit 99 limit.");
    }
    return input.taskIds.map((taskId, index) => {
        const sequence = highest + index + 1;
        const sequencePrefix = normalizeImplementationOrder(sequence, "Structured task sequence");
        return {
            taskId,
            sequence,
            sequencePrefix,
            orderedTaskRootName: composeOrderedDirectoryName("structured-task", taskId, sequence)
        };
    });
}
function inspectOrderedRootCollectionSync(input) {
    const collectionPath = normalizeCollectionPath(input.collectionPath);
    const collection = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(input.repositoryRoot, collectionPath);
    if (!collection.exists) {
        return finalizeRootInventory(input.context, collectionPath, []);
    }
    if (!node_fs_1.default.statSync(collection.runtimePath).isDirectory()) {
        throw new OrderedRootIdentityError("inventory-incomplete", `Ordered root collection '${collectionPath}' is not a directory.`);
    }
    const artifactName = input.context === "basic-root" ? "task-definition.md" : "overview.md";
    const entries = node_fs_1.default.readdirSync(collection.runtimePath, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .sort((left, right) => left.name.localeCompare(right.name))
        .map((directory) => {
        const parsed = parseOrderedDirectoryIdentity(input.context, directory.name);
        const rootPath = node_path_1.default.posix.join(collectionPath, directory.name);
        const artifactPath = node_path_1.default.posix.join(rootPath, artifactName);
        const artifact = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(input.repositoryRoot, artifactPath);
        if (!artifact.exists || !node_fs_1.default.statSync(artifact.runtimePath).isFile()) {
            throw new OrderedRootIdentityError("inventory-incomplete", `${input.context} '${directory.name}' is missing '${artifactName}'.`);
        }
        const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(node_fs_1.default.readFileSync(artifact.runtimePath, "utf8"));
        const stableId = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "id")
            ?? (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "task");
        if (stableId === undefined || stableId !== parsed.stableId) {
            throw new OrderedRootIdentityError("identity-mismatch", `${input.context} directory '${directory.name}' does not match artifact identity '${stableId ?? "missing"}'.`);
        }
        const recordedOrder = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "implementation_order");
        const implementationOrder = recordedOrder === undefined
            ? parsed.orderPrefix
            : normalizeImplementationOrder(recordedOrder);
        if (parsed.shape === "canonical" && recordedOrder === undefined) {
            throw new OrderedRootIdentityError("missing-implementation-order", `${input.context} '${directory.name}' is numbered but lacks implementation_order metadata.`);
        }
        if (parsed.orderPrefix !== null && implementationOrder !== parsed.orderPrefix) {
            throw new OrderedRootIdentityError("order-mismatch", `${input.context} '${directory.name}' disagrees with implementation_order '${implementationOrder}'.`);
        }
        const artifactPaths = input.context === "basic-root"
            ? [artifactPath, node_path_1.default.posix.join(rootPath, "implementation-summary.md")]
            : [artifactPath];
        for (const pairedPath of artifactPaths) {
            const paired = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(input.repositoryRoot, pairedPath);
            if (!paired.exists || !node_fs_1.default.statSync(paired.runtimePath).isFile()) {
                throw new OrderedRootIdentityError("inventory-incomplete", `${input.context} '${directory.name}' is missing '${node_path_1.default.posix.basename(pairedPath)}'.`);
            }
        }
        return {
            ...parsed,
            implementationOrder,
            artifactPaths
        };
    });
    return finalizeRootInventory(input.context, collectionPath, entries);
}
function inspectStructuredTaskCollectionSync(input) {
    const collectionPath = normalizeCollectionPath(input.collectionPath);
    const collection = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(input.repositoryRoot, collectionPath);
    if (!collection.exists)
        return finalizeTaskInventory(collectionPath, []);
    if (!node_fs_1.default.statSync(collection.runtimePath).isDirectory()) {
        throw new OrderedRootIdentityError("inventory-incomplete", `Structured task collection '${collectionPath}' is not a directory.`);
    }
    const entries = node_fs_1.default.readdirSync(collection.runtimePath, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .sort((left, right) => left.name.localeCompare(right.name))
        .map((directory) => {
        const parsed = parseOrderedDirectoryIdentity("structured-task", directory.name);
        const taskDefinitionPath = node_path_1.default.posix.join(collectionPath, directory.name, "task-definition.md");
        const implementationSummaryPath = node_path_1.default.posix.join(collectionPath, directory.name, "implementation-summary.md");
        const taskDefinition = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(input.repositoryRoot, taskDefinitionPath);
        const implementationSummary = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(input.repositoryRoot, implementationSummaryPath);
        if (!taskDefinition.exists || !node_fs_1.default.statSync(taskDefinition.runtimePath).isFile()
            || !implementationSummary.exists || !node_fs_1.default.statSync(implementationSummary.runtimePath).isFile()) {
            throw new OrderedRootIdentityError("inventory-incomplete", `Structured task '${directory.name}' must contain its canonical task pair.`);
        }
        const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(node_fs_1.default.readFileSync(taskDefinition.runtimePath, "utf8"));
        const taskId = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "id")
            ?? (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "task");
        const sequenceText = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "sequence");
        if (taskId === undefined || taskId !== parsed.stableId) {
            throw new OrderedRootIdentityError("identity-mismatch", `Structured task directory '${directory.name}' does not match task identity '${taskId ?? "missing"}'.`);
        }
        const sequence = sequenceText !== undefined && /^\d+$/u.test(sequenceText)
            ? Number.parseInt(sequenceText, 10)
            : Number.NaN;
        if (!Number.isInteger(sequence) || sequence < 1 || sequence > 99) {
            throw new OrderedRootIdentityError("invalid-implementation-order", `Structured task '${taskId}' has invalid sequence '${sequenceText ?? "missing"}'.`);
        }
        if (parsed.order !== null && parsed.order !== sequence) {
            throw new OrderedRootIdentityError("order-mismatch", `Structured task '${directory.name}' prefix does not match sequence '${sequence}'.`);
        }
        return {
            ...parsed,
            taskId,
            sequence,
            taskDefinitionPath,
            implementationSummaryPath
        };
    });
    return finalizeTaskInventory(collectionPath, entries);
}
function finalizeRootInventory(context, collectionPath, entries) {
    rejectDuplicates(entries, (entry) => entry.stableId, "duplicate-stable-identity", context);
    rejectDuplicates(entries.filter((entry) => entry.implementationOrder !== null), (entry) => entry.implementationOrder, "duplicate-implementation-order", context);
    const artifactPaths = entries.flatMap((entry) => entry.artifactPaths);
    const projection = entries.map((entry) => ({
        basename: entry.basename,
        stableId: entry.stableId,
        implementationOrder: entry.implementationOrder,
        shape: entry.shape,
        artifactPaths: entry.artifactPaths
    }));
    return {
        context,
        collectionPath,
        entries,
        occupiedOrders: entries.flatMap((entry) => entry.implementationOrder === null ? [] : [entry.implementationOrder]),
        stableIds: entries.map((entry) => entry.stableId),
        artifactPaths,
        fingerprint: digest(projection)
    };
}
function finalizeTaskInventory(collectionPath, entries) {
    rejectDuplicates(entries, (entry) => entry.taskId, "duplicate-stable-identity", "structured-task");
    rejectDuplicates(entries, (entry) => String(entry.sequence), "duplicate-task-sequence", "structured-task");
    const artifactPaths = entries.flatMap((entry) => [
        entry.taskDefinitionPath,
        entry.implementationSummaryPath
    ]);
    const projection = entries.map((entry) => ({
        basename: entry.basename,
        taskId: entry.taskId,
        sequence: entry.sequence,
        shape: entry.shape,
        taskDefinitionPath: entry.taskDefinitionPath,
        implementationSummaryPath: entry.implementationSummaryPath
    }));
    return { collectionPath, entries, artifactPaths, fingerprint: digest(projection) };
}
function rejectDuplicates(entries, identity, code, context) {
    const observed = new Set();
    for (const entry of entries) {
        const value = identity(entry);
        if (observed.has(value)) {
            throw new OrderedRootIdentityError(code, `${context} inventory repeats '${value}'.`);
        }
        observed.add(value);
    }
}
function nextAvailableOrder(occupied) {
    for (let order = 1; Number.isSafeInteger(order); order += 1) {
        const candidate = String(order).padStart(2, "0");
        if (!occupied.has(candidate))
            return candidate;
    }
    throw new OrderedRootIdentityError("order-exhausted", "No implementation order remains available within the supported integer range.");
}
function normalizeCollectionPath(value) {
    try {
        return (0, paths_1.parseRepositoryRelativePath)(value).normalizedPath;
    }
    catch (error) {
        throw new OrderedRootIdentityError("inventory-incomplete", error instanceof Error ? error.message : "Ordered collection path is invalid.");
    }
}
function requireStableBasename(value) {
    if (typeof value !== "string" || value.trim() === "" || value.includes("/") || value.includes("\\")) {
        throw new OrderedRootIdentityError("invalid-directory-name", "Ordered directory identity must be one basename.");
    }
    return value.trim();
}
function digest(value) {
    return `sha256:${(0, node_crypto_1.createHash)("sha256").update(JSON.stringify(value)).digest("hex")}`;
}
