"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_COMPACT_COLLECTION_PAGE_SIZE = void 0;
exports.getCompactCollectionProfile = getCompactCollectionProfile;
exports.projectDecisionCompleteCommandData = projectDecisionCompleteCommandData;
exports.getStablePaginationRecordId = getStablePaginationRecordId;
exports.createPageContinuationCommand = createPageContinuationCommand;
exports.renderPageContinuation = renderPageContinuation;
const node_crypto_1 = require("node:crypto");
exports.DEFAULT_COMPACT_COLLECTION_PAGE_SIZE = 10;
const IDENTITY_FIELDS = ["id", "command", "name", "path"];
const COMPACT_COLLECTION_PROFILES = Object.freeze({
    "spec list": profile("spec list", "entries"),
    "spec search": profile("spec search", "entries"),
    "commands inventory": profile("commands inventory", "entries", compactCommandInventory),
    "commands manifest": profile("commands manifest", "tools", identityProjection, 1),
    "commands search": profile("commands search", "matches", compactCommandSearch),
    "work candidates": profile("work candidates", "candidates", compactWorkCandidates, 1),
    "planning inventory": profile("planning inventory", "taskDefinitionPaths", compactPlanningInventory),
    "link materialize": profile("link materialize", "candidates", compactLinkMaterialize),
    "validation links-check": profile("validation links-check", "artifacts", compactValidationLinksCheck),
    "validation smoke": profile("validation smoke", "checks", compactValidationSmoke),
    "agent context": profile("agent context", "actions", compactAgentContext)
});
function getCompactCollectionProfile(command) {
    return COMPACT_COLLECTION_PROFILES[command];
}
function projectDecisionCompleteCommandData(command, data, options = {}) {
    if (command === "planning inventory") {
        return compactPlanningInventory(data, options.retainPageLane ?? true);
    }
    if (command === "link materialize") {
        return compactLinkMaterialize(data, options.retainPageLane ?? true);
    }
    if (command === "validation links-check") {
        return compactValidationLinksCheck(data, options.retainPageLane ?? true);
    }
    if (command === "validation smoke") {
        return compactValidationSmoke(data, options.retainPageLane ?? true);
    }
    if (command === "agent context") {
        return compactAgentContext(data, options.retainPageLane ?? true);
    }
    return undefined;
}
function getStablePaginationRecordId(value) {
    if (isRecord(value)) {
        for (const field of IDENTITY_FIELDS) {
            const identity = value[field];
            if (isScalar(identity)) {
                return scalarIdentity(identity);
            }
        }
    }
    if (isScalar(value)) {
        return scalarIdentity(value);
    }
    return hashJsonValue(value);
}
function createPageContinuationCommand(argv, cursor) {
    const args = argv.slice(2);
    const continued = [];
    let replaced = false;
    for (let index = 0; index < args.length; index += 1) {
        const token = args[index];
        if (token === "--page-cursor") {
            continued.push(token, cursor);
            replaced = true;
            index += 1;
        }
        else if (token.startsWith("--page-cursor=")) {
            continued.push(`--page-cursor=${cursor}`);
            replaced = true;
        }
        else {
            continued.push(token);
        }
    }
    if (!replaced) {
        continued.push("--page-cursor", cursor);
    }
    return ["day-shift", ...continued].map(quoteShellArgument).join(" ");
}
function renderPageContinuation(command) {
    return `Next page: ${command}`;
}
function profile(command, lane, projectData = identityProjection, pageSize = exports.DEFAULT_COMPACT_COLLECTION_PAGE_SIZE) {
    return Object.freeze({ command, lane, pageSize, projectData });
}
function identityProjection(data) {
    return data;
}
function compactCommandInventory(data) {
    const entries = Array.isArray(data.entries)
        ? data.entries.map((entry) => isRecord(entry) ? pick(entry, [
            "command",
            "availability",
            "callable",
            "executableEligibility",
            "writeCapable",
            "capability",
            "artifactFamily",
            "mutationPosture",
            "authorization",
            "selectedArtifactRequired"
        ]) : entry)
        : data.entries;
    const coverage = isRecord(data.coverage)
        ? {
            ...pick(data.coverage, [
                "callableCount",
                "runtimeActionCount",
                "callableDescriptorCount",
                "registryCount",
                "mergedCount"
            ]),
            callableMissingCapabilityCount: arrayCount(data.coverage.callableMissingCapability),
            callableDescriptorMissingRuntimeActionCount: arrayCount(data.coverage.callableDescriptorMissingRuntimeAction),
            runtimeActionMissingCallableDescriptorCount: arrayCount(data.coverage.runtimeActionMissingCallableDescriptor),
            intentionalNonCallableCount: arrayCount(data.coverage.intentionalNonCallable),
            registryOnlyCount: arrayCount(data.coverage.registryOnly),
            mixedPostureFamilyCount: arrayCount(data.coverage.mixedPostureFamilies)
        }
        : data.coverage;
    return { ...data, entries, coverage };
}
function compactCommandSearch(data) {
    const matches = Array.isArray(data.matches)
        ? data.matches.map((match) => isRecord(match) ? pick(match, [
            "command",
            "summary",
            "score",
            "availability",
            "callable",
            "capability",
            "artifactFamily",
            "authorization",
            "writeCapable",
            "mutationPosture"
        ]) : match)
        : data.matches;
    return { ...data, matches };
}
function compactWorkCandidates(data) {
    const candidates = Array.isArray(data.candidates)
        ? data.candidates.map((candidate) => {
            if (!isRecord(candidate) || !isRecord(candidate.workEvidence))
                return candidate;
            const { compatibilityEvidence: _compatibilityEvidence, ...compactWorkEvidence } = candidate.workEvidence;
            if (isRecord(compactWorkEvidence.project)
                && compactWorkEvidence.project.ownerProjectId === null
                && arrayCount(compactWorkEvidence.project.sharedProjectIds) === 0
                && arrayCount(compactWorkEvidence.project.dependencies) === 0) {
                delete compactWorkEvidence.project;
            }
            return {
                ...candidate,
                workEvidence: compactWorkEvidence
            };
        })
        : data.candidates;
    const workDiscovery = isRecord(data.workDiscovery)
        ? pick(data.workDiscovery, [
            "planningRootPath",
            "findings",
            "implicitSelection",
            "indexEvidence",
            "readOnly"
        ])
        : data.workDiscovery;
    return { ...data, candidates, workDiscovery };
}
function compactPlanningInventory(data, retainPageLane = true) {
    const pathLanes = [
        "workOverviewPaths",
        "sliceOverviewPaths",
        "phaseOverviewPaths",
        "milestoneOverviewPaths",
        "taskDefinitionPaths",
        "implementationSummaryPaths",
        "reconciliationPaths"
    ];
    const candidates = isRecord(data.workDiscovery) && Array.isArray(data.workDiscovery.candidates)
        ? data.workDiscovery.candidates
        : [];
    const findings = [
        ...(Array.isArray(data.findings) ? data.findings : []),
        ...(isRecord(data.workDiscovery) && Array.isArray(data.workDiscovery.findings)
            ? data.workDiscovery.findings
            : [])
    ];
    const nextActionCounts = countBy(candidates, (candidate) => (isRecord(candidate)
        && isRecord(candidate.nextAction)
        && typeof candidate.nextAction.nextAction === "string"
        ? candidate.nextAction.nextAction
        : "unspecified"));
    const statusCounts = countBy(candidates, (candidate) => (isRecord(candidate) && typeof candidate.status === "string"
        ? candidate.status
        : "unspecified"));
    return {
        ...pick(data, ["repositoryRoot", "detectionSource", "scopePath", "scopeKind"]),
        artifactTotals: Object.fromEntries(pathLanes.map((lane) => [lane, arrayCount(data[lane])])),
        findingCounts: countFindings(findings),
        nextActionEvidence: {
            candidateCount: candidates.length,
            statusCounts,
            nextActionCounts,
            blockerCount: candidates.reduce((count, candidate) => (count + (isRecord(candidate) && Array.isArray(candidate.blockers)
                ? candidate.blockers.length
                : 0)), 0)
        },
        discovery: isRecord(data.discovery)
            ? pick(data.discovery, ["complete", "healthClaim", "usage", "limits"])
            : data.discovery,
        collectionSummaries: Object.fromEntries(pathLanes.map((lane) => [
            lane,
            collectionSummary(arrayCount(data[lane]), lane === "taskDefinitionPaths" && retainPageLane
                ? arrayCount(data[lane])
                : 0)
        ])),
        ...(retainPageLane
            ? { taskDefinitionPaths: Array.isArray(data.taskDefinitionPaths) ? data.taskDefinitionPaths : [] }
            : {}),
        recovery: {
            audit: "Rerun planning inventory with --detail full for complete artifact and candidate evidence.",
            paging: "Use output.page.nextCursor with --page-lane taskDefinitionPaths and the same page size."
        }
    };
}
function compactLinkMaterialize(data, retainPageLane = true) {
    const skippedCandidates = Array.isArray(data.skippedCandidates) ? data.skippedCandidates : [];
    const candidates = Array.isArray(data.candidates) ? data.candidates : [];
    const candidateCount = numericCount(data.candidateCount, candidates.length);
    const skippedCandidateCount = numericCount(data.skippedCandidateCount, skippedCandidates.length);
    const changedPaths = Array.isArray(data.changedPaths) ? data.changedPaths : [];
    const dryRun = data.dryRun === true;
    const artifact = typeof data.artifact === "string" ? data.artifact : null;
    const validationEvidence = isRecord(data.validationEvidence) ? data.validationEvidence : {};
    const evidenceId = typeof validationEvidence.evidenceId === "string"
        ? validationEvidence.evidenceId
        : null;
    const postWriteValidationEvidence = isRecord(validationEvidence.postWriteValidationEvidence)
        ? validationEvidence.postWriteValidationEvidence
        : null;
    const requiredRecheck = isRecord(validationEvidence.requiredRecheck)
        ? validationEvidence.requiredRecheck
        : null;
    const requiredRecheckCommand = Array.isArray(requiredRecheck?.argv)
        && requiredRecheck.argv.every((value) => typeof value === "string")
        ? requiredRecheck.argv.map((value) => quoteShellArgument(value)).join(" ")
        : null;
    const writeCommand = artifact !== null && evidenceId !== null
        ? [
            "day-shift",
            "link",
            "materialize",
            "--write",
            "--artifact",
            artifact,
            "--validation-evidence-id",
            evidenceId
        ].map(quoteShellArgument).join(" ")
        : null;
    return {
        ...pick(data, [
            "repositoryRoot",
            "scope",
            "artifact",
            "artifactInputKind",
            "readOnly",
            "writeCapable",
            "dryRun",
            "localOnly",
            "candidateCount",
            "previewChangedPaths",
            "previewChangedArtifactPaths",
            "changedPaths",
            "changedArtifactPaths",
            "skippedCandidateCount",
            "rollbackGuidance",
            "validationEvidence"
        ]),
        skippedReasonGroups: countBy(skippedCandidates, (candidate) => (isRecord(candidate) && typeof candidate.reason === "string"
            ? candidate.reason
            : "unspecified")),
        collectionSummaries: {
            candidates: collectionSummary(candidateCount, retainPageLane ? candidates.length : 0),
            skippedCandidates: collectionSummary(skippedCandidateCount, 0)
        },
        ...(retainPageLane ? { candidates } : {}),
        nextAction: candidateCount === 0
            ? "No link materialization write is needed."
            : dryRun
                ? writeCommand
                    ?? "Candidate writes require exact selected-artifact revision-bound preview evidence."
                : changedPaths.length > 0
                    ? postWriteValidationEvidence?.reusableAsLifecycleLinkGate === true
                        ? "Link gate satisfied by current post-write validation evidence."
                        : requiredRecheckCommand
                            ?? "Post-write link-gate evidence is unavailable; stop before continuing."
                    : "No artifact changes were applied.",
        recovery: {
            audit: "Rerun link materialize with --detail full to inspect every candidate and skipped record.",
            paging: "Use output.page.nextCursor with --page-lane candidates and the same page size."
        }
    };
}
function compactValidationLinksCheck(data, retainPageLane = true) {
    const discovery = isRecord(data.discovery) ? data.discovery : {};
    const discoveryArtifacts = Array.isArray(discovery.artifacts) ? discovery.artifacts : [];
    const artifacts = discoveryArtifacts.map((artifact) => isRecord(artifact)
        ? {
            ...pick(artifact, ["path", "family", "artifactType", "parseStatus"]),
            ...(isRecord(artifact.lifecycle)
                ? { lifecycle: pick(artifact.lifecycle, ["status", "terminal"]) }
                : {})
        }
        : artifact);
    const findingSummary = isRecord(data.findingSummary) ? data.findingSummary : {};
    const blockingFindings = Array.isArray(findingSummary.blockingFindings)
        ? findingSummary.blockingFindings
        : [];
    const artifact = typeof data.artifact === "string" ? data.artifact : null;
    const auditCommand = [
        "day-shift",
        "validation",
        "links-check",
        "--scope",
        "planning",
        ...(artifact === null ? [] : ["--artifact", artifact]),
        "--format",
        "json",
        "--detail",
        "full"
    ].map(quoteShellArgument).join(" ");
    const blockingCount = numericCount(data.blockingFindingCount, blockingFindings.length);
    const advisoryCount = numericCount(data.advisoryFindingCount, 0);
    return {
        ...pick(data, [
            "repositoryRoot",
            "scope",
            "artifact",
            "artifactInputKind",
            "readOnly",
            "localOnly",
            "writeCapable",
            "implementationState",
            "checksImplemented",
            "checkedArtifactCount",
            "checkedRelationshipCount",
            "traceLinkFindingCount",
            "blockingFindingCount",
            "advisoryFindingCount",
            "selectedRevisionEvidence",
            "summary"
        ]),
        findingSummary: pick(findingSummary, [
            "totalCount",
            "blockingCount",
            "advisoryCount",
            "severityCounts",
            "codeCounts"
        ]),
        blockingFindings,
        collectionSummaries: {
            artifacts: collectionSummary(artifacts.length, retainPageLane ? artifacts.length : 0)
        },
        ...(retainPageLane ? { artifacts } : {}),
        safeNextAction: blockingCount > 0
            ? "Stop before writes. Follow the blocking finding recovery, then rerun this read-only links check."
            : advisoryCount > 0
                ? "Review advisory findings before selecting any separate write-capable recovery."
                : "The selected link-validation gate is clean; continue only through the next separately authorized lifecycle command.",
        recovery: {
            audit: auditCommand,
            ...(artifacts.length > exports.DEFAULT_COMPACT_COLLECTION_PAGE_SIZE
                ? { paging: "Use output.page.nextCursor with --page-lane artifacts and the same page size." }
                : {})
        }
    };
}
function compactValidationSmoke(data, retainPageLane = true) {
    const sourceChecks = Array.isArray(data.checks) ? data.checks : [];
    const checks = sourceChecks.map((check) => isRecord(check)
        ? pick(check, ["id", "status", "message"])
        : check);
    const findingSummary = isRecord(data.findingSummary) ? data.findingSummary : {};
    const blockingFindings = Array.isArray(findingSummary.blockingFindings)
        ? findingSummary.blockingFindings
        : [];
    const nextAction = isRecord(data.nextAction) ? data.nextAction : {};
    const artifact = typeof data.artifact === "string" ? data.artifact : null;
    const auditCommand = [
        "day-shift",
        "validation",
        "smoke",
        "--scope",
        "planning",
        ...(artifact === null ? [] : ["--artifact", artifact]),
        "--format",
        "json",
        "--detail",
        "full"
    ].map(quoteShellArgument).join(" ");
    return {
        ...pick(data, [
            "repositoryRoot",
            "detectionSource",
            "scope",
            "artifact",
            "planningRootPath",
            "readOnly",
            "writeCapable",
            "summary",
            "readinessStatus"
        ]),
        laneCounts: {
            checks: checks.length,
            inventoryFindings: arrayCount(data.inventoryFindings),
            gaps: arrayCount(data.gaps),
            safeNextActions: arrayCount(data.nextActions),
            blockingFindings: blockingFindings.length
        },
        findingSummary: pick(findingSummary, [
            "totalCount",
            "blockingCount",
            "advisoryCount",
            "severityCounts",
            "codeCounts"
        ]),
        blockingFindings,
        nextActions: Array.isArray(data.nextActions) ? data.nextActions : [],
        nextAction: pick(nextAction, [
            "artifactPath",
            "artifactType",
            "recommendation",
            "reason",
            "command",
            "workflowPath",
            "promptPath"
        ]),
        collectionSummaries: {
            checks: collectionSummary(checks.length, retainPageLane ? checks.length : 0)
        },
        ...(retainPageLane ? { checks } : {}),
        recovery: {
            audit: auditCommand,
            ...(checks.length > exports.DEFAULT_COMPACT_COLLECTION_PAGE_SIZE
                ? { paging: "Use output.page.nextCursor with --page-lane checks and the same page size." }
                : {})
        }
    };
}
function compactAgentContext(data, retainPageLane = true) {
    const allowedTransitions = Array.isArray(data.allowedTransitions) ? data.allowedTransitions : [];
    const actions = allowedTransitions.map((action) => isRecord(action)
        ? compactAgentAction(action)
        : action);
    const blockedActions = Array.isArray(data.blockedActions) ? data.blockedActions : [];
    const recommendedIdentity = isRecord(data.recommendedNextAction)
        && typeof data.recommendedNextAction.id === "string"
        ? data.recommendedNextAction.id
        : null;
    const recommendedSource = [...allowedTransitions, ...blockedActions].find((action) => (isRecord(action) && action.id === recommendedIdentity));
    const workEvidence = isRecord(data.workEvidence) ? data.workEvidence : null;
    return {
        ...pick(data, [
            "readOnly",
            "noWriteBoundary",
            "detail",
            "intent",
            "selection",
            "observedState",
            "resumeCapsule",
            "contextPack"
        ]),
        workEvidence: workEvidence === null
            ? null
            : pick(workEvidence, [
                "taskId",
                "workflowLevel",
                "effectiveAgentMode",
                "evidenceProfile",
                "nextAction",
                "basicWorkflow"
            ]),
        relevantBlockers: Array.isArray(data.relevantBlockers) ? data.relevantBlockers : [],
        gate: {
            blockerCount: arrayCount(data.relevantBlockers),
            allowedActionCount: allowedTransitions.length,
            blockedActionCount: blockedActions.length,
            permitted: allowedTransitions.length > 0,
            recommendedActionId: recommendedIdentity
        },
        nextCallableAction: isRecord(recommendedSource)
            ? compactAgentAction(recommendedSource)
            : recommendedIdentity === null
                ? null
                : { id: recommendedIdentity },
        collectionSummaries: {
            actions: collectionSummary(actions.length, retainPageLane ? actions.length : 0)
        },
        ...(retainPageLane ? { actions } : {}),
        recovery: {
            audit: "day-shift agent context --artifact <same-selected-artifact> --format json --detail full",
            ...(actions.length > exports.DEFAULT_COMPACT_COLLECTION_PAGE_SIZE
                ? { paging: "Use output.page.nextCursor with --page-lane actions and the same page size." }
                : {})
        }
    };
}
function compactAgentAction(action) {
    return {
        ...pick(action, [
            "id",
            "callable",
            "command",
            "argv",
            "writeCapable",
            "selectionRequired",
            "nonCallableReason"
        ]),
        ...(Array.isArray(action.preconditions)
            ? {
                preconditions: action.preconditions.map((precondition) => isRecord(precondition)
                    ? pick(precondition, ["id", "status"])
                    : precondition)
            }
            : {}),
        ...(isRecord(action.transition)
            ? { transition: pick(action.transition, ["id", "kind"]) }
            : {})
    };
}
function collectionSummary(originalCount, returnedCount) {
    const omittedCount = Math.max(0, originalCount - returnedCount);
    return {
        originalCount,
        returnedCount,
        omittedCount,
        complete: omittedCount === 0
    };
}
function countBy(values, classifier) {
    const counts = new Map();
    for (const value of values) {
        const key = classifier(value);
        counts.set(key, (counts.get(key) ?? 0) + 1);
    }
    return Object.fromEntries([...counts].sort(([left], [right]) => left.localeCompare(right)));
}
function countFindings(values) {
    return {
        total: values.length,
        error: values.filter((value) => isRecord(value) && value.severity === "error").length,
        warning: values.filter((value) => isRecord(value) && value.severity === "warning").length,
        informational: values.filter((value) => isRecord(value) && value.severity === "informational").length
    };
}
function numericCount(value, fallback) {
    return typeof value === "number" && Number.isSafeInteger(value) && value >= 0
        ? value
        : fallback;
}
function pick(source, fields) {
    return Object.fromEntries(fields
        .filter((field) => Object.hasOwn(source, field))
        .map((field) => [field, source[field]]));
}
function arrayCount(value) {
    return Array.isArray(value) ? value.length : 0;
}
function quoteShellArgument(value) {
    if (/^[A-Za-z0-9_./:@%+=,-]+$/u.test(value)) {
        return value;
    }
    return `'${value.replace(/'/g, `'"'"'`)}'`;
}
function hashJsonValue(value) {
    const serialized = JSON.stringify(value);
    if (serialized === undefined) {
        throw new TypeError("Pagination record identities require JSON-serializable values.");
    }
    return `sha256:${(0, node_crypto_1.createHash)("sha256").update(serialized).digest("hex")}`;
}
function scalarIdentity(value) {
    return value === null ? "null" : String(value);
}
function isScalar(value) {
    return value === null || ["string", "number", "boolean"].includes(typeof value);
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
