/**
 * Utility functions for template manipulation
 *
 * Path format with multi-section support:
 * - "s0" for section 0
 * - "s0.0" for group 0 in section 0
 * - "s0.0.1" for field 1 in group 0 in section 0
 * - "s0.0.1.2" for field 2 in nested group at s0.0.1
 */
/**
 * Deep clone an object
 */
export function deep_clone(obj) {
    return JSON.parse(JSON.stringify(obj));
}
/**
 * Check if data is FormSetData (multi-section format)
 */
export function is_form_set_data(data) {
    return (typeof data === 'object' &&
        data !== null &&
        'sections' in data &&
        Array.isArray(data.sections));
}
/**
 * Convert Section or FormSetData to FormSetData (normalize to multi-section)
 */
export function normalize_to_form_set_data(data) {
    if (is_form_set_data(data)) {
        return data;
    }
    // Single section - wrap in sections array
    // Move icons_behaviour from section to form-set level
    const section = data;
    const icons_behaviour = section.icons_behaviour;
    const section_without_icons = { ...section };
    delete section_without_icons.icons_behaviour;
    return {
        sections: [section_without_icons],
        icons_behaviour,
    };
}
/**
 * Get groups array from Section
 */
export function get_fields(section) {
    return section.groups ?? [];
}
/**
 * Set fields array on Section (uses groups)
 */
export function set_fields(section, fields) {
    return {
        ...section,
        groups: fields,
    };
}
/**
 * Check if a path is a section-only path (e.g., "s0", "s1")
 */
export function is_section_path(path) {
    return /^s\d+$/.test(path);
}
export function parse_path(path) {
    if (!path)
        return { section_index: -1, field_indices: [] };
    // New format: starts with "s" followed by section index
    if (path.startsWith('s')) {
        const parts = path.substring(1).split('.');
        const section_index = parseInt(parts[0], 10);
        const field_indices = parts.slice(1).map(Number);
        return { section_index, field_indices };
    }
    // Legacy format (backwards compatibility): "0.1.2" assumes section 0
    const indices = path.split('.').map(Number);
    return { section_index: 0, field_indices: indices };
}
/**
 * Build a path string from section index and field indices
 */
export function build_path(section_index, field_indices = []) {
    if (field_indices.length === 0) {
        return `s${section_index}`;
    }
    return `s${section_index}.${field_indices.join('.')}`;
}
/**
 * Get section by index from sections array
 */
export function get_section_by_index(sections, index) {
    if (index < 0 || index >= sections.length)
        return null;
    return sections[index];
}
/**
 * Get a field by path from sections array
 */
export function get_field_by_path(sections, path) {
    const { section_index, field_indices } = parse_path(path);
    if (section_index < 0 || section_index >= sections.length)
        return null;
    // If no field indices, this is a section path - return null (not a field)
    if (field_indices.length === 0)
        return null;
    const section = sections[section_index];
    let fields = get_fields(section);
    let field = null;
    for (let i = 0; i < field_indices.length; i++) {
        const index = field_indices[i];
        if (!fields || index >= fields.length)
            return null;
        field = fields[index];
        if (i < field_indices.length - 1) {
            // Navigate to fields for next iteration
            fields = field.fields ?? [];
        }
    }
    return field;
}
/**
 * Update a field at a given path
 */
export function update_field_at_path(sections, path, updates) {
    const cloned = deep_clone(sections);
    const { section_index, field_indices } = parse_path(path);
    if (section_index < 0 || section_index >= cloned.length)
        return cloned;
    if (field_indices.length === 0)
        return cloned; // Can't update section as a field
    const section = cloned[section_index];
    let fields = get_fields(section);
    let parent_fields = fields;
    let field_index = field_indices[0];
    for (let i = 0; i < field_indices.length - 1; i++) {
        const index = field_indices[i];
        if (!fields || index >= fields.length)
            return cloned;
        const field = fields[index];
        parent_fields = fields;
        fields = field.fields ?? [];
        field_index = field_indices[i + 1];
    }
    if (field_index < fields.length) {
        fields[field_index] = { ...fields[field_index], ...updates };
    }
    // Re-assign groups if at section root level
    if (field_indices.length === 1) {
        section.groups = fields;
    }
    return cloned;
}
/**
 * Update a section at a given index
 */
