import { schemaManager } from './schemaManager.js';
import { relationBuilder } from './relationUtils.js';
/**
 * Field Expansion Utility
 *
 * Matches Sequelize implementation 1:1
 */
export class FieldExpansionUtil {
    /**
     * Get column names for a collection
     * Uses schemaDefinitions to get field names since $inferSelect doesn't work for dynamic schemas
     */
    static getColumnNames(collectionName) {
        // First try to get from schema definition (JSON schema)
        const schemaDef = schemaManager.schemaDefinitions?.get(collectionName);
        if (schemaDef && schemaDef.schema && schemaDef.schema.fields) {
            // Filter out pure relation fields (those with relType but no type)
            return Object.entries(schemaDef.schema.fields)
                .filter(([_, fieldSchema]) => !fieldSchema.relType || fieldSchema.type)
                .map(([fieldName]) => fieldName);
        }
        // Fallback to $inferSelect (works for pre-defined schemas)
        const table = schemaManager.getTable(collectionName);
        if (table.$inferSelect) {
            return Object.keys(table.$inferSelect);
        }
        return [];
    }
    /**
     * Expand field selectors like ["*", "author.*", "-password"]
     */
    static expandFields(fields, collectionName) {
        let expandedFields = [];
        try {
            const tableColumns = this.getColumnNames(collectionName);
            for (const field of fields) {
                if (field === '*') {
                    // Wildcard - add all direct fields
                    expandedFields.push(...tableColumns);
                }
                else if (field.includes('*')) {
                    // Wildcard field like "author.*"
                    this.expandWildcardField(field, collectionName, expandedFields);
                }
                else {
                    // Direct field
                    expandedFields.push(field);
                }
            }
            // Remove duplicates
            expandedFields = [...new Set(expandedFields)];
            // Remove redundant fields
            return this.removeRedundantFields(expandedFields);
        }
        catch (error) {
            console.error(`Error expanding fields for ${collectionName}:`, error);
            return fields;
        }
    }
    /**
     * Expand wildcard field patterns
     */
    static expandWildcardField(field, collectionName, expandedFields, prefix = '') {
        const parts = field.split('.');
        try {
            let currentCollection = collectionName;
            let currentPrefix = prefix;
            for (let i = 0; i < parts.length; i++) {
                const part = parts[i];
                if (part === '*') {
                    // Get all fields at this level using our helper
                    const tableColumns = this.getColumnNames(currentCollection);
                    if (i === parts.length - 1) {
                        // Last part is wildcard - expand all fields at this level
                        expandedFields.push(...tableColumns.map((attr) => `${currentPrefix}${attr}`));
                    }
                    else {
                        // Intermediate wildcard - expand fields and recurse for associations
                        expandedFields.push(...tableColumns.map((attr) => `${currentPrefix}${attr}`));
                        // Expand associations
                        const associations = relationBuilder.getAssociations(currentCollection);
                        if (associations) {
                            for (const [associationName, association] of Object.entries(associations)) {
                                this.expandWildcardField(parts.slice(i + 1).join('.'), association.model, expandedFields, `${currentPrefix}${associationName}.`);
                            }
                        }
                    }
                    break;
                }
                else {
                    // Check if it's a field or association
                    const tableColumns = this.getColumnNames(currentCollection);
                    const associations = relationBuilder.getAssociations(currentCollection);
                    const association = associations?.[part];
                    // When there are remaining parts to traverse (not the last part),
                    // prioritize association lookup over column lookup.
                    // This fixes cases where a name is both a column (FK) and an association,
                    // e.g., "question" in exam_attempt_question is both a FK column and
                    // a relation — "exam_attempt_question.question.*" should follow the relation.
                    const hasRemainingParts = i < parts.length - 1;
                    if (hasRemainingParts && association) {
                        // Treat as association — navigate into it
                        currentPrefix += part + '.';
                        currentCollection = association.model;
                    }
                    else if (tableColumns.includes(part)) {
                        // It's a field (or last part matching a column)
                        expandedFields.push(currentPrefix + part);
                        break;
                    }
                    else if (association) {
                        currentPrefix += part + '.';
                        currentCollection = association.model;
                        if (i === parts.length - 1) {
                            // Last part is an association - include all its fields
                            const assocColumns = this.getColumnNames(currentCollection);
                            expandedFields.push(...assocColumns.map((attr) => `${currentPrefix}${attr}`));
                        }
                    }
                    else {
                        // Not a field or association - add as-is (might be computed field)
                        expandedFields.push(currentPrefix + part);
                        break;
                    }
                }
            }
        }
        catch (error) {
            console.error(`Error expanding wildcard field ${field}:`, error);
        }
    }
    /**
     * Remove redundant fields (e.g., if we have "author" and "author.name", keep only "author")
     */
    static removeRedundantFields(fields) {
        return fields.filter((field, index, self) => index === self.findIndex((t) => t === field || t.startsWith(`${field}.`)));
    }
    /**
     * Check if a field should be included based on selectors
     */
    static shouldIncludeField(field, selectors) {
        // If wildcard is present, include all fields unless explicitly excluded
        if (selectors.includes('*')) {
            // Check for exclusions (fields starting with -)
            const exclusions = selectors.filter((s) => s.startsWith('-')).map((s) => s.substring(1));
            return !exclusions.includes(field);
        }
        // Check if field is in selectors
        return selectors.includes(field);
    }
}
export default FieldExpansionUtil;
//# sourceMappingURL=fieldExpansion.js.map