"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSkillDisplayContract = validateSkillDisplayContract;
exports.formatDisplayContractIssue = formatDisplayContractIssue;
exports.sanitizeDisplayConfigForRuntime = sanitizeDisplayConfigForRuntime;
const dataContract_1 = require("../../types/dataContract");
const VALID_TIME_UNITS = ['ns', 'us', 'ms', 's'];
const VALID_WIDTHS = ['narrow', 'medium', 'wide', 'auto'];
function pushIssue(issues, skillName, field, path, message, value, options, stepId) {
    issues.push({
        skillName,
        stepId,
        field,
        path,
        message,
        value,
        filePath: options.filePath,
    });
}
function validateColumnDefinition(issues, skillName, stepId, column, path, options) {
    if (typeof column === 'string') {
        if (column.length === 0) {
            pushIssue(issues, skillName, path, path, 'Column shorthand must be a non-empty string', column, options, stepId);
        }
        return;
    }
    if (!column || typeof column !== 'object' || Array.isArray(column)) {
        pushIssue(issues, skillName, path, path, 'Column definition must be an object', column, options, stepId);
        return;
    }
    const col = column;
    if (!col.name || typeof col.name !== 'string') {
        pushIssue(issues, skillName, `${path}.name`, `${path}.name`, 'Column name is required and must be a string', col.name, options, stepId);
    }
    if (col.type && !dataContract_1.VALID_COLUMN_TYPES.includes(col.type)) {
        pushIssue(issues, skillName, `${path}.type`, `${path}.type`, `Invalid column type. Valid values: ${dataContract_1.VALID_COLUMN_TYPES.join(', ')}`, col.type, options, stepId);
    }
    if (col.format && !dataContract_1.VALID_COLUMN_FORMATS.includes(col.format)) {
        pushIssue(issues, skillName, `${path}.format`, `${path}.format`, `Invalid column format. Valid values: ${dataContract_1.VALID_COLUMN_FORMATS.join(', ')}`, col.format, options, stepId);
    }
    if (col.clickAction && !dataContract_1.VALID_CLICK_ACTIONS.includes(col.clickAction)) {
        pushIssue(issues, skillName, `${path}.clickAction`, `${path}.clickAction`, `Invalid click action. Valid values: ${dataContract_1.VALID_CLICK_ACTIONS.join(', ')}`, col.clickAction, options, stepId);
    }
    if (col.unit && !VALID_TIME_UNITS.includes(col.unit)) {
        pushIssue(issues, skillName, `${path}.unit`, `${path}.unit`, `Invalid time unit. Valid values: ${VALID_TIME_UNITS.join(', ')}`, col.unit, options, stepId);
    }
    if (col.width !== undefined && typeof col.width !== 'number' && !VALID_WIDTHS.includes(col.width)) {
        pushIssue(issues, skillName, `${path}.width`, `${path}.width`, `Invalid width. Must be a number or one of: ${VALID_WIDTHS.join(', ')}`, col.width, options, stepId);
    }
}
function validateDisplayConfig(issues, skillName, display, path, options, stepId) {
    if (!display || typeof display !== 'object' || Array.isArray(display))
        return;
    const config = display;
    if (config.layer && !(0, dataContract_1.isValidDisplayLayer)(String(config.layer))) {
        pushIssue(issues, skillName, `${path}.layer`, `${path}.layer`, `Invalid layer value. Valid values: ${dataContract_1.VALID_DISPLAY_LAYERS.join(', ')}`, config.layer, options, stepId);
    }
    if (config.level && !dataContract_1.VALID_DISPLAY_LEVELS.includes(config.level)) {
        pushIssue(issues, skillName, `${path}.level`, `${path}.level`, `Invalid level value. Valid values: ${dataContract_1.VALID_DISPLAY_LEVELS.join(', ')}`, config.level, options, stepId);
    }
    if (config.format && !dataContract_1.VALID_DISPLAY_FORMATS.includes(config.format)) {
        pushIssue(issues, skillName, `${path}.format`, `${path}.format`, `Invalid format value. Valid values: ${dataContract_1.VALID_DISPLAY_FORMATS.join(', ')}`, config.format, options, stepId);
    }
    if (config.columns !== undefined) {
        if (!Array.isArray(config.columns)) {
            pushIssue(issues, skillName, `${path}.columns`, `${path}.columns`, 'Display columns must be an array', config.columns, options, stepId);
        }
        else {
            config.columns.forEach((column, index) => {
                validateColumnDefinition(issues, skillName, stepId, column, `${path}.columns[${index}]`, options);
            });
        }
    }
    if (config.metadataFields !== undefined) {
        if (!Array.isArray(config.metadataFields)) {
            pushIssue(issues, skillName, `${path}.metadataFields`, `${path}.metadataFields`, 'metadataFields must be an array of strings', config.metadataFields, options, stepId);
        }
        else {
            config.metadataFields.forEach((field, index) => {
                if (typeof field !== 'string') {
                    pushIssue(issues, skillName, `${path}.metadataFields[${index}]`, `${path}.metadataFields[${index}]`, 'Each metadataField must be a string', field, options, stepId);
                }
            });
        }
    }
}
function validateStepDisplay(issues, skillName, step, path, options) {
    if (!step || typeof step !== 'object' || Array.isArray(step))
        return;
    const stepObj = step;
    const stepId = typeof stepObj.id === 'string' ? stepObj.id : undefined;
    validateDisplayConfig(issues, skillName, stepObj.display, `${path}.display`, options, stepId);
    if (Array.isArray(stepObj.steps)) {
        stepObj.steps.forEach((nestedStep, index) => {
            validateStepDisplay(issues, skillName, nestedStep, `${path}.steps[${index}]`, options);
        });
    }
    if (stepObj.then) {
        validateStepDisplay(issues, skillName, stepObj.then, `${path}.then`, options);
    }
    if (Array.isArray(stepObj.conditions)) {
        stepObj.conditions.forEach((condition, index) => {
            if (!condition || typeof condition !== 'object')
                return;
            const thenStep = condition.then;
            validateStepDisplay(issues, skillName, thenStep, `${path}.conditions[${index}].then`, options);
        });
    }
    if (stepObj.else) {
        validateStepDisplay(issues, skillName, stepObj.else, `${path}.else`, options);
    }
}
function validateSkillDisplayContract(skill, options = {}) {
    const issues = [];
    const skillName = typeof skill?.name === 'string' ? skill.name : 'unknown_skill';
    const seenDisplays = new WeakSet();
    const validateOnce = (display, path) => {
        if (!display || typeof display !== 'object' || Array.isArray(display))
            return;
        if (seenDisplays.has(display))
            return;
        seenDisplays.add(display);
        validateDisplayConfig(issues, skillName, display, path, options);
    };
    validateOnce(skill.display, 'display');
    validateOnce(skill.output?.display, 'output.display');
    if (Array.isArray(skill.steps)) {
        skill.steps.forEach((step, index) => {
            validateStepDisplay(issues, skillName, step, `steps[${index}]`, options);
        });
    }
    return issues;
}
function formatDisplayContractIssue(issue) {
    const subject = issue.stepId ? `${issue.skillName}.${issue.stepId}` : issue.skillName;
    return `${subject}: ${issue.path} - ${issue.message} (value: ${String(issue.value)})`;
}
function sanitizeDisplayConfigForRuntime(config, context = {}) {
    const sanitized = { ...config };
    const issues = [];
    const skillName = context.skillName || 'runtime_display';
    const stepId = context.stepId;
    const options = {};
    if (sanitized.layer && !(0, dataContract_1.isValidDisplayLayer)(String(sanitized.layer))) {
        pushIssue(issues, skillName, 'display.layer', 'display.layer', `Invalid layer value. Valid values: ${dataContract_1.VALID_DISPLAY_LAYERS.join(', ')}`, sanitized.layer, options, stepId);
        sanitized.layer = context.defaultLayer || 'list';
    }
    if (sanitized.level && !dataContract_1.VALID_DISPLAY_LEVELS.includes(sanitized.level)) {
        pushIssue(issues, skillName, 'display.level', 'display.level', `Invalid level value. Valid values: ${dataContract_1.VALID_DISPLAY_LEVELS.join(', ')}`, sanitized.level, options, stepId);
        sanitized.level = context.defaultLevel || 'detail';
    }
    if (sanitized.format && !dataContract_1.VALID_DISPLAY_FORMATS.includes(sanitized.format)) {
        pushIssue(issues, skillName, 'display.format', 'display.format', `Invalid format value. Valid values: ${dataContract_1.VALID_DISPLAY_FORMATS.join(', ')}`, sanitized.format, options, stepId);
        sanitized.format = context.defaultFormat || 'table';
    }
    if (!sanitized.level)
        sanitized.level = context.defaultLevel || 'summary';
    if (!sanitized.format)
        sanitized.format = context.defaultFormat || 'table';
    if (sanitized.columns !== undefined) {
        if (!Array.isArray(sanitized.columns)) {
            pushIssue(issues, skillName, 'display.columns', 'display.columns', 'Display columns must be an array', sanitized.columns, options, stepId);
            delete sanitized.columns;
        }
        else {
            const cleanColumns = [];
            sanitized.columns.forEach((column, index) => {
                const path = `display.columns[${index}]`;
                if (typeof column === 'string') {
                    if (column.length > 0) {
                        cleanColumns.push({ name: column });
                    }
                    else {
                        pushIssue(issues, skillName, path, path, 'Column shorthand must be a non-empty string', column, options, stepId);
                    }
                    return;
                }
                if (!column || typeof column !== 'object' || Array.isArray(column)) {
                    pushIssue(issues, skillName, path, path, 'Column definition must be an object', column, options, stepId);
                    return;
                }
                const rawColumn = column;
                if (!rawColumn.name || typeof rawColumn.name !== 'string') {
                    pushIssue(issues, skillName, `${path}.name`, `${path}.name`, 'Column name is required and must be a string', rawColumn.name, options, stepId);
                    return;
                }
                const cleanColumn = { ...rawColumn };
                if (cleanColumn.type && !dataContract_1.VALID_COLUMN_TYPES.includes(cleanColumn.type)) {
                    pushIssue(issues, skillName, `${path}.type`, `${path}.type`, `Invalid column type. Valid values: ${dataContract_1.VALID_COLUMN_TYPES.join(', ')}`, cleanColumn.type, options, stepId);
                    delete cleanColumn.type;
                }
                if (cleanColumn.format && !dataContract_1.VALID_COLUMN_FORMATS.includes(cleanColumn.format)) {
                    pushIssue(issues, skillName, `${path}.format`, `${path}.format`, `Invalid column format. Valid values: ${dataContract_1.VALID_COLUMN_FORMATS.join(', ')}`, cleanColumn.format, options, stepId);
                    delete cleanColumn.format;
                }
                if (cleanColumn.clickAction && !dataContract_1.VALID_CLICK_ACTIONS.includes(cleanColumn.clickAction)) {
                    pushIssue(issues, skillName, `${path}.clickAction`, `${path}.clickAction`, `Invalid click action. Valid values: ${dataContract_1.VALID_CLICK_ACTIONS.join(', ')}`, cleanColumn.clickAction, options, stepId);
                    delete cleanColumn.clickAction;
                }
                if (cleanColumn.unit && !VALID_TIME_UNITS.includes(cleanColumn.unit)) {
                    pushIssue(issues, skillName, `${path}.unit`, `${path}.unit`, `Invalid time unit. Valid values: ${VALID_TIME_UNITS.join(', ')}`, cleanColumn.unit, options, stepId);
                    delete cleanColumn.unit;
                }
                if (cleanColumn.width !== undefined && typeof cleanColumn.width !== 'number' && !VALID_WIDTHS.includes(cleanColumn.width)) {
                    pushIssue(issues, skillName, `${path}.width`, `${path}.width`, `Invalid width. Must be a number or one of: ${VALID_WIDTHS.join(', ')}`, cleanColumn.width, options, stepId);
                    delete cleanColumn.width;
                }
                cleanColumns.push(cleanColumn);
            });
            sanitized.columns = cleanColumns;
        }
    }
    if (sanitized.metadataFields !== undefined) {
        if (!Array.isArray(sanitized.metadataFields)) {
            pushIssue(issues, skillName, 'display.metadataFields', 'display.metadataFields', 'metadataFields must be an array of strings', sanitized.metadataFields, options, stepId);
            delete sanitized.metadataFields;
        }
        else {
            sanitized.metadataFields = sanitized.metadataFields.filter((field, index) => {
                if (typeof field === 'string')
                    return true;
                pushIssue(issues, skillName, `display.metadataFields[${index}]`, `display.metadataFields[${index}]`, 'Each metadataField must be a string', field, options, stepId);
                return false;
            });
        }
    }
    return { config: sanitized, issues };
}
//# sourceMappingURL=displayContractValidator.js.map