"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.browse = browse;
exports.browseCategories = browseCategories;
const types_1 = require("@playdrop/types");
const commandContext_1 = require("../commandContext");
const errors_1 = require("../errors");
const messages_1 = require("../messages");
const output_1 = require("../output");
const refs_1 = require("../refs");
const MERGED_PAGE_SIZE = 100;
function parseBrowseKind(raw) {
    if (!raw || raw.trim().length === 0) {
        return 'all';
    }
    const normalized = raw.trim().toLowerCase();
    if (normalized === 'app' || normalized === 'asset' || normalized === 'asset-pack' || normalized === 'asset-spec' || normalized === 'all') {
        return normalized;
    }
    return null;
}
function parseBrowseCreator(raw) {
    if (!raw || raw.trim().length === 0) {
        return 'all';
    }
    const normalized = raw.trim();
    const lowered = normalized.toLowerCase();
    if (lowered === 'all' || lowered === 'me') {
        return lowered;
    }
    if (normalized.includes('/') || normalized.includes('\\') || normalized.includes('..')) {
        return null;
    }
    return normalized;
}
function parsePositiveInteger(raw, label, fallback) {
    if (raw === undefined) {
        return fallback;
    }
    const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);
    if (!Number.isInteger(parsed) || parsed < 0) {
        return null;
    }
    if (label === 'limit' && parsed <= 0) {
        return null;
    }
    return parsed;
}
function parseAssetCategory(raw) {
    if (!raw || raw.trim().length === 0) {
        return undefined;
    }
    const normalized = raw.trim().toUpperCase().replace(/[-\s]/g, '_');
    if (normalized === '3D' || normalized === 'MODEL3D') {
        return 'MODEL_3D';
    }
    if (types_1.ASSET_CATEGORY_VALUES.includes(normalized)) {
        return normalized;
    }
    return null;
}
function parseAssetSubcategory(raw) {
    if (!raw || raw.trim().length === 0) {
        return undefined;
    }
    const normalized = (0, types_1.normalizeAssetSubcategory)(raw);
    return normalized || null;
}
function parseBrowseSort(raw) {
    if (!raw || raw.trim().length === 0) {
        return undefined;
    }
    const normalized = raw.trim().toLowerCase();
    if (types_1.APP_LIST_SORT_VALUES.includes(normalized)) {
        return normalized;
    }
    if (types_1.ASSET_LIST_SORT_VALUES.includes(normalized)) {
        return normalized;
    }
    if (types_1.ASSET_SPEC_LIST_SORT_VALUES.includes(normalized)) {
        return normalized;
    }
    return null;
}
function buildItemRef(kind, creator, name) {
    return `${creator}/${kind}/${name}`;
}
function toBrowseItem(kind, item) {
    return {
        kind,
        ref: buildItemRef(kind, item.creatorUsername, item.name),
        item: item,
    };
}
function itemSummary(item) {
    if (item.kind === 'app') {
        return item.item.type.toLowerCase();
    }
    if (item.kind === 'asset') {
        const subcategory = item.item.currentVersion?.subcategory;
        return subcategory ? `${item.item.category.toLowerCase()}/${subcategory}` : item.item.category.toLowerCase();
    }
    if (item.kind === 'asset-spec') {
        return item.item.currentVersionRef ? `current ${item.item.currentVersionRef.split('@')[1] ?? 'none'}` : 'no current version';
    }
    return 'asset-pack';
}
function formatCountValue(value) {
    return value.toLocaleString('en-US');
}
function buildCountsSuffix(item) {
    const parts = [];
    if (item.kind === 'app' && typeof item.item.playCount === 'number') {
        if (typeof item.item.viewCount === 'number') {
            parts.push(`view ${formatCountValue(item.item.viewCount)}`);
        }
        parts.push(`launch ${formatCountValue(item.item.playCount)}`);
    }
    if (item.kind === 'asset') {
        if (typeof item.item.viewCount === 'number') {
            parts.push(`view ${formatCountValue(item.item.viewCount)}`);
        }
    }
    if (item.kind === 'asset-pack' && typeof item.item.downloadCount === 'number') {
        parts.push(`download ${formatCountValue(item.item.downloadCount)}`);
    }
    if (item.kind === 'asset-spec') {
        parts.push(`assets ${formatCountValue(item.item.publicAssetCount)}`);
        parts.push(`apps ${formatCountValue(item.item.supportingAppCount)}`);
        if (item.item.documentationAvailable) {
            parts.push('docs yes');
        }
        return ` | ${parts.join(' | ')}`;
    }
    if (typeof item.item.likeCount === 'number') {
        parts.push(`like ${formatCountValue(item.item.likeCount)}`);
    }
    if (typeof item.item.remixCount === 'number') {
        parts.push(`remix ${formatCountValue(item.item.remixCount)}`);
    }
    if (typeof item.item.commentCount === 'number') {
        parts.push(`comment ${formatCountValue(item.item.commentCount)}`);
    }
    return parts.length > 0 ? ` | ${parts.join(' | ')}` : '';
}
async function collectPaginatedItems(fetchPage) {
    const items = [];
    let offset = 0;
    let lastPagination = null;
    while (true) {
        const page = await fetchPage(MERGED_PAGE_SIZE, offset);
        items.push(...page.items);
        lastPagination = page.pagination;
        if (!page.pagination.hasMore) {
            break;
        }
        if (page.pagination.limit <= 0) {
            throw new Error('invalid_pagination_limit');
        }
        offset += page.pagination.limit;
    }
    return {
        items,
        pagination: lastPagination ?? { limit: MERGED_PAGE_SIZE, offset: 0, count: 0, hasMore: false },
    };
}
function sliceMergedItems(items, limit, offset) {
    const count = items.length;
    return {
        items: items.slice(offset, offset + limit),
        pagination: {
            limit,
            offset,
            count,
            hasMore: offset + limit < count,
        },
    };
}
function requirePagination(pagination, context) {
    if (!pagination) {
        throw new Error(`missing_pagination:${context}`);
    }
    return pagination;
}
async function resolveCreatorUsername(creator, command, resolveMe) {
    if (creator === 'all') {
        return null;
    }
    if (creator === 'me') {
        try {
            return await resolveMe();
        }
        catch (error) {
            if (error instanceof types_1.ApiError && (error.status === 401 || error.status === 403)) {
                (0, messages_1.printLoginRequired)('Browsing your content', command);
                process.exitCode = 1;
                return null;
            }
            throw error;
        }
    }
    return creator;
}
function normalizeOptionalOption(value) {
    if (typeof value !== 'string') {
        return undefined;
    }
    const normalized = value.trim();
    return normalized.length > 0 ? normalized : undefined;
}
function normalizeTagFilters(rawTags) {
    if (!Array.isArray(rawTags) || rawTags.length === 0) {
        return [];
    }
    const normalizedRefs = [];
    const seenRefs = new Set();
    for (const rawTag of rawTags) {
        if (typeof rawTag !== 'string' || rawTag.trim().length === 0) {
            return null;
        }
        let normalizedRef = '';
        try {
            normalizedRef = (0, types_1.normalizeTagRef)(rawTag);
        }
        catch {
            return null;
        }
        if (seenRefs.has(normalizedRef)) {
            return null;
        }
        seenRefs.add(normalizedRef);
        normalizedRefs.push(normalizedRef);
    }
    if (normalizedRefs.length > 5) {
        return null;
    }
    return normalizedRefs;
}
function buildTagSuffix(tags) {
    const normalizedTags = Array.isArray(tags) ? tags : [];
    if (normalizedTags.length === 0) {
        return '';
    }
    return ` | tags ${normalizedTags.slice(0, 3).map((tag) => tag.ref).join(', ')}`;
}
function parseBrowseSortOption(rawSort, kind) {
    const sort = parseBrowseSort(rawSort);
    if (sort === null) {
        (0, messages_1.printErrorWithHelp)(`Sort "${rawSort ?? ''}" is not supported.`, ['Use one of: newest, updated, views, likes, used, recent, remixes, comments, assets, apps, alpha.'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    if (sort && kind === 'all') {
        (0, messages_1.printErrorWithHelp)('The --sort option is not supported with --kind all.', ['Choose --kind app, --kind asset, or --kind asset-spec when sorting browse results.'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    if (sort && kind === 'app' && !types_1.APP_LIST_SORT_VALUES.includes(sort)) {
        (0, messages_1.printErrorWithHelp)(`Sort "${sort}" is not supported for apps.`, ['Use one of: newest, updated, views, likes, used.'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    if (sort && kind === 'asset-spec' && !types_1.ASSET_SPEC_LIST_SORT_VALUES.includes(sort)) {
        (0, messages_1.printErrorWithHelp)(`Sort "${sort}" is not supported for asset specs.`, ['Use one of: recent, assets, apps, alpha.'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    if (sort && kind === 'asset' && !types_1.ASSET_LIST_SORT_VALUES.includes(sort)) {
        (0, messages_1.printErrorWithHelp)(`Sort "${sort}" is not supported for assets.`, ['Use one of: recent, likes, remixes, comments.'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    return sort ?? undefined;
}
function parseBrowseLimitsAndFilters(options) {
    const assetCategory = parseAssetCategory(options.assetCategory);
    if (assetCategory === null) {
        (0, messages_1.printErrorWithHelp)(`Asset category "${options.assetCategory}" is not supported.`, ['Use a canonical asset category like IMAGE, VIDEO, AUDIO, SPRITESHEET, or MODEL_3D.'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    const assetSubcategory = parseAssetSubcategory(options.assetSubcategory);
    if (assetSubcategory === null) {
        (0, messages_1.printErrorWithHelp)(`Asset subcategory "${options.assetSubcategory}" is invalid.`, ['Use lowercase slug values like generic, avatar, music, or sfx.'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    const limit = parsePositiveInteger(options.limit, 'limit', 10);
    if (limit === null) {
        (0, messages_1.printErrorWithHelp)('The --limit value must be a positive integer.', ['Example: --limit 10'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    const offset = parsePositiveInteger(options.offset, 'offset', 0);
    if (offset === null) {
        (0, messages_1.printErrorWithHelp)('The --offset value must be zero or a positive integer.', ['Example: --offset 20'], { command: 'browse' });
        process.exitCode = 1;
        return null;
    }
    return {
        assetCategory: assetCategory ?? undefined,
        assetSubcategory: assetSubcategory ?? undefined,
        limit,
        offset,
    };
}
function parseBrowseCommandOptions(options) {
    const kind = parseBrowseKind(options.kind);
    if (!kind) {
        (0, messages_1.printErrorWithHelp)(`Kind "${options.kind ?? ''}" is not supported.`, ['Use one of: app, asset, asset-pack, asset-spec, all.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    const creator = parseBrowseCreator(options.creator);
    if (!creator) {
        (0, messages_1.printErrorWithHelp)('The --creator value is invalid.', ['Use all, me, or a creator slug without path separators.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    const appType = options.appType ? (0, types_1.parseAppType)(options.appType.trim().toUpperCase()) : undefined;
    if (options.appType !== undefined && !appType) {
        (0, messages_1.printErrorWithHelp)(`App type "${options.appType}" is not supported.`, ['Use one of: game, demo, tool, template.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    const auth = options.auth === undefined ? undefined : (0, types_1.parseAppAuthFilter)(options.auth);
    if (options.auth !== undefined && !auth) {
        (0, messages_1.printErrorWithHelp)(`Auth filter "${options.auth}" is not supported.`, ['Use one of: required, optional, none.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    const controller = options.controller === undefined ? undefined : (0, types_1.parseAppControllerFilter)(options.controller);
    if (options.controller !== undefined && !controller) {
        (0, messages_1.printErrorWithHelp)(`Controller filter "${options.controller}" is not supported.`, ['Use one of: required, optional, none.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    const surface = options.surface === undefined ? undefined : (0, types_1.parseAppSurfaceFilter)(options.surface);
    if (options.surface !== undefined && !surface) {
        (0, messages_1.printErrorWithHelp)(`Surface filter "${options.surface}" is not supported.`, ['Use one of: desktop, mobile-portrait, mobile-landscape.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    const hasCapabilityFilters = Boolean(auth || controller || surface);
    if (hasCapabilityFilters && kind !== 'app') {
        (0, messages_1.printErrorWithHelp)('Capability filters only support app browse.', ['Use --kind app together with --auth, --surface, or --controller.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    const browseFilters = parseBrowseLimitsAndFilters(options);
    if (!browseFilters) {
        return { ok: false };
    }
    const sort = parseBrowseSortOption(options.sort, kind);
    if (sort === null) {
        return { ok: false };
    }
    const tags = normalizeTagFilters(options.tag);
    if (tags === null) {
        (0, messages_1.printErrorWithHelp)('The --tag value is invalid.', ['Use canonical refs like theme/pirate or visual-style/pixel-art.', 'Pass at most 5 unique tag refs.'], { command: 'browse' });
        process.exitCode = 1;
        return { ok: false };
    }
    return {
        ok: true,
        input: {
            kind,
            creator,
            appType: appType ?? undefined,
            auth: auth ?? undefined,
            controller: controller ?? undefined,
            surface: surface ?? undefined,
            ...browseFilters,
            assetSpec: normalizeOptionalOption(options.assetSpec),
            assetSpecOwner: normalizeOptionalOption(options.assetSpecOwner),
            assetSpecName: normalizeOptionalOption(options.assetSpecName),
            sort,
            tags,
        },
    };
}
async function loadMergedBrowseResults(input) {
    const { client, creator, creatorUsername, appType, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, limit, offset, tags, } = input;
    const appResultsPromise = creatorUsername
        ? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByCreator(creatorUsername, {
            limit: pageLimit,
            offset: pageOffset,
            type: appType,
            ...(tags.length > 0 ? { tags } : {}),
            ...(input.sort ? { sort: input.sort } : {}),
        })
            .then((response) => ({
            items: response.apps ?? [],
            pagination: requirePagination(response.pagination, 'browse_all_creator_apps'),
        })))
        : (appType
            ? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByType(appType, {
                limit: pageLimit,
                offset: pageOffset,
                ...(tags.length > 0 ? { tags } : {}),
                ...(input.sort ? { sort: input.sort } : {}),
            }).then((response) => ({
                items: response.apps ?? [],
                pagination: requirePagination(response.pagination, 'browse_all_type_apps'),
            })))
            : collectPaginatedItems((pageLimit, pageOffset) => client.fetchApps({
                limit: pageLimit,
                offset: pageOffset,
                ...(tags.length > 0 ? { tags } : {}),
                ...(input.sort ? { sort: input.sort } : {}),
            }).then((response) => ({
                items: response.apps ?? [],
                pagination: requirePagination(response.pagination, 'browse_all_apps'),
            }))));
    const assetResultsPromise = creatorUsername
        ? collectPaginatedItems((pageLimit, pageOffset) => client.listAssetsForCreator(creatorUsername, {
            limit: pageLimit,
            offset: pageOffset,
            category: assetCategory,
            subcategory: assetSubcategory,
            assetSpec,
            assetSpecOwner,
            assetSpecName,
            ...(tags.length > 0 ? { tags } : {}),
        }).then((response) => ({
            items: response.assets ?? [],
            pagination: requirePagination(response.pagination, 'browse_all_creator_assets'),
        })))
        : collectPaginatedItems((pageLimit, pageOffset) => client.listAssets({
            limit: pageLimit,
            offset: pageOffset,
            category: assetCategory,
            subcategory: assetSubcategory,
            assetSpec,
            assetSpecOwner,
            assetSpecName,
            ...(tags.length > 0 ? { tags } : {}),
        }).then((response) => ({
            items: response.assets ?? [],
            pagination: requirePagination(response.pagination, 'browse_all_assets'),
        })));
    const specResultsPromise = creatorUsername
        ? collectPaginatedItems((pageLimit, pageOffset) => client.listAssetSpecsForCreator(creatorUsername, { limit: pageLimit, offset: pageOffset }).then((response) => ({
            items: response.assetSpecs ?? [],
            pagination: requirePagination(response.pagination, 'browse_all_creator_specs'),
        })))
        : collectPaginatedItems((pageLimit, pageOffset) => client.listAssetSpecs({ limit: pageLimit, offset: pageOffset }).then((response) => ({
            items: response.assetSpecs ?? [],
            pagination: requirePagination(response.pagination, 'browse_all_specs'),
        })));
    const packResultsPromise = creatorUsername
        ? collectPaginatedItems((pageLimit, pageOffset) => client.listAssetPacksForCreator(creatorUsername, {
            limit: pageLimit,
            offset: pageOffset,
            containsCategory: assetCategory,
            containsSubcategory: assetSubcategory,
            ...(tags.length > 0 ? { tags } : {}),
        }).then((response) => ({
            items: response.packs ?? [],
            pagination: requirePagination(response.pagination, 'browse_all_creator_packs'),
        })))
        : collectPaginatedItems((pageLimit, pageOffset) => client.listAssetPacks({
            limit: pageLimit,
            offset: pageOffset,
            containsCategory: assetCategory,
            containsSubcategory: assetSubcategory,
            ...(tags.length > 0 ? { tags } : {}),
        }).then((response) => ({
            items: response.packs ?? [],
            pagination: requirePagination(response.pagination, 'browse_all_packs'),
        })));
    const [apps, assets, specs, packs] = await Promise.all([
        appResultsPromise,
        assetResultsPromise,
        specResultsPromise,
        packResultsPromise,
    ]);
    const combined = [
        ...apps.items.map((item) => toBrowseItem('app', item)),
        ...assets.items.map((item) => toBrowseItem('asset', item)),
        ...specs.items.map((item) => toBrowseItem('asset-spec', item)),
        ...packs.items.map((item) => toBrowseItem('asset-pack', item)),
    ];
    return sliceMergedItems(combined, limit, offset);
}
async function runAssetBrowseCommand(input) {
    const { client, creatorUsername, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, sort, limit, offset, tags } = input;
    const response = creatorUsername
        ? await client.listAssetsForCreator(creatorUsername, {
            limit,
            offset,
            category: assetCategory,
            subcategory: assetSubcategory,
            assetSpec,
            assetSpecOwner,
            assetSpecName,
            ...(tags.length > 0 ? { tags } : {}),
            ...(sort ? { sort: sort } : {}),
        })
        : await client.listAssets({
            limit,
            offset,
            category: assetCategory,
            subcategory: assetSubcategory,
            assetSpec,
            assetSpecOwner,
            assetSpecName,
            ...(tags.length > 0 ? { tags } : {}),
            ...(sort ? { sort: sort } : {}),
        });
    return {
        items: (response.assets ?? []).map((item) => toBrowseItem('asset', item)),
        pagination: requirePagination(response.pagination, 'browse_assets'),
    };
}
async function runAssetSpecBrowseCommand(input) {
    const { client, creatorUsername, sort, limit, offset } = input;
    const response = creatorUsername
        ? await client.listAssetSpecsForCreator(creatorUsername, { limit, offset, ...(sort ? { sort: sort } : {}) })
        : await client.listAssetSpecs({ limit, offset, ...(sort ? { sort: sort } : {}) });
    return {
        items: (response.assetSpecs ?? []).map((item) => toBrowseItem('asset-spec', item)),
        pagination: requirePagination(response.pagination, 'browse_asset_specs'),
    };
}
async function runAssetPackBrowseCommand(input) {
    const { client, creatorUsername, assetCategory, assetSubcategory, limit, offset, tags } = input;
    const response = creatorUsername
        ? await client.listAssetPacksForCreator(creatorUsername, {
            limit,
            offset,
            containsCategory: assetCategory,
            containsSubcategory: assetSubcategory,
            ...(tags.length > 0 ? { tags } : {}),
        })
        : await client.listAssetPacks({
            limit,
            offset,
            containsCategory: assetCategory,
            containsSubcategory: assetSubcategory,
            ...(tags.length > 0 ? { tags } : {}),
        });
    return {
        items: (response.packs ?? []).map((item) => toBrowseItem('asset-pack', item)),
        pagination: requirePagination(response.pagination, 'browse_asset_packs'),
    };
}
async function runBrowseCommand(input) {
    const { client, kind, creator, creatorUsername, appType, auth, controller, surface, sort, limit, offset, tags, } = input;
    if (kind === 'app') {
        if (creatorUsername) {
            const response = await client.fetchAppsByCreator(creatorUsername, {
                limit,
                offset,
                type: appType,
                ...(auth ? { auth } : {}),
                ...(controller ? { controller } : {}),
                ...(surface ? { surface } : {}),
                ...(tags.length > 0 ? { tags } : {}),
                ...(sort ? { sort: sort } : {}),
            });
            return {
                items: (response.apps ?? []).map((item) => toBrowseItem('app', item)),
                pagination: requirePagination(response.pagination, 'browse_app_creator'),
            };
        }
        if (appType) {
            const response = await client.fetchAppsByType(appType, {
                limit,
                offset,
                ...(auth ? { auth } : {}),
                ...(controller ? { controller } : {}),
                ...(surface ? { surface } : {}),
                ...(tags.length > 0 ? { tags } : {}),
                ...(sort ? { sort: sort } : {}),
            });
            return {
                items: (response.apps ?? []).map((item) => toBrowseItem('app', item)),
                pagination: requirePagination(response.pagination, 'browse_app_type'),
            };
        }
        const response = await client.fetchApps({
            limit,
            offset,
            ...(auth ? { auth } : {}),
            ...(controller ? { controller } : {}),
            ...(surface ? { surface } : {}),
            ...(tags.length > 0 ? { tags } : {}),
            ...(sort ? { sort: sort } : {}),
        });
        return {
            items: (response.apps ?? []).map((item) => toBrowseItem('app', item)),
            pagination: requirePagination(response.pagination, 'browse_app_all'),
        };
    }
    if (kind === 'asset') {
        return await runAssetBrowseCommand(input);
    }
    if (kind === 'asset-spec') {
        return await runAssetSpecBrowseCommand(input);
    }
    if (kind === 'asset-pack') {
        return await runAssetPackBrowseCommand(input);
    }
    return await loadMergedBrowseResults(input);
}
async function browse(options = {}) {
    const parsed = parseBrowseCommandOptions(options);
    if (!parsed.ok) {
        return;
    }
    const { kind, creator, appType, auth, controller, surface, assetCategory, assetSubcategory, assetSpec, assetSpecOwner, assetSpecName, sort, limit, offset, tags, } = parsed.input;
    await (0, commandContext_1.withPublicEnvironment)('browse', async ({ client }) => {
        try {
            const creatorUsername = await resolveCreatorUsername(creator, 'browse', async () => {
                const response = await client.me();
                const username = response.user?.username?.trim();
                if (!username) {
                    throw new Error('missing_current_user');
                }
                return username;
            });
            if (process.exitCode) {
                return;
            }
            const { items, pagination } = await runBrowseCommand({
                client,
                kind,
                creator,
                creatorUsername,
                appType,
                auth,
                controller,
                surface,
                assetCategory,
                assetSubcategory,
                assetSpec,
                assetSpecOwner,
                assetSpecName,
                sort,
                limit,
                offset,
                tags,
            });
            if (options.json) {
                (0, output_1.printJson)({
                    items,
                    ...(pagination ? { pagination } : {}),
                });
                return;
            }
            if (items.length === 0) {
                const label = kind === 'all' ? 'content' : (0, refs_1.contentKindPluralLabel)(kind);
                console.log(`No ${label} found.`);
                console.log('Next: adjust your filters or run "playdrop search <query>" to look for something specific.');
                return;
            }
            if (kind === 'all') {
                console.log('Browsing content (apps, assets, asset specs, then asset packs):\n');
            }
            else {
                console.log(`Browsing ${(0, refs_1.contentKindPluralLabel)(kind)}:\n`);
            }
            for (const [index, item] of items.entries()) {
                const displayName = item.item.displayName || item.item.name;
                console.log(`${index + 1}. [${item.kind}] ${item.ref} | ${displayName} | @${item.item.creatorUsername} | ${itemSummary(item)}${buildCountsSuffix(item)}${buildTagSuffix(item.item.tags)}`);
            }
            console.log('\nNext: run "playdrop detail <creator>/<kind>/<name>" to inspect one item.');
        }
        catch (error) {
            const handled = (0, errors_1.handleCommandFailure)(error, 'browse', 'Browse', {
                apiMessage: (apiError) => {
                    if (apiError.code === 'invalid_tag_ref' || apiError.code === 'duplicate_tag_ref' || apiError.code === 'tag_filter_limit_exceeded') {
                        return {
                            problem: 'One or more tag filters are invalid.',
                            suggestions: ['Use canonical refs like theme/pirate.', 'Pass at most 5 unique --tag values.'],
                        };
                    }
                    if (apiError.status === 401 || apiError.status === 403) {
                        return {
                            problem: 'Browsing this content requires you to be logged in.',
                            suggestions: ['Run "playdrop auth login" and retry.'],
                        };
                    }
                    return {
                        problem: `Browse failed with status ${apiError.status}.`,
                        suggestions: ['Retry in a moment.', 'Use "playdrop help browse" for usage details.'],
                    };
                },
            });
            if (!handled) {
                throw error;
            }
        }
    });
}
async function browseCategories(options = {}) {
    await (0, commandContext_1.withPublicEnvironment)('browse categories', async ({ client }) => {
        try {
            const response = await client.listAssetCategories();
            const items = response.categories ?? [];
            if (options.json) {
                (0, output_1.printJson)({ items });
                return;
            }
            if (items.length === 0) {
                console.log('No asset categories were returned.');
                return;
            }
            console.log('Asset categories:\n');
            for (const item of items) {
                const formats = Array.isArray(item.formats) ? item.formats.join(', ') : 'none';
                console.log(`${item.category} / ${item.subcategory}`);
                console.log(`  Formats: ${formats}`);
            }
            console.log('\nNext: use these values with "playdrop browse" or "playdrop search".');
        }
        catch (error) {
            const handled = (0, errors_1.handleCommandFailure)(error, 'browse categories', 'Browse categories', {
                apiMessage: (apiError) => ({
                    problem: `Category lookup failed with status ${apiError.status}.`,
                    suggestions: ['Retry in a moment.'],
                }),
            });
            if (!handled) {
                throw error;
            }
        }
    });
}
