"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildAssetKey = buildAssetKey;
exports.buildPackKey = buildPackKey;
exports.getTaskCreatorResult = getTaskCreatorResult;
exports.parseUnversionedAssetTaskRef = parseUnversionedAssetTaskRef;
exports.buildAssetPackUploadPlans = buildAssetPackUploadPlans;
exports.uploadAssetTask = uploadAssetTask;
exports.uploadAssetSpecTask = uploadAssetSpecTask;
exports.uploadAssetPackTask = uploadAssetPackTask;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const types_1 = require("@playdrop/types");
const model_artifacts_1 = require("../assets/model-artifacts");
const assetSpecs_1 = require("../assetSpecs");
const EXTENSION_TO_FORMAT = {
    '.png': 'PNG',
    '.jpg': 'JPEG',
    '.jpeg': 'JPEG',
    '.webp': 'WEBP',
    '.gif': 'GIF',
    '.aseprite': 'ASEPRITE',
    '.mp4': 'MP4',
    '.webm': 'WEBM',
    '.mp3': 'MP3',
    '.wav': 'WAV',
    '.ogg': 'OGG',
    '.vox': 'VOX',
    '.gltf': 'GLTF',
    '.glb': 'GLB',
};
const EXTENSION_TO_MIME = {
    '.png': 'image/png',
    '.jpg': 'image/jpeg',
    '.jpeg': 'image/jpeg',
    '.webp': 'image/webp',
    '.gif': 'image/gif',
    '.aseprite': 'application/octet-stream',
    '.mp4': 'video/mp4',
    '.webm': 'video/webm',
    '.mp3': 'audio/mpeg',
    '.wav': 'audio/wav',
    '.ogg': 'audio/ogg',
    '.vox': 'application/octet-stream',
    '.gltf': 'model/gltf+json',
    '.glb': 'model/gltf-binary',
    '.json': 'application/json',
};
function isPngSignature(buffer) {
    if (buffer.length < 8) {
        return false;
    }
    return buffer[0] === 0x89
        && buffer[1] === 0x50
        && buffer[2] === 0x4e
        && buffer[3] === 0x47
        && buffer[4] === 0x0d
        && buffer[5] === 0x0a
        && buffer[6] === 0x1a
        && buffer[7] === 0x0a;
}
function isMp4Signature(buffer) {
    return buffer.length >= 12
        && buffer[4] === 0x66
        && buffer[5] === 0x74
        && buffer[6] === 0x79
        && buffer[7] === 0x70;
}
function createListingFileFromPath(filePath, expectedMime) {
    const data = (0, node_fs_1.readFileSync)(filePath);
    if (expectedMime === 'image/png' && !isPngSignature(data)) {
        throw new Error(`Invalid listing file signature for ${filePath}. Expected image/png.`);
    }
    if (expectedMime === 'video/mp4' && !isMp4Signature(data)) {
        throw new Error(`Invalid listing file signature for ${filePath}. Expected video/mp4.`);
    }
    return new File([data], (0, node_path_1.basename)(filePath), { type: expectedMime });
}
function computeSha256Hex(buffer) {
    return (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
}
function resolveMimeType(filePath) {
    return EXTENSION_TO_MIME[(0, node_path_1.extname)(filePath).toLowerCase()] ?? 'application/octet-stream';
}
function prepareUploadFile(filePath, fieldName, explicitMimeType) {
    const data = (0, node_fs_1.readFileSync)(filePath);
    const filename = (0, node_path_1.basename)(filePath);
    const contentType = explicitMimeType ?? resolveMimeType(filePath);
    return {
        fieldName,
        filename,
        filePath,
        file: new File([data], filename, { type: contentType }),
        contentType,
        sizeBytes: data.length,
        sha256: computeSha256Hex(data),
    };
}
function buildListingMediaRelativePath(mediaKey) {
    if (mediaKey === 'icon')
        return 'listing/icon.png';
    if (mediaKey === 'heroPortrait')
        return 'listing/hero-portrait.png';
    if (mediaKey === 'heroLandscape')
        return 'listing/hero-landscape.png';
    const portraitScreenshot = /^screenshotsPortrait:(\d+)$/.exec(mediaKey);
    if (portraitScreenshot) {
        return `listing/screenshots/portrait/${Number.parseInt(portraitScreenshot[1], 10) + 1}.png`;
    }
    const landscapeScreenshot = /^screenshotsLandscape:(\d+)$/.exec(mediaKey);
    if (landscapeScreenshot) {
        return `listing/screenshots/landscape/${Number.parseInt(landscapeScreenshot[1], 10) + 1}.png`;
    }
    const portraitVideo = /^videosPortrait:(\d+)$/.exec(mediaKey);
    if (portraitVideo) {
        return `listing/videos/portrait/${Number.parseInt(portraitVideo[1], 10) + 1}.mp4`;
    }
    const landscapeVideo = /^videosLandscape:(\d+)$/.exec(mediaKey);
    if (landscapeVideo) {
        return `listing/videos/landscape/${Number.parseInt(landscapeVideo[1], 10) + 1}.mp4`;
    }
    throw new Error(`Unsupported pack listing media key "${mediaKey}".`);
}
function prepareListingMediaUpload(mediaKey, filePath, expectedMime) {
    const file = createListingFileFromPath(filePath, expectedMime);
    const buffer = (0, node_fs_1.readFileSync)(filePath);
    return {
        mediaKey,
        file,
        filename: (0, node_path_1.basename)(filePath),
        entry: {
            mediaKey,
            relativePath: buildListingMediaRelativePath(mediaKey),
            contentType: expectedMime,
            sizeBytes: buffer.length,
            sha256: computeSha256Hex(buffer),
        },
    };
}
function buildAssetKey(creatorUsername, name) {
    return `${creatorUsername}/${name}`;
}
function buildPackKey(creatorUsername, name, version) {
    return `${creatorUsername}/${name}@${version}`;
}
function getTaskCreatorResult(task, defaultCreator, currentUserRole) {
    const requestedTaskOwner = 'username' in task && typeof task.username === 'string'
        ? String(task.username).trim()
        : '';
    const creatorTargetError = requestedTaskOwner && currentUserRole !== 'ADMIN'
        ? `Task "${task.name}" sets username="${requestedTaskOwner}", but only admins can target another creator.`
        : null;
    return {
        requestedTaskOwner,
        taskCreator: requestedTaskOwner && !creatorTargetError ? requestedTaskOwner : defaultCreator,
        creatorTargetError,
    };
}
function parseUnversionedAssetTaskRef(rawRef, fallbackCreator) {
    const trimmed = rawRef.trim();
    if (!trimmed || trimmed.includes('@')) {
        return null;
    }
    const slashIndex = trimmed.indexOf('/');
    if (slashIndex > 0) {
        return {
            creatorUsername: trimmed.slice(0, slashIndex),
            name: trimmed.slice(slashIndex + 1),
        };
    }
    return {
        creatorUsername: fallbackCreator,
        name: trimmed,
    };
}
function buildAssetPackUploadPlans(tasks, defaultCreator, currentUserRole) {
    const packPlansByKey = new Map();
    const ownedPackByAssetKey = new Map();
    for (const task of tasks) {
        if (task.kind !== 'asset-pack') {
            continue;
        }
        const creator = getTaskCreatorResult(task, defaultCreator, currentUserRole);
        if (creator.creatorTargetError) {
            return {
                ok: false,
                task,
                message: creator.creatorTargetError,
            };
        }
        const packKey = buildPackKey(creator.taskCreator, task.name, task.version);
        const plan = {
            packKey,
            packTask: task,
            packCreator: creator.taskCreator,
            ownedAssetKeys: [],
            uploadKeyByAssetKey: new Map(),
        };
        for (const ownedAsset of task.ownedAssets) {
            const assetKey = buildAssetKey(creator.taskCreator, ownedAsset.name);
            const existingOwner = ownedPackByAssetKey.get(assetKey);
            if (existingOwner && existingOwner.packKey !== packKey) {
                return {
                    ok: false,
                    task,
                    message: `Pack owned asset "${ownedAsset.name}" is declared by multiple local asset packs in this upload run.`,
                };
            }
            if (!plan.uploadKeyByAssetKey.has(assetKey)) {
                plan.uploadKeyByAssetKey.set(assetKey, ownedAsset.name);
                plan.ownedAssetKeys.push(assetKey);
            }
            ownedPackByAssetKey.set(assetKey, plan);
        }
        packPlansByKey.set(packKey, plan);
    }
    return {
        ok: true,
        packPlansByKey,
        ownedPackByAssetKey,
    };
}
function resolveMutationCreatorUsername(task, creatorUsername, targetCreatorUsername) {
    const mutationCreatorUsername = typeof targetCreatorUsername === 'string' && targetCreatorUsername.trim().length > 0
        ? targetCreatorUsername.trim()
        : creatorUsername.trim();
    if (!mutationCreatorUsername) {
        throw new Error(`Asset pack "${task.name}@${task.version}" upload is missing creator username.`);
    }
    return mutationCreatorUsername;
}
function resolveAssetFormat(task) {
    if (typeof task.format === 'string' && task.format.trim().length > 0) {
        return task.format.trim().toUpperCase();
    }
    if (task.category === 'MODEL_3D') {
        const primaryFile = task.filePaths.primary;
        if (typeof primaryFile === 'string') {
            const normalizedPrimary = primaryFile.trim().toLowerCase();
            if (normalizedPrimary.endsWith('.boxel.json')) {
                return 'PLAYDROP_BOXEL_JSON';
            }
            if (normalizedPrimary.endsWith('.vox')) {
                return 'VOX';
            }
            const primaryExtension = (0, node_path_1.extname)(normalizedPrimary);
            if (primaryExtension === '.glb') {
                return 'GLB';
            }
            if (primaryExtension === '.gltf') {
                return 'GLTF';
            }
        }
        const boxelFile = task.filePaths.boxel;
        if (typeof boxelFile === 'string' && boxelFile.trim().toLowerCase().endsWith('.boxel.json')) {
            return 'PLAYDROP_BOXEL_JSON';
        }
        const meshFile = task.filePaths.mesh;
        if (typeof meshFile === 'string') {
            const meshExtension = (0, node_path_1.extname)(meshFile).toLowerCase();
            if (meshExtension === '.glb') {
                return 'GLB';
            }
            if (meshExtension === '.gltf') {
                return 'GLTF';
            }
        }
        throw new Error(`Asset "${task.name}" is missing files.primary or uses an unsupported MODEL_3D primary extension.`);
    }
    const filePaths = Object.values(task.filePaths);
    if (filePaths.length === 0) {
        throw new Error(`Asset "${task.name}" has no files.`);
    }
    if (filePaths.length > 1) {
        throw new Error(`Asset "${task.name}" is missing format and has multiple files. Set "format" explicitly in catalogue.json.`);
    }
    const extension = (0, node_path_1.extname)(filePaths[0]).toLowerCase();
    const inferred = EXTENSION_TO_FORMAT[extension];
    if (!inferred) {
        throw new Error(`Asset "${task.name}" uses unsupported file extension "${extension}". Set "format" explicitly.`);
    }
    return inferred;
}
function validateAssetSubcategoryCompatibility(task, format) {
    const category = typeof task.category === 'string' ? task.category.trim().toUpperCase() : '';
    const subcategory = typeof task.subcategory === 'string' ? task.subcategory.trim().toLowerCase() : '';
    const definition = types_1.ASSET_SUBCATEGORY_DEFINITIONS_BY_KEY[`${category}:${subcategory}`];
    if (!definition) {
        throw new Error(`Asset "${task.name}" uses unsupported subcategory "${task.subcategory}" for category "${task.category}".`);
    }
    if (!definition.formats.includes(format)) {
        throw new Error(`Asset "${task.name}" uses format "${format}" not allowed for subcategory "${subcategory}".`);
    }
    (0, model_artifacts_1.assertGeneratedModel3DAssetRoles)(task, format);
    return subcategory;
}
async function prepareAssetTaskUploadData(task) {
    if ('assetSpec' in task && typeof task.assetSpec === 'string' && task.assetSpec.trim().length > 0) {
        const uploadFiles = Object.entries(task.filePaths)
            .sort(([leftRole], [rightRole]) => leftRole.localeCompare(rightRole))
            .map(([role, filePath]) => {
            const roleContract = task.assetSpecContract?.roles.find((candidate) => candidate.name.trim().toLowerCase() === role.trim().toLowerCase());
            return prepareUploadFile(filePath, role, (0, assetSpecs_1.resolveCustomAssetRoleContentType)(filePath, roleContract));
        });
        return {
            assetSpec: task.assetSpec.trim(),
            uploadFiles,
            manifestFiles: uploadFiles.map((entry) => ({
                role: entry.fieldName,
                filename: entry.filename,
                contentType: entry.contentType,
                sizeBytes: entry.sizeBytes,
                sha256: entry.sha256,
            })),
        };
    }
    const format = resolveAssetFormat(task);
    const subcategory = validateAssetSubcategoryCompatibility(task, format);
    await (0, model_artifacts_1.prepareModel3DAssetArtifacts)(task);
    const uploadFiles = Object.entries(task.filePaths)
        .sort(([leftRole], [rightRole]) => leftRole.localeCompare(rightRole))
        .map(([role, filePath]) => prepareUploadFile(filePath, role));
    return {
        format,
        subcategory,
        uploadFiles,
        manifestFiles: uploadFiles.map((entry) => ({
            role: entry.fieldName,
            filename: entry.filename,
            contentType: entry.contentType,
            sizeBytes: entry.sizeBytes,
            sha256: entry.sha256,
        })),
    };
}
async function uploadAssetTask(client, task, sourceAppVersionId, creatorUsername, options) {
    const prepared = await prepareAssetTaskUploadData(task);
    const visibility = task.visibility;
    const files = prepared.uploadFiles.map((entry) => ({
        file: entry.file,
        fieldName: entry.fieldName,
        filename: entry.filename,
    }));
    const targetCreatorUsername = typeof creatorUsername === 'string' ? creatorUsername.trim() : '';
    if (!targetCreatorUsername) {
        throw new Error(`Asset "${task.name}" upload is missing creator username.`);
    }
    const response = await client.createAssetVersion(targetCreatorUsername, task.name, prepared.assetSpec
        ? {
            displayName: task.kind === 'asset' ? task.displayName : undefined,
            assetSpec: prepared.assetSpec,
            remixRef: task.kind === 'asset' ? task.remix : undefined,
            visibility: visibility,
            license: task.license,
            sourceKind: sourceAppVersionId ? 'APP_EMBEDDED' : 'UPLOAD',
            sourceAppVersionId,
            runtimeKey: task.kind === 'owned-asset' ? task.runtimeKey : undefined,
            tags: task.kind === 'asset' ? task.tags : undefined,
            clearTags: task.kind === 'asset' || task.kind === 'owned-asset' ? options?.clearTags : undefined,
            shopListed: task.shopListed,
            shopPriceCredits: task.shopPriceCredits,
            files,
        }
        : {
            displayName: task.kind === 'asset' ? task.displayName : undefined,
            category: task.category,
            subcategory: prepared.subcategory,
            format: prepared.format,
            remixRef: task.kind === 'asset' ? task.remix : undefined,
            visibility: visibility,
            license: task.license,
            sourceKind: sourceAppVersionId ? 'APP_EMBEDDED' : 'UPLOAD',
            sourceAppVersionId,
            runtimeKey: task.kind === 'owned-asset' ? task.runtimeKey : undefined,
            tags: task.kind === 'asset' ? task.tags : undefined,
            clearTags: task.kind === 'asset' || task.kind === 'owned-asset' ? options?.clearTags : undefined,
            shopListed: task.shopListed,
            shopPriceCredits: task.shopPriceCredits,
            files,
        });
    const uploadedCreatorUsername = response.asset.creatorUsername;
    const name = response.asset.name;
    const revision = response.version.revision;
    const versionNodeId = typeof response.versionNodeId === 'string' ? response.versionNodeId.trim() : '';
    if (typeof uploadedCreatorUsername !== 'string' || uploadedCreatorUsername.trim().length === 0) {
        throw new Error(`Asset "${task.name}" upload did not return creatorUsername.`);
    }
    if (!versionNodeId) {
        throw new Error(`Asset "${task.name}" upload did not return versionNodeId.`);
    }
    return {
        creatorUsername: uploadedCreatorUsername,
        name,
        revision,
        ref: `asset:${uploadedCreatorUsername}/${name}@r${revision}`,
        versionNodeId,
    };
}
async function uploadAssetSpecTask(client, task, creatorUsername) {
    const targetCreatorUsername = typeof creatorUsername === 'string' ? creatorUsername.trim() : '';
    if (!targetCreatorUsername) {
        throw new Error(`Asset spec "${task.name}@${task.version}" upload is missing creator username.`);
    }
    const symbol = prepareUploadFile(task.symbolPath, 'symbol', 'image/svg+xml');
    const contract = prepareUploadFile(task.contractPath, 'contract', 'application/json');
    const documentation = task.documentationPath
        ? prepareUploadFile(task.documentationPath, 'documentation', 'text/markdown')
        : null;
    await client.createAssetSpecVersion(targetCreatorUsername, task.name, {
        version: task.version,
        displayName: task.displayName,
        description: task.description,
        visibility: task.visibility,
        validationKind: task.validationKind,
        status: task.status,
        releaseNotes: task.releaseNotes,
        successorVersion: task.successorVersion,
        symbol: symbol.file,
        contract: contract.file,
        ...(documentation ? { documentation: documentation.file } : {}),
    });
    return {
        creatorUsername: targetCreatorUsername,
        name: task.name,
        version: task.version,
    };
}
function parseAssetRef(raw) {
    const trimmed = raw.trim();
    const canonicalMatch = /^asset:([^/]+)\/([^@]+)@r(\d+)$/i.exec(trimmed);
    if (canonicalMatch) {
        return {
            creatorUsername: canonicalMatch[1],
            name: canonicalMatch[2],
            revision: Number.parseInt(canonicalMatch[3], 10),
        };
    }
    const plainMatch = /^([^/]+)\/([^@]+)@r(\d+)$/i.exec(trimmed);
    if (plainMatch) {
        return {
            creatorUsername: plainMatch[1],
            name: plainMatch[2],
            revision: Number.parseInt(plainMatch[3], 10),
        };
    }
    return null;
}
async function resolveAssetReference(client, rawRef, fallbackCreator, uploadedAssets) {
    const trimmed = rawRef.trim();
    if (!trimmed) {
        throw new Error('Asset pack has an empty asset reference.');
    }
    const parsed = parseAssetRef(trimmed);
    if (parsed) {
        return `asset:${parsed.creatorUsername}/${parsed.name}@r${parsed.revision}`;
    }
    if (trimmed.includes('@')) {
        throw new Error(`Invalid asset ref "${trimmed}". Use "asset:creator/name@rN" format.`);
    }
    const slashIndex = trimmed.indexOf('/');
    const creatorUsername = slashIndex > 0 ? trimmed.slice(0, slashIndex) : fallbackCreator;
    const name = slashIndex > 0 ? trimmed.slice(slashIndex + 1) : trimmed;
    if (!creatorUsername || !name) {
        throw new Error(`Invalid asset reference "${trimmed}".`);
    }
    const key = `${creatorUsername}/${name}`;
    const uploaded = uploadedAssets.get(key);
    if (uploaded) {
        return uploaded.ref;
    }
    const detail = await client.fetchAssetBySlug(creatorUsername, name);
    const revision = detail?.asset?.currentVersion?.revision;
    if (typeof revision !== 'number' || !Number.isInteger(revision) || revision <= 0) {
        throw new Error(`Asset "${creatorUsername}/${name}" has no current version to reference from asset pack.`);
    }
    return `asset:${creatorUsername}/${name}@r${revision}`;
}
function shouldAbortPackUploadOnError(error) {
    return error instanceof types_1.ApiError && error.status >= 400 && error.status < 500 && error.status !== 408 && error.status !== 429;
}
function describeError(error) {
    if (error instanceof Error && error.message.trim().length > 0) {
        return error.message;
    }
    return String(error);
}
async function prepareLocalPackAssets(task, mutationCreatorUsername, localAssetTasks, uploadKeyByAssetKey) {
    const preparedLocalAssets = new Map();
    for (const localAssetTask of localAssetTasks) {
        const assetKey = buildAssetKey(mutationCreatorUsername, localAssetTask.name);
        const uploadKey = uploadKeyByAssetKey.get(assetKey);
        if (!uploadKey) {
            throw new Error(`Asset pack "${task.name}@${task.version}" is missing upload key for local asset "${localAssetTask.name}".`);
        }
        preparedLocalAssets.set(uploadKey, {
            task: localAssetTask,
            prepared: await prepareAssetTaskUploadData(localAssetTask),
        });
    }
    return preparedLocalAssets;
}
function buildLocalAssetManifestEntries(preparedLocalAssets) {
    const localAssets = [];
    for (const [uploadKey, localAsset] of preparedLocalAssets) {
        localAssets.push({
            uploadKey,
            name: localAsset.task.name,
            displayName: localAsset.task.displayName,
            description: localAsset.task.description,
            category: localAsset.task.category,
            subcategory: localAsset.prepared.subcategory ?? null,
            format: localAsset.prepared.format,
            visibility: localAsset.task.visibility,
            license: localAsset.task.license,
            tags: localAsset.task.tags,
            shopListed: localAsset.task.shopListed,
            shopPriceCredits: localAsset.task.shopPriceCredits,
            files: localAsset.prepared.manifestFiles,
        });
    }
    return localAssets;
}
async function buildPackMembersAndExistingAssets(client, task, mutationCreatorUsername, uploadedAssets, uploadKeyByAssetKey) {
    const existingAssetsByRef = new Map();
    const members = [];
    const ownedAssetNames = new Set(task.ownedAssets.map((asset) => asset.name));
    for (const rawRef of task.assets) {
        const localCandidate = parseUnversionedAssetTaskRef(rawRef, mutationCreatorUsername);
        if (localCandidate && localCandidate.creatorUsername === mutationCreatorUsername && ownedAssetNames.has(localCandidate.name)) {
            const localAssetKey = buildAssetKey(localCandidate.creatorUsername, localCandidate.name);
            const localUploadKey = uploadKeyByAssetKey.get(localAssetKey);
            if (localUploadKey) {
                members.push({ uploadKey: localUploadKey });
                continue;
            }
        }
        const assetRef = await resolveAssetReference(client, rawRef, mutationCreatorUsername, uploadedAssets);
        if (!existingAssetsByRef.has(assetRef)) {
            existingAssetsByRef.set(assetRef, { assetRef });
        }
        members.push({ assetRef });
    }
    return {
        existingAssets: Array.from(existingAssetsByRef.values()),
        members,
    };
}
function buildPackListingUploads(task) {
    const listingUploads = [];
    if (task.listing?.iconPath) {
        listingUploads.push(prepareListingMediaUpload('icon', task.listing.iconPath, 'image/png'));
    }
    if (task.listing?.heroPortraitPath) {
        listingUploads.push(prepareListingMediaUpload('heroPortrait', task.listing.heroPortraitPath, 'image/png'));
    }
    if (task.listing?.heroLandscapePath) {
        listingUploads.push(prepareListingMediaUpload('heroLandscape', task.listing.heroLandscapePath, 'image/png'));
    }
    task.listing?.screenshotPortraitPaths.forEach((filePath, index) => {
        listingUploads.push(prepareListingMediaUpload(`screenshotsPortrait:${index}`, filePath, 'image/png'));
    });
    task.listing?.screenshotLandscapePaths.forEach((filePath, index) => {
        listingUploads.push(prepareListingMediaUpload(`screenshotsLandscape:${index}`, filePath, 'image/png'));
    });
    task.listing?.videoPortraitPaths.forEach((filePath, index) => {
        listingUploads.push(prepareListingMediaUpload(`videosPortrait:${index}`, filePath, 'video/mp4'));
    });
    task.listing?.videoLandscapePaths.forEach((filePath, index) => {
        listingUploads.push(prepareListingMediaUpload(`videosLandscape:${index}`, filePath, 'video/mp4'));
    });
    return listingUploads;
}
function buildPackListingPayload(listingUploads) {
    if (listingUploads.length === 0) {
        return undefined;
    }
    return {
        icon: listingUploads.find((entry) => entry.mediaKey === 'icon')?.entry,
        heroPortrait: listingUploads.find((entry) => entry.mediaKey === 'heroPortrait')?.entry,
        heroLandscape: listingUploads.find((entry) => entry.mediaKey === 'heroLandscape')?.entry,
        screenshotsPortrait: listingUploads.filter((entry) => entry.mediaKey.startsWith('screenshotsPortrait:')).map((entry) => entry.entry),
        screenshotsLandscape: listingUploads.filter((entry) => entry.mediaKey.startsWith('screenshotsLandscape:')).map((entry) => entry.entry),
        videosPortrait: listingUploads.filter((entry) => entry.mediaKey.startsWith('videosPortrait:')).map((entry) => entry.entry),
        videosLandscape: listingUploads.filter((entry) => entry.mediaKey.startsWith('videosLandscape:')).map((entry) => entry.entry),
    };
}
function buildPackInitializeRequest(task, ownedAssets, existingAssets, members, listingUploads, options) {
    return {
        version: task.version,
        displayName: task.displayName,
        remixRef: task.remix,
        visibility: task.visibility,
        license: task.license,
        tags: task.tags,
        clearTags: options?.clearTags,
        hostingMode: task.hostingMode,
        externalUrl: task.externalUrl,
        downloadUrl: task.downloadUrl,
        releaseNotes: task.releaseNotes,
        ownedAssets,
        existingAssets,
        members,
        listing: buildPackListingPayload(listingUploads),
    };
}
async function uploadPendingPackSessionEntries(client, task, mutationCreatorUsername, sessionId, preparedLocalAssets, uploadedAssetKeys, listingUploads, uploadedMediaKeys) {
    for (const [uploadKey, localAsset] of preparedLocalAssets) {
        if (uploadedAssetKeys.has(uploadKey)) {
            continue;
        }
        await client.uploadAssetPackSessionAsset(mutationCreatorUsername, task.name, sessionId, uploadKey, {
            files: localAsset.prepared.uploadFiles.map((entry) => ({
                file: entry.file,
                fieldName: entry.fieldName,
                filename: entry.filename,
            })),
        });
    }
    for (const media of listingUploads) {
        if (uploadedMediaKeys.has(media.mediaKey)) {
            continue;
        }
        await client.uploadAssetPackSessionMedia(mutationCreatorUsername, task.name, sessionId, media.mediaKey, {
            file: media.file,
            filename: media.filename,
        });
    }
}
function buildUploadedPackInfo(creatorUsername, task, versionNodeId, versionAssets, uploadedLocalAssets) {
    return {
        creatorUsername,
        name: task.name,
        version: task.version,
        versionNodeId,
        ref: `pack:${creatorUsername}/${task.name}@${task.version}`,
        assetRefs: Array.isArray(versionAssets)
            ? versionAssets.map((asset) => `asset:${asset.creatorUsername}/${asset.name}@r${asset.revision}`)
            : [],
        uploadedLocalAssets: uploadedLocalAssets ?? [],
    };
}
async function uploadAssetPackTask(client, task, creatorUsername, uploadedAssets, localAssetTasks, uploadKeyByAssetKey, targetCreatorUsername, options) {
    const mutationCreatorUsername = resolveMutationCreatorUsername(task, creatorUsername, targetCreatorUsername);
    const preparedLocalAssets = await prepareLocalPackAssets(task, mutationCreatorUsername, localAssetTasks, uploadKeyByAssetKey);
    const ownedAssets = buildLocalAssetManifestEntries(preparedLocalAssets);
    const { existingAssets, members } = await buildPackMembersAndExistingAssets(client, task, mutationCreatorUsername, uploadedAssets, uploadKeyByAssetKey);
    const listingUploads = buildPackListingUploads(task);
    const initializeBody = buildPackInitializeRequest(task, ownedAssets, existingAssets, members, listingUploads, options);
    const initializeResponse = await client.initializeAssetPackUpload(mutationCreatorUsername, task.name, initializeBody);
    if (initializeResponse.status === 'finalized') {
        if (!initializeResponse.versionNodeId || !initializeResponse.version) {
            throw new Error(`Asset pack "${task.name}" finalize response is missing version details.`);
        }
        return buildUploadedPackInfo(mutationCreatorUsername, task, initializeResponse.versionNodeId, initializeResponse.version.assets, initializeResponse.uploadedLocalAssets);
    }
    const sessionId = initializeResponse.session.id;
    try {
        await uploadPendingPackSessionEntries(client, task, mutationCreatorUsername, sessionId, preparedLocalAssets, new Set(initializeResponse.session.uploadedAssetKeys), listingUploads, new Set(initializeResponse.session.uploadedMediaKeys));
        const finalizedResponse = await client.finalizeAssetPackUpload(mutationCreatorUsername, task.name, sessionId);
        const versionNodeId = typeof finalizedResponse.versionNodeId === 'string' ? finalizedResponse.versionNodeId.trim() : '';
        if (!versionNodeId) {
            throw new Error(`Asset pack "${task.name}@${task.version}" upload did not return versionNodeId.`);
        }
        if (!finalizedResponse.version) {
            throw new Error(`Asset pack "${task.name}@${task.version}" upload did not return version details.`);
        }
        return buildUploadedPackInfo(mutationCreatorUsername, task, versionNodeId, finalizedResponse.version.assets, finalizedResponse.uploadedLocalAssets);
    }
    catch (error) {
        if (shouldAbortPackUploadOnError(error)) {
            try {
                await client.abortAssetPackUpload(mutationCreatorUsername, task.name, sessionId);
            }
            catch (abortError) {
                throw new Error(`Asset pack "${task.name}@${task.version}" failed and aborting session "${sessionId}" also failed: ${describeError(abortError)}. Original error: ${describeError(error)}`);
            }
        }
        throw error;
    }
}
