"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeReleaseManifestData = normalizeReleaseManifestData;
exports.normalizeReleaseNotesSourceSummary = normalizeReleaseNotesSourceSummary;
exports.normalizeReleaseMetadataReference = normalizeReleaseMetadataReference;
exports.normalizeReleaseManifestCompatibility = normalizeReleaseManifestCompatibility;
exports.normalizeCompatibilityVersionRange = normalizeCompatibilityVersionRange;
exports.normalizeManifestArtifactRecord = normalizeManifestArtifactRecord;
exports.isRecord = isRecord;
exports.collectInstallerAssetPaths = collectInstallerAssetPaths;
exports.isInstallerAssetFilename = isInstallerAssetFilename;
exports.classifyInstallerAssetTarget = classifyInstallerAssetTarget;
exports.normalizeDownloadOrigin = normalizeDownloadOrigin;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("../../../lib/errors");
const cli_constants_1 = require("../../../lib/config/cli-constants");
const workspace_compatibility_1 = require("../../../lib/workspace-compatibility");
const output_lifecycle_manifest_platforms_1 = require("./output-lifecycle-manifest-platforms");
const release_notes_contracts_1 = require("./release-notes-contracts");
function normalizeReleaseManifestData(value) {
    if (typeof value !== 'object' || value === null || Array.isArray(value)) {
        throw (0, errors_1.createUsageError)('Release manifest contains invalid top-level metadata.', [
            'Reason: malformed-release-manifest',
            'Recovery: regenerate the manifest from release artifact evidence.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    const manifest = value;
    if (!['active', 'deprecated', 'withdrawn', 'replaced'].includes(String(manifest.status))) {
        throw (0, errors_1.createUsageError)('Release manifest status metadata is missing or malformed.', [
            'Reason: malformed-manifest-status',
            'Recovery: regenerate the manifest from release publication metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!isRecord(manifest.command)) {
        throw (0, errors_1.createUsageError)('Release manifest command metadata is missing or malformed.', [
            'Reason: malformed-manifest-command',
            'Recovery: regenerate the manifest from the configured release command name and staged command path.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (typeof manifest.command.installedName !== 'string' ||
        manifest.command.installedName.trim().length === 0 ||
        typeof manifest.command.stagedCommandPath !== 'string' ||
        manifest.command.stagedCommandPath.trim().length === 0 ||
        !isRecord(manifest.command.invocations) ||
        typeof manifest.command.invocations.development !== 'string' ||
        manifest.command.invocations.development.trim().length === 0 ||
        typeof manifest.command.invocations.stagedRelease !== 'string' ||
        manifest.command.invocations.stagedRelease.trim().length === 0 ||
        typeof manifest.command.invocations.installedUser !== 'string' ||
        manifest.command.invocations.installedUser.trim().length === 0) {
        throw (0, errors_1.createUsageError)('Release manifest command metadata is missing required fields.', [
            'Reason: malformed-manifest-command',
            'Recovery: regenerate the manifest from the configured release command name and staged command path.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!isRecord(manifest.runtime)) {
        throw (0, errors_1.createUsageError)('Release manifest runtime metadata is missing or malformed.', [
            'Reason: malformed-manifest-runtime',
            'Recovery: regenerate the manifest from release runtime metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (typeof manifest.runtime.model !== 'string' ||
        typeof manifest.runtime.requiredNodeVersion !== 'string' ||
        manifest.runtime.requiredNodeVersion.trim().length === 0 ||
        typeof manifest.runtime.productionDependenciesIncluded !== 'boolean') {
        throw (0, errors_1.createUsageError)('Release manifest runtime metadata is missing required fields.', [
            'Reason: malformed-manifest-runtime',
            'Recovery: regenerate the manifest from release runtime metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!isRecord(manifest.installation)) {
        throw (0, errors_1.createUsageError)('Release manifest installation metadata is missing or malformed.', [
            'Reason: malformed-manifest-installation',
            'Recovery: regenerate the manifest from release installation metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (typeof manifest.installation.readmePath !== 'string' ||
        !isRecord(manifest.installation.defaultInstallDirectory) ||
        typeof manifest.installation.defaultInstallDirectory.posix !== 'string' ||
        typeof manifest.installation.defaultInstallDirectory.windows !== 'string' ||
        !isRecord(manifest.installation.defaultBinDirectory) ||
        typeof manifest.installation.defaultBinDirectory.posix !== 'string' ||
        typeof manifest.installation.defaultBinDirectory.windows !== 'string') {
        throw (0, errors_1.createUsageError)('Release manifest installation metadata is missing required fields.', [
            'Reason: malformed-manifest-installation',
            'Recovery: regenerate the manifest from release installation metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!Array.isArray(manifest.artifacts)) {
        throw (0, errors_1.createUsageError)('Release manifest artifact metadata is missing or malformed.', [
            'Reason: malformed-manifest-artifact',
            'Recovery: regenerate the manifest from checksum artifact records.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    const compatibility = normalizeReleaseManifestCompatibility(manifest.compatibility);
    const supportedPlatforms = (0, output_lifecycle_manifest_platforms_1.normalizeReleaseManifestSupportedPlatforms)(manifest.supportedPlatforms);
    if (!isRecord(manifest.replacement) ||
        !['active', 'deprecated', 'withdrawn', 'replaced'].includes(String(manifest.replacement.status))) {
        throw (0, errors_1.createUsageError)('Release manifest replacement metadata is missing or malformed.', [
            'Reason: malformed-manifest-replacement',
            'Recovery: regenerate the manifest from release replacement metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!isRecord(manifest.releaseNotes) ||
        typeof manifest.releaseNotes.path !== 'string' ||
        typeof manifest.releaseNotes.url !== 'string') {
        throw (0, errors_1.createUsageError)('Release manifest release-notes metadata is missing or malformed.', [
            'Reason: malformed-manifest-release-notes',
            'Recovery: regenerate the manifest from release notes evidence.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    normalizeReleaseNotesSourceSummary(manifest.releaseNotes.sourceSummary);
    return {
        ...manifest,
        status: manifest.status,
        command: {
            installedName: manifest.command.installedName,
            stagedCommandPath: manifest.command.stagedCommandPath,
            invocations: {
                development: manifest.command.invocations.development,
                stagedRelease: manifest.command.invocations.stagedRelease,
                installedUser: manifest.command.invocations.installedUser,
            },
        },
        runtime: {
            model: manifest.runtime.model,
            requiredNodeVersion: manifest.runtime.requiredNodeVersion,
            productionDependenciesIncluded: manifest.runtime.productionDependenciesIncluded,
        },
        supportedPlatforms,
        installation: {
            readmePath: manifest.installation.readmePath,
            ...(manifest.installation.installerManifest === undefined
                ? {}
                : {
                    installerManifest: normalizeReleaseMetadataReference(manifest.installation.installerManifest, 'installer manifest'),
                }),
            defaultInstallDirectory: {
                posix: manifest.installation.defaultInstallDirectory.posix,
                windows: manifest.installation.defaultInstallDirectory.windows,
            },
            defaultBinDirectory: {
                posix: manifest.installation.defaultBinDirectory.posix,
                windows: manifest.installation.defaultBinDirectory.windows,
            },
        },
        compatibility,
        artifacts: manifest.artifacts.map((artifact) => normalizeManifestArtifactRecord(artifact)),
        releaseNotes: {
            ...manifest.releaseNotes,
            path: manifest.releaseNotes.path,
            url: manifest.releaseNotes.url,
        },
    };
}
function normalizeReleaseNotesSourceSummary(value) {
    if (!isRecord(value)) {
        throw (0, errors_1.createUsageError)('Release manifest release-notes source summary is missing or malformed.', [
            'Reason: malformed-release-notes-source-summary',
            'Recovery: regenerate release notes source metadata before manifest validation.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (value.status !== 'generated' && value.status !== 'not-generated') {
        throw (0, errors_1.createUsageError)('Release manifest release-notes source summary has an invalid status.', [
            'Reason: malformed-release-notes-source-summary',
            'Recovery: regenerate release notes source metadata before manifest validation.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!isRecord(value.counts)) {
        throw (0, errors_1.createUsageError)('Release manifest release-notes source summary has malformed count metadata.', [
            'Reason: malformed-release-notes-source-summary',
            'Recovery: regenerate release notes source metadata before manifest validation.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (isRecord(value.provenance)) {
        return {
            status: value.status,
            counts: value.counts,
            provenance: normalizeReleaseMetadataReference(value.provenance, 'release-notes provenance'),
        };
    }
    if (typeof value.previousReleaseMarker !== 'string' ||
        typeof value.planningArtifactRange !== 'string' ||
        !isRecord(value.marker) ||
        !Array.isArray(value.selectedSources) ||
        !Array.isArray(value.excludedSources)) {
        throw (0, errors_1.createUsageError)('Release manifest release-notes source summary has malformed trace metadata.', [
            'Reason: malformed-release-notes-source-summary',
            'Recovery: regenerate release notes source metadata before manifest validation.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    return value;
}
function normalizeReleaseMetadataReference(value, label) {
    if (!isRecord(value) ||
        typeof value.filename !== 'string' ||
        typeof value.path !== 'string' ||
        typeof value.downloadPath !== 'string' ||
        typeof value.url !== 'string' ||
        typeof value.byteSize !== 'number' ||
        !Number.isSafeInteger(value.byteSize) ||
        value.byteSize < 0 ||
        typeof value.sha256 !== 'string' ||
        !/^[a-f0-9]{64}$/.test(value.sha256)) {
        throw (0, errors_1.createUsageError)(`Release manifest ${label} reference is missing or malformed.`, [
            'Reason: malformed-release-metadata-reference',
            `Reference: ${label}`,
            'Recovery: regenerate the release metadata artifacts and manifest.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    return {
        filename: value.filename,
        path: value.path,
        downloadPath: value.downloadPath,
        url: value.url,
        byteSize: value.byteSize,
        sha256: value.sha256,
    };
}
function normalizeReleaseManifestCompatibility(value) {
    if (!isRecord(value)) {
        throw (0, errors_1.createUsageError)('Release manifest compatibility metadata is missing or malformed.', [
            'Reason: malformed-manifest-compatibility',
            'Recovery: regenerate the manifest from release compatibility metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!isRecord(value.artifactSchemas)) {
        throw (0, errors_1.createUsageError)('Release manifest compatibility artifact schema metadata is missing or malformed.', [
            'Reason: malformed-manifest-compatibility',
            'Recovery: regenerate the manifest from release compatibility metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    return {
        workspaceMetadata: normalizeCompatibilityVersionRange(value.workspaceMetadata, 'workspaceMetadata'),
        artifactSchemas: {
            specification: normalizeCompatibilityVersionRange(value.artifactSchemas.specification, 'artifactSchemas.specification'),
            planning: normalizeCompatibilityVersionRange(value.artifactSchemas.planning, 'artifactSchemas.planning'),
            prompt: normalizeCompatibilityVersionRange(value.artifactSchemas.prompt, 'artifactSchemas.prompt'),
            template: normalizeCompatibilityVersionRange(value.artifactSchemas.template, 'artifactSchemas.template'),
            promptManifest: normalizeCompatibilityVersionRange(value.artifactSchemas.promptManifest, 'artifactSchemas.promptManifest'),
            templateManifest: normalizeCompatibilityVersionRange(value.artifactSchemas.templateManifest, 'artifactSchemas.templateManifest'),
            releaseManifest: normalizeCompatibilityVersionRange(value.artifactSchemas.releaseManifest, 'artifactSchemas.releaseManifest'),
            releaseRuntimeMetadata: normalizeCompatibilityVersionRange(value.artifactSchemas.releaseRuntimeMetadata, 'artifactSchemas.releaseRuntimeMetadata'),
        },
    };
}
function normalizeCompatibilityVersionRange(value, label) {
    const parsed = (0, workspace_compatibility_1.readCompatibilityVersionRangeShape)(value);
    if (!parsed.ok && parsed.reason === 'missing') {
        throw (0, errors_1.createUsageError)('Release manifest compatibility range is missing or malformed.', [
            'Reason: malformed-manifest-compatibility',
            `Range: ${label}`,
            'Recovery: regenerate the manifest from release compatibility metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!parsed.ok && parsed.reason === 'malformed') {
        throw (0, errors_1.createUsageError)('Release manifest compatibility range must use positive integer bounds.', [
            'Reason: malformed-manifest-compatibility',
            `Range: ${label}`,
            'Recovery: regenerate the manifest from release compatibility metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!parsed.ok && parsed.reason === 'contradictory') {
        const record = value;
        throw (0, errors_1.createUsageError)('Release manifest compatibility range is contradictory.', [
            'Reason: contradictory-manifest-compatibility',
            `Range: ${label}`,
            `Minimum: ${String(record.minimum)}`,
            `Maximum: ${String(record.maximum)}`,
            'Recovery: regenerate the manifest with a minimum version less than or equal to the maximum version.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    if (!parsed.ok) {
        throw (0, errors_1.createUsageError)('Release manifest compatibility range is missing or malformed.', [
            'Reason: malformed-manifest-compatibility',
            `Range: ${label}`,
            'Recovery: regenerate the manifest from release compatibility metadata.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    return parsed.range;
}
function normalizeManifestArtifactRecord(value) {
    if (typeof value !== 'object' || value === null || Array.isArray(value)) {
        throw (0, errors_1.createUsageError)('Release manifest contains an invalid artifact entry.', [
            'Reason: malformed-manifest-artifact',
            'Recovery: regenerate the manifest from checksum artifact records.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    const artifact = value;
    const kind = artifact.kind;
    const target = artifact.target;
    const filename = artifact.filename;
    const artifactPath = artifact.path;
    const downloadPath = artifact.downloadPath;
    const url = artifact.url;
    const byteSize = artifact.byteSize;
    const sha256 = artifact.sha256;
    const runtimeModel = artifact.runtimeModel;
    if ((kind !== 'archive' && kind !== 'installer') ||
        !['macos-linux', 'windows', 'posix-installer', 'windows-installer'].includes(String(target)) ||
        typeof filename !== 'string' ||
        filename.trim().length === 0 ||
        typeof artifactPath !== 'string' ||
        artifactPath.trim().length === 0 ||
        typeof downloadPath !== 'string' ||
        downloadPath.trim().length === 0 ||
        typeof url !== 'string' ||
        url.trim().length === 0 ||
        typeof byteSize !== 'number' ||
        !Number.isSafeInteger(byteSize) ||
        byteSize < 0 ||
        typeof sha256 !== 'string' ||
        !/^[a-f0-9]{64}$/.test(sha256) ||
        typeof runtimeModel !== 'string') {
        throw (0, errors_1.createUsageError)('Release manifest artifact metadata is missing or malformed.', [
            'Reason: malformed-manifest-artifact',
            'Recovery: regenerate the manifest from checksum artifact records.',
            'Exit behavior: exits before successful-release state is recorded.',
        ]);
    }
    return {
        ...artifact,
        kind,
        target: target,
        filename,
        path: artifactPath,
        downloadPath,
        url,
        byteSize,
        sha256,
        runtimeModel: runtimeModel,
    };
}
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function collectInstallerAssetPaths(versionDirectory) {
    const bundleDirectory = node_path_1.default.join(versionDirectory, release_notes_contracts_1.RELEASE_DOWNLOAD_BUNDLE_DIRECTORY);
    if (!node_fs_1.default.existsSync(bundleDirectory)) {
        return [];
    }
    return node_fs_1.default
        .readdirSync(bundleDirectory, { withFileTypes: true })
        .filter((entry) => entry.isFile())
        .map((entry) => node_path_1.default.join(bundleDirectory, entry.name))
        .filter((assetPath) => isInstallerAssetFilename(node_path_1.default.basename(assetPath)))
        .sort();
}
function isInstallerAssetFilename(filename) {
    return (/^install(?:-[a-z0-9._-]+)?\.(?:sh|ps1|cmd)$/i.test(filename) ||
        /^uninstall(?:-[a-z0-9._-]+)?\.(?:sh|ps1|cmd)$/i.test(filename) ||
        /^day-shift-installer(?:-[a-z0-9._-]+)?\.(?:sh|ps1|cmd)$/i.test(filename));
}
function classifyInstallerAssetTarget(filename) {
    return /\.(?:ps1|cmd)$/i.test(filename) ? 'windows-installer' : 'posix-installer';
}
function normalizeDownloadOrigin(value) {
    const trimmed = value.trim();
    if (!/^https:\/\/[^/\s]+(?:\/[^\s]*)?$/i.test(trimmed)) {
        throw (0, errors_1.createUsageError)('Release installer official origin must be an HTTPS URL.', [
            'Reason: invalid-installer-origin',
            `Recovery: provide an HTTPS website distribution origin such as ${cli_constants_1.DEFAULT_RELEASE_DOWNLOAD_ORIGIN}.`,
            'Exit behavior: exits before generated installer assets are created.',
        ]);
    }
    return trimmed.endsWith('/') ? trimmed : `${trimmed}/`;
}