export function update_section_at_index(sections, section_index, updates) {
    const cloned = deep_clone(sections);
    if (section_index < 0 || section_index >= cloned.length)
        return cloned;
    cloned[section_index] = { ...cloned[section_index], ...updates };
    return cloned;
}
/**
 * Add a field at a given path
 * If parent_path is null, cannot add (must specify at least a section)
 * If parent_path is a section path (e.g., "s0"), adds to that section's groups
 * If parent_path is a group path (e.g., "s0.0"), adds to that group's fields
 */
export function add_field_at_path(sections, parent_path, field, position) {
    console.log('[add_field_at_path] Adding field:', field.label || field.id, 'to parent_path:', parent_path, 'position:', position);
    const cloned = deep_clone(sections);
    if (parent_path === null) {
        console.log('[add_field_at_path] FAILED - parent_path is null, cannot add without section context');
        return cloned;
    }
    const { section_index, field_indices } = parse_path(parent_path);
    if (section_index < 0 || section_index >= cloned.length) {
        console.log('[add_field_at_path] FAILED - invalid section index:', section_index);
        return cloned;
    }
    const section = cloned[section_index];
    if (field_indices.length === 0) {
        // Adding to section root (groups array)
        const fields = get_fields(section);
        if (position !== undefined) {
            fields.splice(position, 0, field);
        }
        else {
            fields.push(field);
        }
        section.groups = fields;
        console.log('[add_field_at_path] Added to section root, new groups count:', section.groups?.length);
    }
    else {
        // Adding to a group's fields
        const parent = get_field_by_path(cloned, parent_path);
        console.log('[add_field_at_path] Found parent:', parent?.label, 'field_type:', parent?.field_type);
        if (parent && parent.field_type === 'group') {
            if (!parent.fields)
                parent.fields = [];
            if (position !== undefined) {
                parent.fields.splice(position, 0, field);
            }
            else {
                parent.fields.push(field);
            }
            console.log('[add_field_at_path] Added to group, new fields count:', parent.fields.length);
        }
        else {
            console.log('[add_field_at_path] FAILED - parent not found or not a group!');
        }
    }
    return cloned;
}
/**
 * Add a new section
 */
export function add_section(sections, section, position) {
    const cloned = deep_clone(sections);
    if (position !== undefined) {
        cloned.splice(position, 0, section);
    }
    else {
        cloned.push(section);
    }
    return cloned;
}
/**
 * Delete a section at a given index
 */
export function delete_section_at_index(sections, section_index) {
    const cloned = deep_clone(sections);
    if (section_index < 0 || section_index >= cloned.length)
        return cloned;
    cloned.splice(section_index, 1);
    return cloned;
}
/**
 * Delete a field at a given path
 */
export function delete_field_at_path(sections, path) {
    const cloned = deep_clone(sections);
    const { section_index, field_indices } = parse_path(path);
    if (section_index < 0 || section_index >= cloned.length)
        return cloned;
    if (field_indices.length === 0) {
        // This is a section deletion - use delete_section_at_index instead
        return delete_section_at_index(cloned, section_index);
    }
    const section = cloned[section_index];
    if (field_indices.length === 1) {
        // Root level deletion within section
        const fields = get_fields(section);
        fields.splice(field_indices[0], 1);
        section.groups = fields;
    }
    else {
        // Nested deletion - get parent and remove from fields
        const parent_indices = field_indices.slice(0, -1);
        const parent_path = build_path(section_index, parent_indices);
        const parent = get_field_by_path(cloned, parent_path);
        if (parent && parent.fields) {
            parent.fields.splice(field_indices[field_indices.length - 1], 1);
        }
    }
    return cloned;
}
/**
 * Duplicate a field at a given path
 */
