"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.expandFieldsWithSets = expandFieldsWithSets;
const errors_1 = require("../types/errors");
/**
 * Separates field references into inclusions and exclusions based on '-' prefix
 */
function separateInclusionsAndExclusions(fields) {
    const inclusions = [];
    const exclusions = new Set();
    for (const field of fields) {
        if (field.startsWith('-')) {
            exclusions.add(field.substring(1)); // Remove the '-' prefix
        }
        else {
            inclusions.push(field);
        }
    }
    return { inclusions, exclusions };
}
/**
 * Applies exclusions and removes duplicates while preserving order
 */
function applyExclusionsAndDeduplicate(expandedFields, exclusions) {
    const result = [];
    const seen = new Set();
    for (const field of expandedFields) {
        if (!exclusions.has(field) && !seen.has(field)) {
            result.push(field);
            seen.add(field);
        }
    }
    return result;
}
/**
 * Expands a single set, handling nested set references (up to 3 levels deep)
 * and applying any exclusions defined within the set
 */
function expandSingleSet(setName, setDefinition, currentTable, depth = 1) {
    if (depth > 3) {
        throw new errors_1.CompileError(`Set nesting limit reached. Only up to 3 levels of nesting are allowed.`);
    }
    // Separate inclusions and exclusions within this set definition
    const { inclusions, exclusions } = separateInclusionsAndExclusions(setDefinition.fields);
    const expandedFields = [];
    for (const setField of inclusions) {
        if (setField.endsWith('*')) {
            // Nested set reference - expand it directly
            const nestedSetName = setField.substring(0, setField.length - 1);
            // Check if nested set exists
            if (!currentTable.sets || !currentTable.sets[nestedSetName]) {
                const availableSets = currentTable.sets
                    ? Object.keys(currentTable.sets).join(', ')
                    : 'none';
                throw new errors_1.CompileError(`Nested set "${nestedSetName}" referenced by set "${setName}" not found in table "${currentTable.name}". Available sets: ${availableSets}`);
            }
            const nestedSetDef = currentTable.sets[nestedSetName];
            // Check for circular reference (A references B, B references A)
            if (nestedSetDef.fields.some((f) => f.endsWith('*') &&
                f.substring(0, f.length - 1) === setName)) {
                throw new errors_1.CompileError(`Circular reference detected: set "${setName}" references set "${nestedSetName}", which references "${setName}"`);
            }
            // Expand the nested set (which will also handle its own exclusions)
            const nestedExpanded = expandSingleSet(nestedSetName, nestedSetDef, currentTable, depth + 1);
            expandedFields.push(...nestedExpanded);
        }
        else {
            // Regular field or cross-table reference
            expandedFields.push(setField);
        }
    }
    // Apply exclusions defined in this set to the expanded fields
    return applyExclusionsAndDeduplicate(expandedFields, exclusions);
}
/**
 * Expands all set references from the inclusions list
 */
function expandSetReferences(inclusions, currentTable) {
    const expandedFields = [];
    for (const inclusion of inclusions) {
        if (inclusion.endsWith('*')) {
            // This is a set reference
            const setName = inclusion.substring(0, inclusion.length - 1);
            // Check if set exists
            if (!currentTable.sets || !currentTable.sets[setName]) {
                const availableSets = currentTable.sets
                    ? Object.keys(currentTable.sets).join(', ')
                    : 'none';
                throw new errors_1.CompileError(`Set "${setName}" not found in table "${currentTable.name}". Available sets: ${availableSets}`);
            }
            const setDefinition = currentTable.sets[setName];
            // Expand the set
            const setFields = expandSingleSet(setName, setDefinition, currentTable);
            expandedFields.push(...setFields);
        }
        else {
            // Regular field reference
            expandedFields.push(inclusion);
        }
    }
    return expandedFields;
}
/**
 * Expands field references that include set references (ending with *)
 * and applies exclusions (starting with -)
 *
 * Supports up to 3 levels of set nesting.
 *
 * @param fields - Array of field names, set references (ending with *), or exclusions (starting with -)
 * @param currentTable - The table containing the sets being expanded
 * @returns Expanded field list with exclusions applied and duplicates removed
 */
function expandFieldsWithSets(fields, currentTable) {
    const { inclusions, exclusions } = separateInclusionsAndExclusions(fields);
    const expandedFields = expandSetReferences(inclusions, currentTable);
    const result = applyExclusionsAndDeduplicate(expandedFields, exclusions);
    return result;
}
//# sourceMappingURL=fieldSetExpander.js.map