"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validate = validate;
const types_1 = require("@playdrop/types");
const types_2 = require("@playdrop/types");
const commandContext_1 = require("../commandContext");
const taskSelection_1 = require("../taskSelection");
const taskUtils_1 = require("../taskUtils");
const uploadLog_1 = require("../uploadLog");
const apps_1 = require("../apps");
const assetSpecs_1 = require("../assetSpecs");
const externalAssetPackValidation_1 = require("../externalAssetPackValidation");
const upload_content_1 = require("./upload-content");
function buildLocalAssetSpecLookups(tasks) {
    const exactByRef = new Map();
    const uniqueByNameVersion = new Map();
    for (const task of tasks) {
        if (task.username) {
            exactByRef.set(`asset-spec:${task.username}/${task.name}@${task.version}`, task);
        }
        const nameVersionKey = `${task.name}@${task.version}`;
        if (!uniqueByNameVersion.has(nameVersionKey)) {
            uniqueByNameVersion.set(nameVersionKey, task);
        }
        else {
            uniqueByNameVersion.set(nameVersionKey, null);
        }
    }
    return { exactByRef, uniqueByNameVersion };
}
function resolveLocalAssetSpecTask(task, lookups) {
    if (!task.assetSpec) {
        return null;
    }
    const direct = lookups.exactByRef.get(task.assetSpec);
    if (direct) {
        return direct;
    }
    const parsed = (0, types_2.parseAssetSpecVersionRef)(task.assetSpec);
    if (!parsed) {
        return null;
    }
    const fallback = lookups.uniqueByNameVersion.get(`${parsed.name}@${parsed.version}`);
    return fallback ?? null;
}
function validateAssetTask(task, assetSpecLookups) {
    if (!task.assetSpec) {
        return;
    }
    const localAssetSpecTask = resolveLocalAssetSpecTask(task, assetSpecLookups);
    if (!localAssetSpecTask) {
        return;
    }
    const errors = [];
    (0, assetSpecs_1.validateCustomAssetFilesAgainstContract)(localAssetSpecTask.contract, task.filePaths, errors, `Asset "${task.name}"`);
    if (errors.length > 0) {
        throw new Error(errors.join(' '));
    }
}
async function loadAuthenticatedValidationContext(workspacePath, envOverride) {
    const ctx = await (0, commandContext_1.resolveOptionalEnvironmentContext)('project validate', {
        env: envOverride,
        workspacePath,
    });
    if (!ctx) {
        return null;
    }
    const { client } = ctx;
    try {
        const me = await client.me();
        const username = typeof me?.user?.username === 'string' ? me.user.username.trim() : '';
        if (!username) {
            return null;
        }
        return {
            client,
            currentUserRole: me.user.role ?? null,
            username,
        };
    }
    catch {
        return null;
    }
}
async function loadHostedLaunchValidationContext(workspacePath, envOverride) {
    const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project validate', 'run hosted app launch validation', {
        env: envOverride,
        workspacePath,
    });
    if (!ctx) {
        return null;
    }
    const username = typeof ctx.account?.username === 'string' ? ctx.account.username.trim() : '';
    if (!username) {
        process.exitCode = 1;
        return null;
    }
    let currentUserRole = null;
    let currentUser = null;
    try {
        const me = await ctx.client.me();
        currentUserRole = me.user.role ?? null;
        currentUser = me.user ?? null;
    }
    catch {
        currentUserRole = null;
        currentUser = null;
    }
    return {
        client: ctx.client,
        currentUserRole,
        currentUser,
        username,
        apiBase: ctx.envConfig.apiBase,
        webBase: ctx.envConfig.webBase ?? null,
        token: ctx.token,
    };
}
function printResolvedValidationAccount(ctx) {
    const username = typeof ctx.account?.username === 'string' && ctx.account.username.trim().length > 0
        ? ctx.account.username.trim()
        : 'unknown';
    console.log(`Resolved account: ${username} (${ctx.env})`);
}
function resolveValidationWorkspacePath(pathOrName, resolution) {
    return resolution === 'name' ? process.cwd() : pathOrName;
}
function formatLiveTagClearWarning(input) {
    const countLabel = input.liveTagCount === 1 ? '1 existing live tag' : `${input.liveTagCount} existing live tags`;
    return `${input.entityLabel} "${input.displayName}" would remove ${countLabel} on publish. Add a tags field or pass --clear-tags to confirm.`;
}
// eslint-disable-next-line complexity
async function collectLiveTagClearWarnings(tasks, workspacePath, envOverride) {
    const context = await loadAuthenticatedValidationContext(workspacePath, envOverride);
    if (!context) {
        return [];
    }
    const warnings = [];
    for (const task of tasks) {
        if ((task.kind !== 'app' && task.kind !== 'asset' && task.kind !== 'asset-pack')
            || task.tags.length > 0) {
            continue;
        }
        const creatorResult = (0, upload_content_1.getTaskCreatorResult)(task, context.username, context.currentUserRole);
        if (creatorResult.creatorTargetError) {
            continue;
        }
        try {
            if (task.kind === 'app') {
                const response = await context.client.fetchAppBySlug(creatorResult.taskCreator, task.name);
                const liveTagCount = Array.isArray(response.app.tags) ? response.app.tags.length : 0;
                if (liveTagCount > 0) {
                    warnings.push(formatLiveTagClearWarning({
                        entityLabel: 'App',
                        displayName: `${creatorResult.taskCreator}/${task.name}`,
                        liveTagCount,
                    }));
                }
                continue;
            }
            if (task.kind === 'asset') {
                const response = await context.client.fetchAssetBySlug(creatorResult.taskCreator, task.name);
                const liveTagCount = Array.isArray(response.asset.tags) ? response.asset.tags.length : 0;
                if (liveTagCount > 0) {
                    warnings.push(formatLiveTagClearWarning({
                        entityLabel: 'Asset',
                        displayName: `${creatorResult.taskCreator}/${task.name}`,
                        liveTagCount,
                    }));
                }
                continue;
            }
            const response = await context.client.fetchAssetPackBySlug(creatorResult.taskCreator, task.name);
            const liveTagCount = Array.isArray(response.pack.tags) ? response.pack.tags.length : 0;
            if (liveTagCount > 0) {
                warnings.push(formatLiveTagClearWarning({
                    entityLabel: 'Asset pack',
                    displayName: `${creatorResult.taskCreator}/${task.name}`,
                    liveTagCount,
                }));
            }
        }
        catch (error) {
            if (error instanceof types_1.ApiError && (error.status === 404 || error.status === 401 || error.status === 403)) {
                continue;
            }
        }
    }
    return warnings;
}
async function validate(pathOrName, options = {}) {
    const selection = (0, taskSelection_1.selectTasks)(pathOrName);
    const workspacePath = resolveValidationWorkspacePath(pathOrName, selection.resolution);
    const resolvedValidationContext = await (0, commandContext_1.resolveOptionalEnvironmentContext)('project validate', {
        env: options.env,
        workspacePath,
    });
    if (resolvedValidationContext) {
        printResolvedValidationAccount(resolvedValidationContext);
    }
    if (selection.errors.length > 0) {
        (0, taskSelection_1.reportTaskErrors)(selection.errors, 'project validate');
        process.exitCode = 1;
        return;
    }
    const externalAssetPackErrors = await (0, externalAssetPackValidation_1.validateExternalAssetPackTasks)(selection.tasks);
    if (externalAssetPackErrors.length > 0) {
        (0, taskSelection_1.reportTaskErrors)(externalAssetPackErrors.map((message) => ({
            type: 'invalid-catalogue',
            message,
            help: ['Update the asset pack downloadUrl to a public hostname that resolves successfully, then try again.'],
        })), 'project validate');
        process.exitCode = 1;
        return;
    }
    const tasks = selection.tasks;
    const warnings = selection.warnings;
    if (tasks.length === 0) {
        (0, uploadLog_1.printTaskSummary)([], warnings, { action: 'validate' });
        return;
    }
    const results = [];
    const assetSpecTasks = tasks.filter((task) => task.kind === 'asset-spec');
    const assetSpecLookups = buildLocalAssetSpecLookups(assetSpecTasks);
    const sortedTasks = (0, taskUtils_1.sortTasks)(tasks);
    const hostedLaunchValidationRequired = sortedTasks.some((task) => task.kind === 'app' && task.hostingMode !== 'EXTERNAL' && !task.externalUrl);
    const hostedLaunchValidationContext = hostedLaunchValidationRequired
        ? await loadHostedLaunchValidationContext(workspacePath, options.env)
        : null;
    if (hostedLaunchValidationRequired && !hostedLaunchValidationContext) {
        return;
    }
    for (const task of sortedTasks) {
        const entityId = task.name;
        try {
            if (task.kind === 'app') {
                await (0, apps_1.validateAppTask)(task);
                (0, apps_1.collectAppValidationWarnings)(task, 'source').forEach((warning) => warnings.add(warning));
                if (task.hostingMode !== 'EXTERNAL' && !task.externalUrl) {
                    const launchContext = hostedLaunchValidationContext;
                    const creatorResult = (0, upload_content_1.getTaskCreatorResult)(task, launchContext.username, launchContext.currentUserRole);
                    if (creatorResult.creatorTargetError) {
                        throw new Error(creatorResult.creatorTargetError);
                    }
                    const launchCheck = await (0, apps_1.runLocalHostedLaunchCheck)({
                        client: launchContext.client,
                        apiBase: launchContext.apiBase,
                        webBase: launchContext.webBase,
                        creatorUsername: creatorResult.taskCreator,
                        task,
                        token: launchContext.token,
                        currentUser: launchContext.currentUser,
                    });
                    if (launchCheck.status !== 'PASSED') {
                        throw new Error((0, apps_1.formatHostedLaunchCheckFailure)(task.name, launchCheck, 'local'));
                    }
                }
            }
            else if (task.kind === 'asset-spec') {
                // Parsing already validates the contract, symbol, and documentation files.
            }
            else if (task.kind === 'asset') {
                validateAssetTask(task, assetSpecLookups);
            }
            else if (task.kind === 'owned-asset' || task.kind === 'asset-pack') {
                // Structural validation already ran during catalogue parsing.
            }
            else {
                throw new Error(`project validate does not support "${entityId}".`);
            }
            const entry = {
                action: 'validate',
                status: 'success',
                entityType: task.kind,
                entityId,
                catalogue: task.cataloguePath,
            };
            results.push(entry);
            console.log((0, uploadLog_1.formatTaskLogLine)(entry));
        }
        catch (error) {
            const message = (error?.message ?? String(error));
            const entry = {
                action: 'validate',
                status: 'error',
                entityType: task.kind,
                entityId,
                catalogue: task.cataloguePath,
                detail: message,
            };
            results.push(entry);
            console.log((0, uploadLog_1.formatTaskLogLine)(entry));
        }
    }
    const liveTagWarnings = await collectLiveTagClearWarnings(sortedTasks, workspacePath, options.env);
    liveTagWarnings.forEach((warning) => warnings.add(warning));
    (0, uploadLog_1.printTaskSummary)(results, warnings, { action: 'validate' });
}