export function duplicate_field_at_path(sections, path, new_id_suffix = '_copy') {
    const field = get_field_by_path(sections, path);
    if (!field)
        return sections;
    const cloned_field = deep_clone(field);
    cloned_field.id = `${field.id}${new_id_suffix}`;
    cloned_field.label = `${field.label} (Copy)`;
    // Update IDs for nested fields if it's a group
    if (cloned_field.field_type === 'group' && cloned_field.fields) {
        update_nested_ids(cloned_field.fields, new_id_suffix);
    }
    const { section_index, field_indices } = parse_path(path);
    const position = field_indices[field_indices.length - 1] + 1;
    const parent_indices = field_indices.slice(0, -1);
    const parent_path = parent_indices.length > 0
        ? build_path(section_index, parent_indices)
        : `s${section_index}`;
    return add_field_at_path(sections, parent_path, cloned_field, position);
}
/**
 * Update IDs for nested fields
 */
function update_nested_ids(fields, suffix) {
    for (const field of fields) {
        field.id = `${field.id}${suffix}`;
        if (field.field_type === 'group' && field.fields) {
            update_nested_ids(field.fields, suffix);
        }
    }
}
/**
 * Adjust target path after deletion if needed
 * When source path comes before target path in the same parent, indices shift
 */
function adjust_path_after_delete(from_path, to_path) {
    const from_parsed = parse_path(from_path);
    const to_parsed = parse_path(to_path);
    // Must be in same section
    if (from_parsed.section_index !== to_parsed.section_index)
        return to_path;
    const from_indices = from_parsed.field_indices;
    const to_indices = to_parsed.field_indices;
    // Check if paths share the same parent
    const from_parent = from_indices.slice(0, -1);
    const to_parent = to_indices.slice(0, -1);
    // If they don't have the same parent, no adjustment needed
    if (from_parent.length !== to_parent.length)
        return to_path;
    for (let i = 0; i < from_parent.length; i++) {
        if (from_parent[i] !== to_parent[i])
            return to_path;
    }
    // Same parent - check if from comes before to
    const from_index = from_indices[from_indices.length - 1];
    const to_index = to_indices[to_indices.length - 1];
    if (from_index < to_index) {
        // After deleting from_index, to_index shifts down by 1
        const adjusted_indices = [...to_indices];
        adjusted_indices[adjusted_indices.length - 1] = to_index - 1;
        return build_path(to_parsed.section_index, adjusted_indices);
    }
    return to_path;
}
/**
 * Move a field from one path to another
 */
export function move_field(sections, from_path, to_path, position) {
    console.log('[move_field] Starting move:', { from_path, to_path, position });
    const field = get_field_by_path(sections, from_path);
    if (!field) {
        console.log('[move_field] Field not found at from_path');
        return sections;
    }
    console.log('[move_field] Found field to move:', field.label || field.id);
    // Clone the field
    const cloned_field = deep_clone(field);
    // Delete from original position
    let result = delete_field_at_path(sections, from_path);
    console.log('[move_field] After delete, sections[0] groups count:', result[0]?.groups?.length);
    // Adjust target path if source was before target in same parent
    const adjusted_to_path = adjust_path_after_delete(from_path, to_path);
    console.log('[move_field] Adjusted to_path:', adjusted_to_path, '(was:', to_path, ')');
    // Determine where to insert
    const to_parsed = parse_path(adjusted_to_path);
    if (position === 'inside') {
        // Insert as child of target (target must be a group or section)
        console.log('[move_field] Inserting inside target at path:', adjusted_to_path);
        if (is_section_path(adjusted_to_path)) {
            // Dropping inside a section - add to its groups
            result = add_field_at_path(result, adjusted_to_path, cloned_field);
        }
        else {
            const target_before = get_field_by_path(result, adjusted_to_path);
            console.log('[move_field] Target before add:', target_before?.label, 'field_type:', target_before?.field_type);
            result = add_field_at_path(result, adjusted_to_path, cloned_field);
            const target_after = get_field_by_path(result, adjusted_to_path);
            console.log('[move_field] Target after add:', target_after?.label, 'fields count:', target_after?.fields?.length);
        }
    }
    else {
        // Insert before or after the target
        const { section_index, field_indices } = to_parsed;
        let parent_path;
        let insert_position;
        if (field_indices.length === 0) {
            // Target is a section - drop before/after in section list (not supported for fields)
            console.log('[move_field] Target is a section, dropping after section');
            parent_path = `s${section_index}`;
            insert_position = 0;
        }
        else if (field_indices.length === 1) {
            // Target is at section root
            parent_path = `s${section_index}`;
            const target_index = field_indices[0];
            insert_position = position === 'after' ? target_index + 1 : target_index;
        }
        else {
            // Target is nested
            parent_path = build_path(section_index, field_indices.slice(0, -1));
            const target_index = field_indices[field_indices.length - 1];
            insert_position = position === 'after' ? target_index + 1 : target_index;
        }
        console.log('[move_field] Inserting', position, 'at parent:', parent_path, 'position:', insert_position);
        result = add_field_at_path(result, parent_path, cloned_field, insert_position);
    }
    return result;
}
/**
 * Move a section from one index to another
 */
