"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.upload = upload;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const semver_1 = require("semver");
const types_1 = require("@playdrop/types");
const apps_1 = require("../apps");
const commandContext_1 = require("../commandContext");
const http_1 = require("../http");
const catalogue_1 = require("../catalogue");
const messages_1 = require("../messages");
const taskSelection_1 = require("../taskSelection");
const taskUtils_1 = require("../taskUtils");
const uploadLog_1 = require("../uploadLog");
const upload_content_1 = require("./upload-content");
const appUrls_1 = require("../appUrls");
const upload_graph_1 = require("./upload-graph");
function normalizeSavedItemKinds(kinds) {
    return [...new Set(Array.isArray(kinds) ? kinds : [])].sort();
}
function sameSavedItemKinds(left, right) {
    const normalizedLeft = normalizeSavedItemKinds(left);
    const normalizedRight = normalizeSavedItemKinds(right);
    if (normalizedLeft.length !== normalizedRight.length) {
        return false;
    }
    return normalizedLeft.every((value, index) => value === normalizedRight[index]);
}
function resolveCatalogueWorkspaceRoot(startPath) {
    const absoluteStart = (0, node_path_1.resolve)(startPath);
    const currentStats = (0, node_fs_1.existsSync)(absoluteStart) ? (0, node_fs_1.statSync)(absoluteStart) : null;
    let currentDir = currentStats?.isFile() ? (0, node_path_1.dirname)(absoluteStart) : absoluteStart;
    while (true) {
        const parentDir = (0, node_path_1.dirname)(currentDir);
        if (parentDir === currentDir) {
            return currentDir;
        }
        const parentCataloguePath = (0, node_path_1.join)(parentDir, 'catalogue.json');
        if (!(0, node_fs_1.existsSync)(parentCataloguePath)) {
            return currentDir;
        }
        currentDir = parentDir;
    }
}
function resolveUploadWorkspaceRoot(pathOrName, resolution) {
    if (resolution === 'name') {
        return resolveCatalogueWorkspaceRoot(process.cwd());
    }
    return resolveCatalogueWorkspaceRoot(pathOrName);
}
function buildTagGroupLogEntry(status, entityId, detail) {
    return {
        action: 'upload',
        status,
        entityType: 'tag-group',
        entityId,
        catalogue: 'catalogue:tag-groups',
        detail,
    };
}
function buildTagLogEntry(status, entityId, detail) {
    return {
        action: 'upload',
        status,
        entityType: 'tag',
        entityId,
        catalogue: 'catalogue:tag-groups',
        detail,
    };
}
async function syncTagGroupsFromCatalogue(client, groups) {
    const entries = [];
    if (groups.length === 0) {
        return entries;
    }
    const live = await client.fetchAdminTagGroups();
    const liveGroupsBySlug = new Map(live.groups.map((group) => [group.slug, group]));
    for (const groupDefinition of groups) {
        let liveGroup = liveGroupsBySlug.get(groupDefinition.slug) ?? null;
        if (!liveGroup) {
            const created = await client.createAdminTagGroup({
                slug: groupDefinition.slug,
                displayName: groupDefinition.displayName,
                ...(groupDefinition.allowedTargetKinds ? { allowedTargetKinds: groupDefinition.allowedTargetKinds } : {}),
            });
            liveGroup = {
                ...created.group,
                tags: [],
            };
            liveGroupsBySlug.set(liveGroup.slug, liveGroup);
            entries.push(buildTagGroupLogEntry('success', liveGroup.slug, `created "${liveGroup.displayName}"`));
        }
        else if (liveGroup.displayName !== groupDefinition.displayName
            || !sameSavedItemKinds(liveGroup.allowedTargetKinds, groupDefinition.allowedTargetKinds)) {
            const updated = await client.updateAdminTagGroup(groupDefinition.slug, {
                displayName: groupDefinition.displayName,
                allowedTargetKinds: normalizeSavedItemKinds(groupDefinition.allowedTargetKinds),
            });
            liveGroup = {
                ...updated.group,
                tags: liveGroup.tags,
            };
            liveGroupsBySlug.set(liveGroup.slug, liveGroup);
            entries.push(buildTagGroupLogEntry('success', liveGroup.slug, `updated "${liveGroup.displayName}"`));
        }
        if (!liveGroup) {
            throw new Error(`tag_group_sync_failed:${groupDefinition.slug}`);
        }
        const liveTagsBySlug = new Map(liveGroup.tags.map((tag) => [tag.slug, tag]));
        for (const tagDefinition of groupDefinition.tags) {
            const liveTag = liveTagsBySlug.get(tagDefinition.slug) ?? null;
            const tagRef = `${groupDefinition.slug}/${tagDefinition.slug}`;
            if (!liveTag) {
                const created = await client.createAdminTag(groupDefinition.slug, {
                    slug: tagDefinition.slug,
                    displayName: tagDefinition.displayName,
                });
                liveTagsBySlug.set(created.tag.slug, created.tag);
                entries.push(buildTagLogEntry('success', tagRef, `created "${created.tag.displayName}"`));
                continue;
            }
            if (liveTag.displayName !== tagDefinition.displayName) {
                const updated = await client.updateAdminTag(groupDefinition.slug, tagDefinition.slug, {
                    displayName: tagDefinition.displayName,
                });
                liveTagsBySlug.set(updated.tag.slug, updated.tag);
                entries.push(buildTagLogEntry('success', tagRef, `updated "${updated.tag.displayName}"`));
            }
        }
    }
    return entries;
}
function buildApiUnavailableMessage(apiBase) {
    if (apiBase && typeof apiBase === 'string') {
        return `Could not reach the Playdrop API at ${apiBase}. Check your internet connection, then ensure the server is running before retrying.`;
    }
    return 'Could not reach the Playdrop API. Check your internet connection, then ensure the server is running before retrying.';
}
function isConnectionRefusedError(error) {
    const networkCode = error?.cause?.code;
    if (error instanceof TypeError || networkCode === 'ECONNREFUSED') {
        return true;
    }
    const aggregateErrors = error?.cause?.errors;
    return Array.isArray(aggregateErrors) && aggregateErrors.some((entry) => entry?.code === 'ECONNREFUSED');
}
async function fetchCurrentUserInfo(client, apiBase) {
    try {
        const data = await client.me();
        return {
            username: data?.user?.username ?? null,
            role: data?.user?.role ?? null,
            user: data?.user ?? null,
        };
    }
    catch (error) {
        if (error instanceof types_1.UnsupportedClientError) {
            (0, http_1.handleUnsupportedError)(error, 'Upload');
            return { username: null, role: null, user: null };
        }
        if (error instanceof http_1.CLIUnsupportedClientError) {
            throw error;
        }
        if (error instanceof types_1.ApiError) {
            return { username: null, role: null, user: null };
        }
        if (isConnectionRefusedError(error)) {
            throw new Error(buildApiUnavailableMessage(apiBase));
        }
        throw error;
    }
}
function normalizePortalBase(webBase) {
    return (0, appUrls_1.normalizePlaydropWebBase)(webBase);
}
function normalizeCreatorUsername(username) {
    if (!username)
        return null;
    const trimmed = username.trim();
    if (!trimmed || trimmed.toLowerCase() === 'unknown') {
        return null;
    }
    return trimmed;
}
function isNotFoundApiError(error) {
    return error instanceof types_1.ApiError && error.status === 404;
}
function createRemoteDependencyCache() {
    return {
        assetSpecVersionExists: new Map(),
        assetSpecSupportExists: new Map(),
        assetVersionExists: new Map(),
        packVersionExists: new Map(),
    };
}
function createTaskExecutionId(task, creator, index) {
    if (task.kind === 'asset-spec') {
        return `${index}:${task.kind}:${creator}/${task.name}@${task.version}`;
    }
    if (task.kind === 'asset-pack') {
        return `${index}:${task.kind}:${creator}/${task.name}@${task.version}`;
    }
    if (task.kind === 'owned-asset') {
        return `${index}:${task.kind}:${creator}/${task.appName}:${task.name}`;
    }
    return `${index}:${task.kind}:${creator}/${task.name}`;
}
function buildTaskDetailEntry(task, entityId, status, detail) {
    return {
        action: 'upload',
        status,
        entityType: task.kind,
        entityId,
        catalogue: task.cataloguePath,
        detail,
    };
}
function buildTaskDirectErrorEntry(task, entityId, detail) {
    return buildTaskDetailEntry(task, entityId, 'error', detail.startsWith('upload failed:') ? detail : `upload failed: ${detail}`);
}
function buildTaskBlockedEntry(task, entityId, detail) {
    return buildTaskDetailEntry(task, entityId, 'blocked', detail);
}
function buildAssetSpecFamilyKey(creatorUsername, name) {
    return `${creatorUsername}/${name}`;
}
function buildAssetSpecVersionKey(creatorUsername, name, version) {
    return `${creatorUsername}/${name}@${version}`;
}
function pushIndexedNode(map, key, node) {
    const existing = map.get(key);
    if (existing) {
        existing.push(node);
        return;
    }
    map.set(key, [node]);
}
function pickSingleLocalDependency(candidates, ref) {
    if (!candidates || candidates.length === 0) {
        return { node: null, blockedDetail: null };
    }
    if (candidates.length > 1) {
        return {
            node: null,
            blockedDetail: `local_dependency_ambiguous:${ref}`,
        };
    }
    return { node: candidates[0], blockedDetail: null };
}
function pickCompatibleLocalAssetSpecTask(candidates, versionRange) {
    if (!candidates || candidates.length === 0) {
        return null;
    }
    const matching = candidates.filter((candidate) => (candidate.task.kind === 'asset-spec' && (0, semver_1.satisfies)(candidate.task.version, versionRange)));
    if (matching.length === 0) {
        return null;
    }
    matching.sort((left, right) => (0, semver_1.compare)(right.task.version, left.task.version));
    return matching[0];
}
async function validateRemoteAssetSpecVersionExists(client, rawRef, cache) {
    if (cache.assetSpecVersionExists.has(rawRef)) {
        return cache.assetSpecVersionExists.get(rawRef);
    }
    const parsed = (0, types_1.parseAssetSpecVersionRef)(rawRef);
    if (!parsed) {
        cache.assetSpecVersionExists.set(rawRef, false);
        return false;
    }
    try {
        await client.fetchAssetSpecVersion(parsed.creatorUsername, parsed.name, parsed.version);
        cache.assetSpecVersionExists.set(rawRef, true);
        return true;
    }
    catch (error) {
        if (isNotFoundApiError(error)) {
            cache.assetSpecVersionExists.set(rawRef, false);
            return false;
        }
        throw error;
    }
}
async function validateRemoteAssetSpecSupportExists(client, familyRef, versionRange, cache) {
    const cacheKey = `${familyRef}@@${versionRange}`;
    if (cache.assetSpecSupportExists.has(cacheKey)) {
        return cache.assetSpecSupportExists.get(cacheKey);
    }
    const parsed = (0, types_1.parseAssetSpecFamilyRef)(familyRef);
    if (!parsed) {
        cache.assetSpecSupportExists.set(cacheKey, false);
        return false;
    }
    try {
        const response = await client.listAssetSpecVersions(parsed.creatorUsername, parsed.name, { limit: 200, offset: 0 });
        const exists = response.versions.some((version) => (0, semver_1.satisfies)(version.version, versionRange));
        cache.assetSpecSupportExists.set(cacheKey, exists);
        return exists;
    }
    catch (error) {
        if (isNotFoundApiError(error)) {
            cache.assetSpecSupportExists.set(cacheKey, false);
            return false;
        }
        throw error;
    }
}
async function validateRemoteAssetVersionExists(client, rawRef, cache) {
    if (cache.assetVersionExists.has(rawRef)) {
        return cache.assetVersionExists.get(rawRef);
    }
    const parsed = (0, types_1.parseContentVersionRef)(rawRef);
    if (!parsed || parsed.kind !== 'asset') {
        cache.assetVersionExists.set(rawRef, false);
        return false;
    }
    try {
        const detail = await client.fetchAssetBySlug(parsed.creatorUsername, parsed.name);
        if (detail.asset.currentVersion?.revision === parsed.revision) {
            cache.assetVersionExists.set(rawRef, true);
            return true;
        }
        const versions = await client.listAssetVersions(parsed.creatorUsername, parsed.name, { limit: 200, offset: 0 });
        const exists = versions.versions.some((version) => version.revision === parsed.revision);
        cache.assetVersionExists.set(rawRef, exists);
        return exists;
    }
    catch (error) {
        if (isNotFoundApiError(error)) {
            cache.assetVersionExists.set(rawRef, false);
            return false;
        }
        throw error;
    }
}
async function validateRemotePackVersionExists(client, rawRef, cache) {
    if (cache.packVersionExists.has(rawRef)) {
        return cache.packVersionExists.get(rawRef);
    }
    const parsed = (0, types_1.parseContentVersionRef)(rawRef);
    if (!parsed || parsed.kind !== 'pack') {
        cache.packVersionExists.set(rawRef, false);
        return false;
    }
    try {
        const detail = await client.fetchAssetPackBySlug(parsed.creatorUsername, parsed.name);
        if (detail.pack.currentVersion?.version === parsed.version) {
            cache.packVersionExists.set(rawRef, true);
            return true;
        }
        const versions = await client.listAssetPackVersions(parsed.creatorUsername, parsed.name, { limit: 200, offset: 0 });
        const exists = versions.versions.some((version) => version.version === parsed.version);
        cache.packVersionExists.set(rawRef, exists);
        return exists;
    }
    catch (error) {
        if (isNotFoundApiError(error)) {
            cache.packVersionExists.set(rawRef, false);
            return false;
        }
        throw error;
    }
}
function addLocalDependency(node, dependencyTaskId, ref) {
    if (node.localDependencies.some((dependency) => dependency.taskId === dependencyTaskId)) {
        return;
    }
    node.localDependencies.push({ taskId: dependencyTaskId, ref });
}
function normalizePackDependencyRef(rawRef) {
    const parsed = (0, types_1.parseContentVersionRef)(rawRef);
    if (!parsed || parsed.kind !== 'pack') {
        return null;
    }
    return (0, types_1.formatContentVersionRef)(parsed);
}
function buildTagRegistryEntry(allowedTargetKinds) {
    return {
        allowedTargetKinds: normalizeSavedItemKinds(allowedTargetKinds),
        tags: new Set(),
    };
}
function mergeTagRegistryEntry(registry, groupSlug, entry, options) {
    const existing = registry.get(groupSlug);
    if (!existing) {
        registry.set(groupSlug, {
            allowedTargetKinds: [...entry.allowedTargetKinds],
            tags: new Set(entry.tags),
        });
        return;
    }
    if (options.replaceAllowedTargetKinds) {
        existing.allowedTargetKinds = [...entry.allowedTargetKinds];
    }
    entry.tags.forEach((tagSlug) => existing.tags.add(tagSlug));
}
async function buildRemoteTagRegistry(client) {
    const registry = new Map();
    let directoryOffset = 0;
    while (true) {
        const directory = await client.fetchTagDirectory({
            limit: 200,
            ...(directoryOffset > 0 ? { offset: directoryOffset } : {}),
            tagLimit: 1,
        });
        for (const summary of directory.groups) {
            const groupSlug = summary.group.slug;
            const tagCount = Math.max(summary.tagCount, summary.tags.length);
            const entry = buildTagRegistryEntry(summary.group.allowedTargetKinds);
            let offset = 0;
            while (offset < tagCount || (tagCount === 0 && offset === 0)) {
                const response = await client.fetchTagGroup(groupSlug, {
                    sort: 'alpha',
                    limit: 200,
                    ...(offset > 0 ? { offset } : {}),
                });
                response.tags.forEach((tag) => entry.tags.add(tag.slug));
                if (response.tags.length === 0 || response.tags.length < 200) {
                    break;
                }
                offset += response.tags.length;
            }
            mergeTagRegistryEntry(registry, groupSlug, entry, { replaceAllowedTargetKinds: true });
        }
        if (!directory.pagination?.hasMore) {
            break;
        }
        directoryOffset += directory.groups.length;
    }
    return registry;
}
function buildLocalTagRegistry(groups) {
    const registry = new Map();
    for (const group of groups) {
        const entry = buildTagRegistryEntry(group.allowedTargetKinds);
        group.tags.forEach((tag) => entry.tags.add(tag.slug));
        mergeTagRegistryEntry(registry, group.slug, entry, { replaceAllowedTargetKinds: true });
    }
    return registry;
}
async function buildUploadTagRegistry(client, localGroups) {
    const registry = await buildRemoteTagRegistry(client);
    const localRegistry = buildLocalTagRegistry(localGroups);
    for (const [groupSlug, entry] of localRegistry.entries()) {
        mergeTagRegistryEntry(registry, groupSlug, entry, { replaceAllowedTargetKinds: true });
    }
    return registry;
}
function validateTagRefsForTarget(tagRegistry, target) {
    for (const rawTagRef of target.tags) {
        let normalizedRef = '';
        try {
            normalizedRef = (0, types_1.normalizeTagRef)(rawTagRef);
        }
        catch {
            return `invalid_tag_ref:${String(rawTagRef ?? '').trim() || String(rawTagRef ?? '')}`;
        }
        const [groupSlug, tagSlug] = normalizedRef.split('/');
        if (!groupSlug || !tagSlug) {
            return `invalid_tag_ref:${normalizedRef}`;
        }
        const registryEntry = tagRegistry.get(groupSlug);
        if (!registryEntry || !registryEntry.tags.has(tagSlug)) {
            return `unknown_tag_ref:${normalizedRef}`;
        }
        if (registryEntry.allowedTargetKinds.length > 0
            && !registryEntry.allowedTargetKinds.includes(target.targetKind)) {
            return `tag_not_allowed_for_target_kind:${normalizedRef}:${target.targetKind}`;
        }
    }
    return null;
}
function collectTaskTagValidationTargets(task) {
    if (task.kind === 'app') {
        return [
            { tags: task.tags, targetKind: 'APP' },
            ...task.ownedAssets.map((ownedAsset) => ({
                tags: ownedAsset.tags,
                targetKind: 'ASSET',
            })),
        ];
    }
    if (task.kind === 'asset') {
        return [{ tags: task.tags, targetKind: 'ASSET' }];
    }
    if (task.kind === 'owned-asset') {
        return [{ tags: task.tags, targetKind: 'ASSET' }];
    }
    if (task.kind === 'asset-pack') {
        return [
            { tags: task.tags, targetKind: 'PACK' },
            ...task.ownedAssets.map((ownedAsset) => ({
                tags: ownedAsset.tags,
                targetKind: 'ASSET',
            })),
        ];
    }
    return [];
}
function tasksRequireTagValidation(tasks) {
    return tasks.some((task) => collectTaskTagValidationTargets(task).some((target) => target.tags.length > 0));
}
async function buildUploadPreflight(client, tasks, defaultCreator, currentUserRole, localTagGroups) {
    const nodes = (0, taskUtils_1.sortTasks)(tasks).map((task, index) => {
        const creatorResult = (0, upload_content_1.getTaskCreatorResult)(task, defaultCreator, currentUserRole);
        const entityId = buildTaskEntityId(task, creatorResult.taskCreator);
        const node = {
            id: createTaskExecutionId(task, creatorResult.taskCreator, index),
            task,
            creator: creatorResult.taskCreator,
            entityId,
            localDependencies: [],
            localAppAssetKeysByRef: new Map(),
            localAppPackRefsByRef: new Map(),
        };
        return { node, creatorResult };
    });
    const initialEntries = [];
    for (const { node, creatorResult } of nodes) {
        if (creatorResult.creatorTargetError) {
            initialEntries.push({
                taskId: node.id,
                entry: buildTaskDirectErrorEntry(node.task, node.entityId, creatorResult.creatorTargetError),
            });
        }
    }
    const assetSpecVersionTasksByKey = new Map();
    const assetSpecFamilyTasksByKey = new Map();
    const assetTasksByKey = new Map();
    const packTasksByKey = new Map();
    for (const { node } of nodes) {
        if (node.task.kind === 'asset-spec') {
            pushIndexedNode(assetSpecVersionTasksByKey, buildAssetSpecVersionKey(node.creator, node.task.name, node.task.version), node);
            pushIndexedNode(assetSpecFamilyTasksByKey, buildAssetSpecFamilyKey(node.creator, node.task.name), node);
            continue;
        }
        if (node.task.kind === 'asset') {
            pushIndexedNode(assetTasksByKey, (0, upload_content_1.buildAssetKey)(node.creator, node.task.name), node);
            continue;
        }
        if (node.task.kind === 'asset-pack') {
            pushIndexedNode(packTasksByKey, (0, upload_content_1.buildPackKey)(node.creator, node.task.name, node.task.version), node);
        }
    }
    const remoteCache = createRemoteDependencyCache();
    const tagRegistry = tasksRequireTagValidation(tasks)
        ? await buildUploadTagRegistry(client, localTagGroups)
        : null;
    const initialStatusByTaskId = new Map(initialEntries.map((entry) => [entry.taskId, entry.entry.status]));
    for (const { node } of nodes) {
        if (initialStatusByTaskId.has(node.id)) {
            continue;
        }
        let blockedDetail = null;
        if (!blockedDetail && tagRegistry) {
            for (const target of collectTaskTagValidationTargets(node.task)) {
                blockedDetail = validateTagRefsForTarget(tagRegistry, target);
                if (blockedDetail) {
                    break;
                }
            }
        }
        if (!blockedDetail && node.task.kind === 'asset' && node.task.assetSpec) {
            const parsedAssetSpec = (0, types_1.parseAssetSpecVersionRef)(node.task.assetSpec);
            if (!parsedAssetSpec) {
                blockedDetail = `dependency_invalid:${node.task.assetSpec}`;
            }
            else {
                const assetSpecKey = buildAssetSpecVersionKey(parsedAssetSpec.creatorUsername, parsedAssetSpec.name, parsedAssetSpec.version);
                const localDependency = pickSingleLocalDependency(assetSpecVersionTasksByKey.get(assetSpecKey), (0, types_1.formatAssetSpecVersionRef)(parsedAssetSpec));
                if (localDependency.blockedDetail) {
                    blockedDetail = localDependency.blockedDetail;
                }
                else if (localDependency.node) {
                    addLocalDependency(node, localDependency.node.id, (0, types_1.formatAssetSpecVersionRef)(parsedAssetSpec));
                }
                else if (!(await validateRemoteAssetSpecVersionExists(client, (0, types_1.formatAssetSpecVersionRef)(parsedAssetSpec), remoteCache))) {
                    blockedDetail = `dependency_missing:${(0, types_1.formatAssetSpecVersionRef)(parsedAssetSpec)}`;
                }
            }
        }
        if (!blockedDetail && node.task.kind === 'app') {
            for (const support of node.task.assetSpecSupport) {
                const parsedFamily = (0, types_1.parseAssetSpecFamilyRef)(support.assetSpec);
                if (!parsedFamily) {
                    blockedDetail = `dependency_invalid:${support.assetSpec}`;
                    break;
                }
                const familyKey = buildAssetSpecFamilyKey(parsedFamily.creatorUsername, parsedFamily.name);
                const localDependency = pickCompatibleLocalAssetSpecTask(assetSpecFamilyTasksByKey.get(familyKey), support.versionRange);
                if (localDependency) {
                    addLocalDependency(node, localDependency.id, `${support.assetSpec}@${support.versionRange}`);
                    continue;
                }
                const remoteExists = await validateRemoteAssetSpecSupportExists(client, support.assetSpec, support.versionRange, remoteCache);
                if (!remoteExists) {
                    blockedDetail = `dependency_missing:${support.assetSpec}@${support.versionRange}`;
                    break;
                }
            }
        }
        if (!blockedDetail && node.task.kind === 'app') {
            for (const assetDependency of node.task.uses.assets) {
                const parsedAsset = (0, types_1.parseContentVersionRef)(assetDependency.ref);
                if (!parsedAsset || parsedAsset.kind !== 'asset') {
                    blockedDetail = `dependency_invalid:${assetDependency.ref}`;
                    break;
                }
                const assetRef = (0, types_1.formatContentVersionRef)(parsedAsset);
                const assetKey = (0, upload_content_1.buildAssetKey)(parsedAsset.creatorUsername, parsedAsset.name);
                const localDependency = pickSingleLocalDependency(assetTasksByKey.get(assetKey), assetRef);
                if (localDependency.blockedDetail) {
                    blockedDetail = localDependency.blockedDetail;
                    break;
                }
                if (localDependency.node) {
                    addLocalDependency(node, localDependency.node.id, assetRef);
                    node.localAppAssetKeysByRef.set(assetRef, (0, upload_content_1.buildAssetKey)(localDependency.node.creator, localDependency.node.task.name));
                    continue;
                }
                if (!(await validateRemoteAssetVersionExists(client, assetRef, remoteCache))) {
                    blockedDetail = `dependency_missing:${assetRef}`;
                    break;
                }
            }
        }
        if (!blockedDetail && node.task.kind === 'app') {
            for (const rawPackRef of node.task.uses.packs) {
                const normalizedPackRef = normalizePackDependencyRef(rawPackRef);
                if (!normalizedPackRef) {
                    blockedDetail = `dependency_invalid:${rawPackRef.trim() || rawPackRef}`;
                    break;
                }
                const parsedPack = (0, types_1.parseContentVersionRef)(normalizedPackRef);
                if (!parsedPack || parsedPack.kind !== 'pack') {
                    blockedDetail = `dependency_invalid:${normalizedPackRef}`;
                    break;
                }
                const localDependency = pickSingleLocalDependency(packTasksByKey.get((0, upload_content_1.buildPackKey)(parsedPack.creatorUsername, parsedPack.name, parsedPack.version)), normalizedPackRef);
                if (localDependency.blockedDetail) {
                    blockedDetail = localDependency.blockedDetail;
                    break;
                }
                if (localDependency.node) {
                    addLocalDependency(node, localDependency.node.id, normalizedPackRef);
                    if (localDependency.node.task.kind === 'asset-pack') {
                        node.localAppPackRefsByRef.set(normalizedPackRef, `pack:${localDependency.node.creator}/${localDependency.node.task.name}@${localDependency.node.task.version}`);
                    }
                    continue;
                }
                if (!(await validateRemotePackVersionExists(client, normalizedPackRef, remoteCache))) {
                    blockedDetail = `dependency_missing:${normalizedPackRef}`;
                    break;
                }
            }
        }
        if (blockedDetail) {
            initialEntries.push({
                taskId: node.id,
                entry: buildTaskBlockedEntry(node.task, node.entityId, blockedDetail),
            });
        }
    }
    return {
        nodes: nodes.map((entry) => entry.node),
        initialEntries,
    };
}
function resolveAppTaskLocalDependencies(task, node, state) {
    if (node.localAppAssetKeysByRef.size === 0 && node.localAppPackRefsByRef.size === 0) {
        return task;
    }
    const usesAssets = task.uses.assets.map((dependency) => {
        const assetKey = node.localAppAssetKeysByRef.get(dependency.ref);
        if (!assetKey) {
            return dependency;
        }
        const uploaded = state.uploadedAssetsByKey.get(assetKey);
        if (!uploaded) {
            throw new Error(`Local asset dependency "${dependency.ref}" did not produce an uploaded asset ref.`);
        }
        return {
            ...dependency,
            ref: uploaded.ref,
        };
    });
    const usesPacks = task.uses.packs.map((rawRef) => {
        const normalized = normalizePackDependencyRef(rawRef);
        if (!normalized) {
            return rawRef;
        }
        return node.localAppPackRefsByRef.get(normalized) ?? normalized;
    });
    return {
        ...task,
        uses: {
            assets: usesAssets,
            packs: usesPacks,
        },
    };
}
function buildAppOverviewUrl(base, creator, task) {
    const typeSlug = (0, appUrls_1.getAppTypeSlug)(task.type ?? 'GAME');
    const encodedCreator = encodeURIComponent(creator);
    const encodedName = encodeURIComponent(task.name);
    return `${base}/creators/${encodedCreator}/apps/${typeSlug}/${encodedName}/overview`;
}
function buildAssetOverviewUrl(base, creator, name) {
    const encodedCreator = encodeURIComponent(creator);
    const encodedName = encodeURIComponent(name);
    return `${base}/creators/${encodedCreator}/assets/${encodedName}/overview`;
}
function buildAssetPackOverviewUrl(base, creator, name) {
    const encodedCreator = encodeURIComponent(creator);
    const encodedName = encodeURIComponent(name);
    return `${base}/creators/${encodedCreator}/asset-packs/${encodedName}/overview`;
}
function appendOverviewLink(entry, task, portalBase, creator) {
    if (!portalBase || !creator || entry.status === 'error') {
        return;
    }
    let url = null;
    if (task.kind === 'app') {
        url = buildAppOverviewUrl(portalBase, creator, task);
    }
    else if (task.kind === 'asset-spec') {
        url = null;
    }
    else if (task.kind === 'asset' || task.kind === 'owned-asset') {
        url = buildAssetOverviewUrl(portalBase, creator, task.name);
    }
    else if (task.kind === 'asset-pack') {
        url = buildAssetPackOverviewUrl(portalBase, creator, task.name);
    }
    if (!url) {
        return;
    }
    entry.detail = entry.detail ? `${entry.detail} | view: ${url}` : `view: ${url}`;
}
function buildTaskEntityId(task, creatorUsername) {
    return task.kind === 'owned-asset'
        ? `${creatorUsername}/${task.appName}:${task.name}`
        : `${creatorUsername}/${task.name}`;
}
function pushLoggedEntry(results, entry) {
    results.push(entry);
    console.log((0, uploadLog_1.formatTaskLogLine)(entry));
}
function buildUploadErrorDetail(error) {
    if (error instanceof types_1.ApiError && error.code === 'tag_clear_confirmation_required') {
        return 'tag_clear_confirmation_required: This publish would remove existing live tags. Re-run with --clear-tags to confirm.';
    }
    if (error instanceof types_1.ApiError) {
        const ref = typeof error.details?.ref === 'string' ? error.details.ref.trim() : '';
        const targetKind = typeof error.details?.targetKind === 'string' ? error.details.targetKind.trim() : '';
        if (error.code === 'unknown_tag_ref' && ref) {
            return `unknown_tag_ref:${ref}`;
        }
        if (error.code === 'invalid_tag_ref' && ref) {
            return `invalid_tag_ref:${ref}`;
        }
        if (error.code === 'duplicate_tag_ref' && ref) {
            return `duplicate_tag_ref:${ref}`;
        }
        if (error.code === 'tag_not_allowed_for_target_kind' && ref && targetKind) {
            return `tag_not_allowed_for_target_kind:${ref}:${targetKind}`;
        }
    }
    const rawMessage = typeof error?.message === 'string' && error.message.trim()
        ? error.message.trim()
        : 'upload failed';
    const message = rawMessage.replace(/^Upload failed for [^:]+:\s*/i, '');
    if (/^[a-z0-9_]+:\s+/i.test(message)) {
        return message;
    }
    if (/status\s+413/i.test(message)) {
        return 'upload_too_large: Upload exceeded the Playdrop HTTP 413 limit. Reduce the size of your bundle, source archive, or listing media and try again.';
    }
    if (!message.startsWith('upload failed')) {
        return `upload failed: ${message}`;
    }
    return message;
}
function buildTaskErrorEntry(task, entityId, error) {
    return {
        action: 'upload',
        status: 'error',
        entityType: task.kind,
        entityId,
        catalogue: task.cataloguePath,
        detail: buildUploadErrorDetail(error),
    };
}
function buildPackPlanningFailureEntry(task, defaultCreator, currentUserRole, message) {
    const creator = (0, upload_content_1.getTaskCreatorResult)(task, defaultCreator, currentUserRole);
    return {
        action: 'upload',
        status: 'error',
        entityType: task.kind,
        entityId: buildTaskEntityId(task, creator.taskCreator),
        catalogue: task.cataloguePath,
        detail: `upload failed: ${message}`,
    };
}
function appendTaskRelations(graphState, fromNodeId, relations, contextLabel) {
    (relations ?? []).forEach((relation, index) => {
        if (relation.type === 'REMIXES') {
            throw new Error(`${contextLabel} relations[${index}] must use remix instead of REMIXES.`);
        }
        (0, upload_graph_1.appendPendingRelation)(graphState, fromNodeId, relation.type, relation.to, `${contextLabel} relations[${index}]`);
    });
}
async function uploadAppTask(state, task, taskCreator, options) {
    const { upload, warnings } = await (0, apps_1.runAppPipeline)(state.client, task, {
        skipEcs: options?.skipEcs,
        skipReview: options?.skipReview,
        clearTags: options?.clearTags,
        creatorUsername: taskCreator,
        apiBase: state.apiBase,
        webBase: state.portalBase,
        token: state.token,
        user: state.currentUser,
        runLocalLaunchCheck: true,
        runStagedUploadLaunchCheck: true,
        ensureRegisteredAppShell: true,
    });
    if (!upload.versionCreated || !upload.version) {
        throw new Error(`App "${task.name}" upload did not return a created version.`);
    }
    if (typeof upload.versionId !== 'number') {
        throw new Error(`App "${task.name}" upload did not return version ID.`);
    }
    if (typeof upload.versionNodeId !== 'string' || upload.versionNodeId.trim().length === 0) {
        throw new Error(`App "${task.name}" upload did not return versionNodeId.`);
    }
    const appRef = `app:${taskCreator}/${task.name}@${upload.version}`;
    const uploadedApp = {
        creatorUsername: taskCreator,
        name: task.name,
        version: upload.version,
        versionId: upload.versionId,
        versionNodeId: upload.versionNodeId,
        ref: appRef,
    };
    state.uploadedAppsByName.set(task.name, uploadedApp);
    (0, upload_graph_1.registerCanonicalNode)(state.graphState, appRef, uploadedApp.versionNodeId);
    (0, upload_graph_1.registerLocalRef)(state.graphState.localAppNodeByName, state.graphState.ambiguousAppNames, task.name, uploadedApp.versionNodeId);
    appendTaskRelations(state.graphState, uploadedApp.versionNodeId, task.graph.relations, `[${task.cataloguePath}] app "${task.name}"`);
    const entry = {
        action: 'upload',
        status: 'success',
        entityType: 'app',
        entityId: buildTaskEntityId(task, taskCreator),
        catalogue: task.cataloguePath,
        detail: `version ${upload.version} created`,
        playUrl: (0, appUrls_1.buildPlatformPlayUrl)(state.portalBase, {
            creatorUsername: taskCreator,
            appName: task.name,
            appType: task.type ?? 'GAME',
        }),
    };
    appendOverviewLink(entry, task, state.portalBase, taskCreator);
    return { entry, warnings };
}
async function uploadAssetSpecDefinitionTask(state, task, taskCreator) {
    const uploaded = await (0, upload_content_1.uploadAssetSpecTask)(state.client, task, taskCreator);
    return {
        action: 'upload',
        status: 'success',
        entityType: 'asset-spec',
        entityId: buildTaskEntityId(task, taskCreator),
        catalogue: task.cataloguePath,
        detail: `asset-spec:${uploaded.creatorUsername}/${uploaded.name}@${uploaded.version}`,
    };
}
async function uploadStandaloneAssetTask(state, task, taskCreator, options) {
    const ownedPack = state.packPlanning.ownedPackByAssetKey.get((0, upload_content_1.buildAssetKey)(taskCreator, task.name));
    if (ownedPack) {
        return {
            action: 'upload',
            status: 'success',
            entityType: 'asset',
            entityId: buildTaskEntityId(task, taskCreator),
            catalogue: task.cataloguePath,
            detail: `staged with asset pack ${ownedPack.packTask.name}@${ownedPack.packTask.version}`,
        };
    }
    const uploaded = await (0, upload_content_1.uploadAssetTask)(state.client, task, undefined, taskCreator, {
        clearTags: options?.clearTags,
    });
    state.uploadedAssetsByKey.set(`${uploaded.creatorUsername}/${uploaded.name}`, uploaded);
    (0, upload_graph_1.registerCanonicalNode)(state.graphState, uploaded.ref, uploaded.versionNodeId);
    (0, upload_graph_1.registerLocalRef)(state.graphState.localAssetNodeByName, state.graphState.ambiguousAssetNames, task.name, uploaded.versionNodeId);
    appendTaskRelations(state.graphState, uploaded.versionNodeId, task.relations, `[${task.cataloguePath}] asset "${task.name}"`);
    const entry = {
        action: 'upload',
        status: 'success',
        entityType: 'asset',
        entityId: buildTaskEntityId(task, taskCreator),
        catalogue: task.cataloguePath,
        detail: uploaded.ref,
    };
    appendOverviewLink(entry, task, state.portalBase, uploaded.creatorUsername);
    return entry;
}
async function uploadOwnedAssetTask(state, task, taskCreator, options) {
    const sourceApp = state.uploadedAppsByName.get(task.appName);
    if (!sourceApp) {
        throw new Error(`Owned asset "${task.name}" references app "${task.appName}" that was not uploaded in this run.`);
    }
    const uploaded = await (0, upload_content_1.uploadAssetTask)(state.client, task, sourceApp.versionId, sourceApp.creatorUsername, { clearTags: options?.clearTags });
    state.uploadedAssetsByKey.set(`${uploaded.creatorUsername}/${uploaded.name}`, uploaded);
    (0, upload_graph_1.registerCanonicalNode)(state.graphState, uploaded.ref, uploaded.versionNodeId);
    (0, upload_graph_1.registerLocalRef)(state.graphState.localAssetNodeByName, state.graphState.ambiguousAssetNames, task.name, uploaded.versionNodeId);
    const entry = {
        action: 'upload',
        status: 'success',
        entityType: 'owned-asset',
        entityId: buildTaskEntityId(task, taskCreator),
        catalogue: task.cataloguePath,
        detail: `${uploaded.ref} (source app version ${sourceApp.versionId})`,
    };
    appendOverviewLink(entry, task, state.portalBase, uploaded.creatorUsername);
    return entry;
}
function collectPackLocalAssetTasks(state, task, taskCreator) {
    const packKey = (0, upload_content_1.buildPackKey)(taskCreator, task.name, task.version);
    const packPlan = state.packPlanning.packPlansByKey.get(packKey);
    if (!packPlan) {
        throw new Error(`Asset pack "${task.name}@${task.version}" is missing an upload plan.`);
    }
    const localAssetTasks = task.ownedAssets.filter((ownedAsset) => packPlan.ownedAssetKeys.includes((0, upload_content_1.buildAssetKey)(taskCreator, ownedAsset.name)));
    return { packKey, packPlan, localAssetTasks };
}
function registerUploadedLocalPackAssets(state, task, taskCreator, packPlan, localAssetTasks, uploadedPack) {
    const localTaskByUploadKey = new Map();
    for (const localAssetTask of localAssetTasks) {
        const uploadKey = packPlan.uploadKeyByAssetKey.get((0, upload_content_1.buildAssetKey)(taskCreator, localAssetTask.name));
        if (!uploadKey) {
            throw new Error(`Asset pack "${task.name}@${task.version}" is missing upload key for local asset "${localAssetTask.name}".`);
        }
        localTaskByUploadKey.set(uploadKey, localAssetTask);
    }
    for (const uploadedLocalAsset of uploadedPack.uploadedLocalAssets) {
        const localTask = localTaskByUploadKey.get(uploadedLocalAsset.uploadKey);
        if (!localTask) {
            continue;
        }
        const uploadedLocalRef = {
            creatorUsername: uploadedLocalAsset.creatorUsername,
            name: uploadedLocalAsset.name,
            revision: uploadedLocalAsset.revision,
            ref: `asset:${uploadedLocalAsset.creatorUsername}/${uploadedLocalAsset.name}@r${uploadedLocalAsset.revision}`,
            versionNodeId: uploadedLocalAsset.versionNodeId,
        };
        state.uploadedAssetsByKey.set((0, upload_content_1.buildAssetKey)(uploadedLocalAsset.creatorUsername, uploadedLocalAsset.name), uploadedLocalRef);
        (0, upload_graph_1.registerCanonicalNode)(state.graphState, uploadedLocalRef.ref, uploadedLocalRef.versionNodeId);
        (0, upload_graph_1.registerLocalRef)(state.graphState.localAssetNodeByName, state.graphState.ambiguousAssetNames, localTask.name, uploadedLocalRef.versionNodeId);
    }
}
async function uploadPackTask(state, task, taskCreator, options) {
    const { packPlan, localAssetTasks } = collectPackLocalAssetTasks(state, task, taskCreator);
    const mutationTargetCreator = typeof task.username === 'string' && task.username.trim().length > 0
        ? taskCreator
        : undefined;
    const uploadedPack = await (0, upload_content_1.uploadAssetPackTask)(state.client, task, taskCreator, state.uploadedAssetsByKey, localAssetTasks, packPlan.uploadKeyByAssetKey, mutationTargetCreator, { clearTags: options?.clearTags });
    (0, upload_graph_1.registerCanonicalNode)(state.graphState, uploadedPack.ref, uploadedPack.versionNodeId);
    (0, upload_graph_1.registerLocalRef)(state.graphState.localPackNodeByNameVersion, state.graphState.ambiguousPackNameVersions, `${task.name}@${task.version}`, uploadedPack.versionNodeId);
    registerUploadedLocalPackAssets(state, task, taskCreator, packPlan, localAssetTasks, uploadedPack);
    appendTaskRelations(state.graphState, uploadedPack.versionNodeId, task.relations, `[${task.cataloguePath}] assetPack "${task.name}@${task.version}"`);
    const entry = {
        action: 'upload',
        status: 'success',
        entityType: 'asset-pack',
        entityId: buildTaskEntityId(task, taskCreator),
        catalogue: task.cataloguePath,
        detail: `version ${task.version} created (${uploadedPack.assetRefs.length} assets)`,
    };
    appendOverviewLink(entry, task, state.portalBase, taskCreator);
    return entry;
}
async function processSingleUploadTask(state, node, taskCreator, options) {
    const task = node.task.kind === 'app'
        ? resolveAppTaskLocalDependencies(node.task, node, state)
        : node.task;
    if (task.kind === 'app') {
        return await uploadAppTask(state, task, taskCreator, options);
    }
    if (task.kind === 'asset-spec') {
        return { entry: await uploadAssetSpecDefinitionTask(state, task, taskCreator), warnings: [] };
    }
    if (task.kind === 'asset') {
        return { entry: await uploadStandaloneAssetTask(state, task, taskCreator, options), warnings: [] };
    }
    if (task.kind === 'owned-asset') {
        return { entry: await uploadOwnedAssetTask(state, task, taskCreator, options), warnings: [] };
    }
    if (task.kind === 'asset-pack') {
        return { entry: await uploadPackTask(state, task, taskCreator, options), warnings: [] };
    }
    throw new Error(`Unsupported task kind "${task.kind}".`);
}
async function flushGraphState(client, graphState, results) {
    try {
        await (0, upload_graph_1.applyGraphOperations)(client, graphState);
    }
    catch (error) {
        const message = typeof error?.message === 'string' && error.message.trim().length > 0
            ? error.message.trim()
            : 'graph batch upload failed';
        const entry = {
            action: 'upload',
            status: 'error',
            entityType: 'app',
            entityId: 'graph',
            catalogue: 'graph:batch',
            detail: `upload failed: ${message}`,
        };
        pushLoggedEntry(results, entry);
        process.exitCode = process.exitCode || 1;
    }
}
async function processUploadTasks(client, tasks, owner, ownerUsername, currentUserRole, localTagGroups, currentUser, token, warnings, apiBase, webBase, options) {
    const portalBase = normalizePortalBase(webBase);
    const defaultCreator = normalizeCreatorUsername(ownerUsername) ?? owner;
    const results = [];
    const preflight = await buildUploadPreflight(client, tasks, defaultCreator, currentUserRole, localTagGroups);
    const sortedTasks = preflight.nodes.map((node) => node.task);
    const packPlanning = (0, upload_content_1.buildAssetPackUploadPlans)(sortedTasks, defaultCreator, currentUserRole);
    const taskStatusById = new Map(preflight.nodes.map((node) => [node.id, 'pending']));
    preflight.initialEntries.forEach(({ taskId, entry }) => {
        taskStatusById.set(taskId, entry.status);
        pushLoggedEntry(results, entry);
    });
    if (!packPlanning.ok) {
        const entry = buildPackPlanningFailureEntry(packPlanning.task, defaultCreator, currentUserRole, packPlanning.message);
        pushLoggedEntry(results, entry);
        return results;
    }
    const state = {
        client,
        sortedTasks,
        defaultCreator,
        currentUserRole,
        currentUser,
        token,
        apiBase,
        portalBase,
        uploadedAppsByName: new Map(),
        uploadedAssetsByKey: new Map(),
        graphState: (0, upload_graph_1.buildEmptyGraphState)(),
        packPlanning,
    };
    while (true) {
        const pendingNodes = preflight.nodes.filter((node) => taskStatusById.get(node.id) === 'pending');
        if (pendingNodes.length === 0) {
            break;
        }
        let madeProgress = false;
        for (const node of preflight.nodes) {
            if (taskStatusById.get(node.id) !== 'pending') {
                continue;
            }
            const failedDependency = node.localDependencies.find((dependency) => {
                const dependencyStatus = taskStatusById.get(dependency.taskId);
                return dependencyStatus === 'error' || dependencyStatus === 'blocked';
            });
            if (failedDependency) {
                const entry = buildTaskBlockedEntry(node.task, node.entityId, `local_dependency_failed:${failedDependency.ref}`);
                taskStatusById.set(node.id, entry.status);
                pushLoggedEntry(results, entry);
                madeProgress = true;
                continue;
            }
            const waitingOnLocalDependency = node.localDependencies.some((dependency) => taskStatusById.get(dependency.taskId) === 'pending');
            if (waitingOnLocalDependency) {
                continue;
            }
            const creatorResult = (0, upload_content_1.getTaskCreatorResult)(node.task, defaultCreator, currentUserRole);
            if (creatorResult.requestedTaskOwner && !creatorResult.creatorTargetError) {
                console.log(`[Admin] Publishing ${node.task.name} as user: ${creatorResult.taskCreator}`);
            }
            try {
                const outcome = await processSingleUploadTask(state, node, node.creator, options);
                outcome.warnings.forEach((warning) => warnings.add(warning));
                taskStatusById.set(node.id, outcome.entry.status);
                pushLoggedEntry(results, outcome.entry);
            }
            catch (error) {
                if (error instanceof http_1.CLIUnsupportedClientError) {
                    throw error;
                }
                const entry = buildTaskErrorEntry(node.task, node.entityId, error);
                taskStatusById.set(node.id, entry.status);
                pushLoggedEntry(results, entry);
            }
            madeProgress = true;
            break;
        }
        if (madeProgress) {
            continue;
        }
        for (const node of pendingNodes) {
            const entry = buildTaskDirectErrorEntry(node.task, node.entityId, 'dependency_cycle_detected');
            taskStatusById.set(node.id, entry.status);
            pushLoggedEntry(results, entry);
        }
    }
    await flushGraphState(client, state.graphState, results);
    return results;
}
function printResolvedUploadAccount(input) {
    const username = typeof input.username === 'string' && input.username.trim().length > 0
        ? input.username.trim()
        : 'unknown';
    console.log(`Resolved account: ${username} (${input.env})`);
}
async function upload(pathOrName, options) {
    const selection = (0, taskSelection_1.selectTasks)(pathOrName);
    const workspaceRoot = resolveUploadWorkspaceRoot(pathOrName, selection.resolution);
    const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project publish', 'Publishing content', {
        env: options?.env,
        workspacePath: workspaceRoot,
    });
    if (!ctx) {
        return;
    }
    const { client, env, envConfig } = ctx;
    printResolvedUploadAccount({
        env,
        username: ctx.account?.username,
    });
    let userInfo = { username: null, role: null, user: null };
    try {
        userInfo = await fetchCurrentUserInfo(client, envConfig.apiBase);
    }
    catch (error) {
        if (error instanceof http_1.CLIUnsupportedClientError) {
            return;
        }
        throw error;
    }
    const owner = typeof userInfo.username === 'string' && userInfo.username.trim().length > 0
        ? userInfo.username.trim()
        : 'unknown';
    const tagGroupLoad = (0, catalogue_1.resolveCatalogueTagGroups)(workspaceRoot);
    if (tagGroupLoad.errors.length > 0) {
        (0, taskSelection_1.reportTaskErrors)(tagGroupLoad.errors.map((message) => ({
            type: 'invalid-catalogue',
            message,
            help: ['Update the tagGroups entries noted above, then retry the publish.'],
        })), 'project publish');
        process.exitCode = 1;
        return;
    }
    if (selection.errors.length > 0) {
        const canPublishTagGroupsOnly = selection.errors.every((error) => error.type === 'no-tasks') && tagGroupLoad.groups.length > 0;
        if (canPublishTagGroupsOnly) {
            selection.errors.length = 0;
        }
    }
    if (selection.errors.length > 0) {
        (0, taskSelection_1.reportTaskErrors)(selection.errors, 'project publish');
        process.exitCode = 1;
        return;
    }
    const warnings = selection.warnings;
    const tasks = selection.tasks;
    if (tagGroupLoad.groups.length > 0 && userInfo.role !== 'ADMIN') {
        (0, messages_1.printErrorWithHelp)('Publishing tagGroups requires an admin account.', [
            'Log in as an admin before publishing taxonomy changes.',
            'Remove tagGroups from catalogue.json if this publish should only upload content.',
        ], { command: 'project publish' });
        process.exitCode = 1;
        return;
    }
    const results = [];
    if (tagGroupLoad.groups.length > 0) {
        const taxonomyEntries = await syncTagGroupsFromCatalogue(client, tagGroupLoad.groups);
        taxonomyEntries.forEach((entry) => pushLoggedEntry(results, entry));
    }
    if (tasks.length > 0) {
        const uploadEntries = await processUploadTasks(client, tasks, owner, userInfo.username, userInfo.role, tagGroupLoad.groups, userInfo.user, ctx.token, warnings, envConfig.apiBase, envConfig.webBase, options);
        results.push(...uploadEntries);
    }
    (0, uploadLog_1.printTaskSummary)(results, warnings, { action: 'upload', environment: env });
    (0, uploadLog_1.printPublishedAppNextSteps)(results);
}