export function move_section(sections, from_index, to_index) {
    if (from_index === to_index)
        return sections;
    if (from_index < 0 || from_index >= sections.length)
        return sections;
    if (to_index < 0 || to_index >= sections.length)
        return sections;
    const cloned = deep_clone(sections);
    const [section] = cloned.splice(from_index, 1);
    cloned.splice(to_index, 0, section);
    return cloned;
}
/**
 * Build tree nodes from sections array
 */
export function build_tree_nodes(sections) {
    return sections.map((section, section_index) => build_section_node(section, section_index));
}
/**
 * Build a section tree node
 */
function build_section_node(section, section_index) {
    const path = `s${section_index}`;
    const fields = get_fields(section);
    const node = {
        id: `section_${section_index}`,
        type: 'section',
        label: section.section_name || `Section ${section_index + 1}`,
        section,
        path,
        children: fields.map((field, index) => build_tree_node(field, `${path}.${index}`, `section_${section_index}`)),
    };
    return node;
}
/**
 * Build a single tree node for a field
 */
function build_tree_node(field, path, parent_id) {
    const type = field.field_type === 'group' ? 'group' : 'field';
    const node = {
        id: field.id,
        type,
        label: field.label || field.id || 'Untitled',
        field_config: field,
        path,
        parent_id: parent_id ?? undefined,
    };
    if (field.field_type === 'group' && field.fields) {
        node.children = field.fields.map((sub_field, index) => build_tree_node(sub_field, `${path}.${index}`, field.id));
    }
    return node;
}
/**
 * Get all field IDs from sections array (for dependency validation)
 */
export function get_all_field_ids(sections) {
    const ids = [];
    function collect_ids(groups) {
        for (const field of groups) {
            if (field.id)
                ids.push(field.id);
            if (field.field_type === 'group' && field.fields) {
                collect_ids(field.fields);
            }
        }
    }
    for (const section of sections) {
        const fields = get_fields(section);
        collect_ids(fields);
    }
    return ids;
}
/**
 * Find a field by ID (returns path)
 */
export function find_field_path_by_id(sections, id) {
    for (let section_index = 0; section_index < sections.length; section_index++) {
        const section = sections[section_index];
        const fields = get_fields(section);
        function search(groups, base_path) {
            for (let i = 0; i < groups.length; i++) {
                const field = groups[i];
                const path = base_path ? `${base_path}.${i}` : `s${section_index}.${i}`;
                if (field.id === id)
                    return path;
                if (field.field_type === 'group' && field.fields) {
                    const found = search(field.fields, path);
                    if (found)
                        return found;
                }
            }
            return null;
        }
        const found = search(fields, '');
        if (found)
            return found;
    }
    return null;
}
/**
 * Generate a unique field ID
 */
export function generate_field_id(sections, base = 'field') {
    const existing_ids = new Set(get_all_field_ids(sections));
    let counter = 1;
    let id = base;
    while (existing_ids.has(id)) {
        id = `${base}_${counter}`;
        counter++;
    }
    return id;
}
// Legacy helpers for backwards compatibility with single-section code
/**
 * Get template name from first section
 * @deprecated Use sections[0].section_name directly
 */
export function get_template_name(sections) {
    if (sections.length === 0)
        return 'Untitled';
    return sections[0].section_name ?? 'Untitled';
}
/**
 * Set template name on first section
 * @deprecated Use update_section_at_index directly
 */
export function set_template_name(sections, name) {
    if (sections.length === 0)
        return sections;
    return update_section_at_index(sections, 0, { section_name: name });
}
//# sourceMappingURL=template_utils.js.map