/**
 * Schema Operations Module
 * Handles: Edit, delete, toggle, duplicate operations
 */
(function($) {
    'use strict';

    window.SchemafyOperations = {
        /**
         * Map schema types to builder card types
         * Some schema types use different names in the UI vs Schema.org
         */
        schemaTypeMap: {
            'FAQPage': 'FAQ',
            'VideoObject': 'Video',
            // Article subtypes
            'NewsArticle': 'Article', 'BlogPosting': 'Article',
            'AdvertiserContentArticle': 'Article', 'AnalysisNewsArticle': 'Article',
            'AskPublicNewsArticle': 'Article', 'BackgroundNewsArticle': 'Article',
            'MedicalScholarlyArticle': 'Article', 'ReportageNewsArticle': 'Article',
            'SatiricalArticle': 'Article', 'ScholarlyArticle': 'Article',
            'SocialMediaPosting': 'Article', 'TechArticle': 'Article',
            // LocalBusiness subtypes
            'AccountingService': 'LocalBusiness', 'AutoDealer': 'LocalBusiness',
            'AutoRental': 'LocalBusiness', 'AutoRepair': 'LocalBusiness',
            'AutoWash': 'LocalBusiness', 'AutomotiveBusiness': 'LocalBusiness',
            'Bakery': 'LocalBusiness', 'BeautySalon': 'LocalBusiness',
            'CafeOrCoffeeShop': 'LocalBusiness', 'ChildCare': 'LocalBusiness',
            'Dentist': 'LocalBusiness', 'Electrician': 'LocalBusiness',
            'EntertainmentBusiness': 'LocalBusiness', 'EventVenue': 'LocalBusiness',
            'ExerciseGym': 'LocalBusiness', 'FinancialService': 'LocalBusiness',
            'Florist': 'LocalBusiness', 'FoodEstablishment': 'LocalBusiness',
            'GeneralContractor': 'LocalBusiness', 'GovernmentOffice': 'LocalBusiness',
            'HealthAndBeautyBusiness': 'LocalBusiness', 'HomeAndConstructionBusiness': 'LocalBusiness',
            'HousePainter': 'LocalBusiness', 'HVACBusiness': 'LocalBusiness',
            'InsuranceAgency': 'LocalBusiness', 'LegalService': 'LocalBusiness',
            'Locksmith': 'LocalBusiness', 'LodgingBusiness': 'LocalBusiness',
            'MedicalBusiness': 'LocalBusiness', 'MovingCompany': 'LocalBusiness',
            'NailSalon': 'LocalBusiness', 'Optician': 'LocalBusiness',
            'Pharmacy': 'LocalBusiness', 'Physician': 'LocalBusiness',
            'Physiotherapist': 'LocalBusiness', 'Plumber': 'LocalBusiness',
            'ProfessionalService': 'LocalBusiness', 'RealEstateAgent': 'LocalBusiness',
            'Restaurant': 'LocalBusiness', 'RoofingContractor': 'LocalBusiness',
            'SportsActivityLocation': 'LocalBusiness', 'Store': 'LocalBusiness',
            'TaxiService': 'LocalBusiness', 'TravelAgency': 'LocalBusiness',
            'VeterinaryCare': 'LocalBusiness',
            // Organization subtypes
            'Airline': 'Organization', 'Consortium': 'Organization',
            'Corporation': 'Organization', 'EducationalOrganization': 'Organization',
            'GovernmentOrganization': 'Organization', 'LibrarySystem': 'Organization',
            'NGO': 'Organization', 'NewsMediaOrganization': 'Organization',
            'OnlineBusiness': 'Organization', 'PerformingGroup': 'Organization',
            'Project': 'Organization', 'ResearchOrganization': 'Organization',
            'SearchRescueOrganization': 'Organization', 'SportsOrganization': 'Organization',
            'WorkersUnion': 'Organization',
            // WebPage subtypes
            'AboutPage': 'WebPage', 'CheckoutPage': 'WebPage',
            'ContactPage': 'WebPage', 'WebSite': 'WebPage',
            'CollectionPage': 'WebPage', 'ImageGallery': 'WebPage',
            'ItemPage': 'WebPage', 'MedicalWebPage': 'WebPage',
            'ProfilePage': 'WebPage', 'QAPage': 'WebPage',
            'RealEstateListing': 'WebPage', 'SearchResultsPage': 'WebPage'
        },

        smartBuilderCardTypes: [
            'Article', 'Product', 'Organization', 'LocalBusiness', 'Person',
            'Event', 'Recipe', 'Review', 'FAQ', 'WebPage', 'Course', 'HowTo',
            'JobPosting', 'Service', 'Video', 'BreadcrumbList'
        ],

        getSchemaTypeRegistry: function() {
            if (typeof schemafy_ajax !== 'undefined' && schemafy_ajax.schema_type_registry) {
                return schemafy_ajax.schema_type_registry;
            }

            if (typeof schemafyBuilder !== 'undefined' && schemafyBuilder.schema_type_registry) {
                return schemafyBuilder.schema_type_registry;
            }

            return {};
        },

        getSchemaTypeMap: function() {
            const registry = this.getSchemaTypeRegistry();

            return registry.typeMap || this.schemaTypeMap;
        },

        getSmartBuilderCardTypes: function() {
            const registry = this.getSchemaTypeRegistry();

            return registry.smartBuilderCards || registry.builderCards || this.smartBuilderCardTypes;
        },

        getHybridFallbackType: function() {
            const registry = this.getSchemaTypeRegistry();

            return registry.hybridFallback || 'WebPage';
        },

        getSchemaTypeCandidates: function(typeValue, fallbackType) {
            let candidates = [];

            if (Array.isArray(typeValue)) {
                candidates = typeValue;
            } else if (typeof typeValue === 'string') {
                candidates = typeValue.indexOf(',') !== -1 ? typeValue.split(',') : [typeValue];
            }

            if (fallbackType && candidates.indexOf(fallbackType) === -1) {
                candidates.push(fallbackType);
            }

            const seenTypes = {};

            return candidates
                .map(function(type) {
                    return String(type || '').trim().replace(/^https?:\/\/schema\.org\//i, '');
                })
                .filter(function(type) {
                    if (!type || type === 'https://schema.org' || seenTypes[type]) {
                        return false;
                    }
                    seenTypes[type] = true;
                    return true;
                });
        },

        resolveSmartBuilderTarget: function(typeValue, fallbackType) {
            const candidates = this.getSchemaTypeCandidates(typeValue, fallbackType);
            const schemaTypeMap = this.getSchemaTypeMap();
            const smartBuilderCardTypes = this.getSmartBuilderCardTypes();
            const cardSet = {};
            smartBuilderCardTypes.forEach(function(type) { cardSet[type] = true; });

            let bestType = '';
            let bestScore = -1;

            candidates.forEach(function(type, index) {
                const mappedCard = schemaTypeMap[type] || type;
                let score = 0;

                if (schemaTypeMap[type] && schemaTypeMap[type] !== type) {
                    score = 90;
                } else if (cardSet[type]) {
                    score = 70;
                }

                score += index * 0.01;
                if (type === fallbackType) {
                    score += 5;
                }

                if (score > bestScore && (cardSet[mappedCard] || schemaTypeMap[type])) {
                    bestScore = score;
                    bestType = type;
                }
            });

            if (!bestType && candidates.length) {
                const hybridFallback = this.getHybridFallbackType();
                if (cardSet[hybridFallback]) {
                    return {
                        schemaType: candidates[0],
                        loadType: hybridFallback,
                        cardType: hybridFallback,
                        supported: true,
                        hybrid: true,
                        label: candidates.length > 1 ? candidates.join(' + ') : candidates[0]
                    };
                }

                bestType = candidates[0];
            }

            const cardType = schemaTypeMap[bestType] || bestType;
            return {
                schemaType: bestType,
                loadType: bestType,
                cardType: cardType,
                supported: !!cardSet[cardType],
                label: candidates.length > 1 ? candidates.join(' + ') : (bestType || String(typeValue || fallbackType || ''))
            };
        },
        /**
         * Edit a schema - show mode selection modal
         * @param {number|string} id - Schema ID to edit
         */
        editSchema: function(id) {
            const self = this;

            // Validate ID
            if (!id || isNaN(parseInt(id))) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_id) || 'Invalid schema ID', 'error');
                return;
            }

            // Show loading indicator
            this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.loading_schema) || 'Loading schema...', 'info');

            // Fetch the schema data
            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_get_schema',
                    id: id,
                    nonce: schemafy_ajax.nonce
                },
                success: function(response) {
                    if (response && response.success && response.data) {
                        self.showEditModeSelectionModal(response.data);
                    } else {
                        const errorMsg = (response && response.data) ? response.data : 'Failed to load schema';
                        self.showNotification(errorMsg, 'error');
                    }
                },
                error: function() {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.network_error_loading) || 'Network error loading schema', 'error');
                }
            });
        },

        /**
         * Show modal to choose between Smart Builder and Manual JSON for editing
         * @param {Object} schema - Schema data object
         */
        showEditModeSelectionModal: function(schema) {
            // Validate schema object
            if (!schema || !schema.id) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_data) || 'Invalid schema data', 'error');
                return;
            }

            // Remove existing modal if any
            this.closeEditModeModal();

            // Escape schema name for safe HTML insertion
            const safeSchemaName = this.escapeHtml(schema.schema_name || 'Unnamed Schema');

            const features = (typeof schemafy_ajax !== 'undefined' && schemafy_ajax.features) ? schemafy_ajax.features : {};
            const upgradeUrl = (typeof schemafy_ajax !== 'undefined' && schemafy_ajax.upgrade_url) ? schemafy_ajax.upgrade_url : '#';
            const i18n = (schemafy_ajax && schemafy_ajax.i18n) || {};

            // An editor is offered only when this build can actually open the
            // schema in it: Smart Builder needs a card for the resolved type,
            // Manual JSON needs the Manual editor to be part of the build.
            let smartBuilderAvailable = false;
            try {
                const schemaData = this.parseSchemaData(schema.schema_data);
                if (schemaData) {
                    const target = this.resolveSmartBuilderTarget(schemaData['@type'] || schema.schema_type, schema.schema_type);
                    const $typeCard = $(`.schemafy-schema-type-card[data-type="${this.escapeSelector(target.cardType)}"]`);
                    smartBuilderAvailable = $typeCard.length > 0 && !$typeCard.hasClass('schemafy-schema-locked');
                }
            } catch(e) {}

            const upgradeLink = `<a href="${upgradeUrl}" class="schemafy-btn-select-mode schemafy-btn-upgrade">
                        <span class="dashicons dashicons-unlock"></span>
                        ${i18n.see_plans || 'See plans'}
                   </a>`;
            const upgradeBadge = `<span class="schemafy-plan-required-badge">${i18n.not_in_your_plan || 'Not included in your plan'}</span>`;

            const smartBuilderLocked = !smartBuilderAvailable;

            const smartBuilderBtn = smartBuilderAvailable
                ? `<button class="schemafy-btn-select-mode schemafy-btn-primary" data-mode="smart-builder">
                        <span class="dashicons dashicons-arrow-right-alt"></span>
                        ${i18n.use_smart_builder || 'Use Smart Builder'}
                   </button>`
                : upgradeLink;
            const smartBuilderBadge = smartBuilderAvailable
                ? `<span class="schemafy-recommended-badge">${i18n.recommended_badge || 'Recommended'}</span>`
                : upgradeBadge;

            const manualJsonBtn = `<button class="schemafy-btn-select-mode schemafy-btn-secondary" data-mode="manual-json">
                        <span class="dashicons dashicons-arrow-right-alt"></span>
                        ${i18n.use_manual_json || 'Use Manual JSON'}
                   </button>`;

            const modal = `
                <div id="schemafy-edit-mode-modal" class="schemafy-modal-overlay" role="dialog" aria-modal="true" aria-labelledby="schemafy-edit-mode-title">
                    <div class="schemafy-modal-container schemafy-editor-mode-modal">
                        <div class="schemafy-modal-header">
                            <h2 id="schemafy-edit-mode-title">
                                <span class="dashicons dashicons-edit"></span>
                                ${i18n.edit_schema_prefix || 'Edit Schema: '}${safeSchemaName}
                            </h2>
                            <button class="schemafy-modal-close" id="schemafy-close-edit-mode-modal" aria-label="Close modal">
                                <span class="dashicons dashicons-no"></span>
                            </button>
                        </div>
                        <div class="schemafy-modal-body">
                            <p class="schemafy-mode-description">
                                ${i18n.choose_edit_mode || 'Choose how you want to edit this schema:'}
                            </p>
                            <div class="schemafy-mode-selection-grid">
                                <div class="schemafy-mode-card schemafy-mode-smart-builder${smartBuilderLocked ? ' schemafy-mode-locked' : ''}" ${!smartBuilderLocked ? 'data-mode="smart-builder" tabindex="0" role="button"' : ''}>
                                    <div class="schemafy-mode-icon">
                                        <span class="dashicons dashicons-layout"></span>
                                    </div>
                                    <h3>Smart Builder</h3>
                                    <p>${i18n.visual_guided_fields || 'Visual interface with guided fields'}</p>
                                    <ul class="schemafy-mode-features">
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.no_coding_required || 'No coding required'}</li>
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.pre_configured_templates || 'Pre-configured templates'}</li>
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.field_validation_label || 'Field validation'}</li>
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.beginner_friendly || 'Beginner friendly'}</li>
                                    </ul>
                                    ${smartBuilderBtn}
                                    ${smartBuilderBadge}
                                </div>
                                <div class="schemafy-mode-card schemafy-mode-manual-json" data-mode="manual-json" tabindex="0" role="button">
                                    <div class="schemafy-mode-icon">
                                        <span class="dashicons dashicons-editor-code"></span>
                                    </div>
                                    <h3>Manual JSON</h3>
                                    <p>${i18n.direct_json_editor || 'Direct JSON code editor'}</p>
                                    <ul class="schemafy-mode-features">
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.full_control || 'Full control'}</li>
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.custom_properties || 'Custom properties'}</li>
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.advanced_schemas_label || 'Advanced schemas'}</li>
                                        <li><span class="dashicons dashicons-yes"></span> ${i18n.for_developers || 'For developers'}</li>
                                    </ul>
                                    ${manualJsonBtn}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            `;

            $('body').append(modal);

            const $modal = $('#schemafy-edit-mode-modal');

            // Fade in modal
            $modal.fadeIn(200);

            // Store schema reference for event handlers
            $modal.data('schema', schema);

            // Bind events using namespaced events for clean removal
            this.bindEditModeModalEvents($modal, schema);

            // Focus first interactive element for accessibility
            $modal.find('.schemafy-mode-card').first().focus();
        },

        /**
         * Bind events to the edit mode modal
         * @param {jQuery} $modal - Modal jQuery element
         * @param {Object} schema - Schema data object
         */
        bindEditModeModalEvents: function($modal, schema) {
            const self = this;

            // Close button click
            $modal.on('click.schemafyEditModal', '#schemafy-close-edit-mode-modal', function(e) {
                e.preventDefault();
                e.stopPropagation();
                self.closeEditModeModal();
            });

            // Click on overlay background to close
            $modal.on('click.schemafyEditModal', function(e) {
                if (e.target === this) {
                    self.closeEditModeModal();
                }
            });

            // Escape key to close
            $(document).on('keydown.schemafyEditModal', function(e) {
                if (e.key === 'Escape' || e.keyCode === 27) {
                    self.closeEditModeModal();
                }
            });

            // Mode selection button click (only for buttons with data-mode, not upgrade links)
            $modal.on('click.schemafyEditModal', '.schemafy-btn-select-mode[data-mode]', function(e) {
                e.preventDefault();
                const mode = $(this).data('mode');
                self.handleModeSelection(mode, schema);
            });

            // Card click to select (only unlocked cards, excluding button/link clicks)
            $modal.on('click.schemafyEditModal', '.schemafy-mode-card:not(.schemafy-mode-locked)', function(e) {
                if (!$(e.target).hasClass('schemafy-btn-select-mode') && !$(e.target).closest('.schemafy-btn-select-mode').length) {
                    $(this).find('.schemafy-btn-select-mode[data-mode]').trigger('click');
                }
            });

            // Keyboard support for unlocked cards only
            $modal.on('keydown.schemafyEditModal', '.schemafy-mode-card:not(.schemafy-mode-locked)', function(e) {
                if (e.key === 'Enter' || e.keyCode === 13 || e.key === ' ' || e.keyCode === 32) {
                    e.preventDefault();
                    $(this).find('.schemafy-btn-select-mode[data-mode]').trigger('click');
                }
            });
        },

        /**
         * Handle mode selection from modal
         * @param {string} mode - Selected mode ('smart-builder' or 'manual-json')
         * @param {Object} schema - Schema data object
         */
        handleModeSelection: function(mode, schema) {
            this.closeEditModeModal();

            if (mode === 'smart-builder') {
                this.loadSchemaInSmartBuilder(schema);
            } else {
                this.loadSchemaInManualEditor(schema);
            }
        },

        /**
         * Close the edit mode modal and cleanup
         */
        closeEditModeModal: function() {
            const $modal = $('#schemafy-edit-mode-modal');
            if ($modal.length) {
                // Remove namespaced events
                $modal.off('.schemafyEditModal');
                $(document).off('keydown.schemafyEditModal');

                $modal.fadeOut(200, function() {
                    $(this).remove();
                });
            }
        },

        /**
         * Load schema in Smart Builder for editing
         * @param {Object} schema - Schema data object
         */
        loadSchemaInSmartBuilder: function(schema) {
            const self = this;

            // Validate schema
            if (!schema || !schema.schema_data) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_data) || 'Invalid schema data', 'error');
                return;
            }

            // Switch to builder tab
            const $builderTab = $('[data-tab="builder"]');
            if (!$builderTab.length) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.smart_builder_tab_not_found) || 'Smart Builder tab not found. Opening in Manual JSON.', 'warning');
                this.loadSchemaInManualEditor(schema);
                return;
            }

            $builderTab.trigger('click');

            // Wait for tab to load
            setTimeout(function() {
                // Parse the schema data to get the type
                let schemaData;
                try {
                    schemaData = self.parseSchemaData(schema.schema_data);
                } catch (e) {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.error_parsing_schema) || 'Error parsing schema data. Opening in Manual JSON.', 'warning');
                    self.loadSchemaInManualEditor(schema);
                    return;
                }

                if (!self.isSingleSchemaNode(schemaData)) {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_format) || 'Invalid schema format. Opening in Manual JSON.', 'warning');
                    self.loadSchemaInManualEditor(schema);
                    return;
                }

                const schemaType = schemaData['@type'] || schema.schema_type;

                if (!schemaType) {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.schema_type_not_found) || 'Schema type not found. Opening in Manual JSON.', 'warning');
                    self.loadSchemaInManualEditor(schema);
                    return;
                }

                const target = self.resolveSmartBuilderTarget(schemaType, schema.schema_type);

                if (!target.supported) {
                    self.showNotification(
                        `Schema type "${self.escapeHtml(target.label)}" not available in Smart Builder. Opening in Manual JSON instead.`,
                        'warning'
                    );
                    self.loadSchemaInManualEditor(schema);
                    return;
                }

                // Find the parent card used for access control and visual state.
                const $typeCard = $(`.schemafy-schema-type-card[data-type="${self.escapeSelector(target.cardType)}"]`);
                if (!$typeCard.length) {
                    self.showNotification(
                        `Schema type "${self.escapeHtml(target.label)}" not available in Smart Builder. Opening in Manual JSON instead.`,
                        'warning'
                    );
                    self.loadSchemaInManualEditor(schema);
                    return;
                }

                // Locked cards keep their normal upgrade behavior.
                if ($typeCard.hasClass('schemafy-schema-locked')) {
                    $typeCard.trigger('click');
                    return;
                }

                $('.schemafy-schema-type-card').removeClass('active');
                $typeCard.addClass('active');

                if (window.SchemafyBuilder && typeof window.SchemafyBuilder.updateStepIndicator === 'function') {
                    window.SchemafyBuilder.updateStepIndicator(2);
                }

                if (window.SchemafyBuilder && typeof window.SchemafyBuilder.loadSchemaFields === 'function') {
                    window.SchemafyBuilder.loadSchemaFields(target.loadType);
                    self.waitForFormAndPopulate(target.loadType, schemaData, schema);
                } else {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.smart_builder_tab_not_found) || 'Smart Builder tab not found. Opening in Manual JSON.', 'warning');
                    self.loadSchemaInManualEditor(schema);
                }

                window.scrollTo({ top: 0, behavior: 'smooth' });
            }, 150);
        },

        /**
         * Wait for form to be ready and populate fields
         * @param {string} schemaType - Expected schema type
         * @param {Object} schemaData - Parsed schema data
         * @param {Object} schema - Original schema object
         */
        waitForFormAndPopulate: function(schemaType, schemaData, schema) {
            const self = this;

            // Register a one-shot callback that smart-builder.js fires after
            // renderSchemaFieldsFromConfig/renderSchemaFields completes.
            // This avoids the 3-second polling timeout that caused redirects on slow servers.
            window._schemafyBuilderFormReady = function(renderedType) {
                window._schemafyBuilderFormReady = null;

                // If a locked card triggered an upgrade popup, stay silent.
                const $lockedCard = $(`.schemafy-schema-type-card[data-type="${self.escapeSelector(schemaType)}"]`);
                if ($lockedCard.length && $lockedCard.hasClass('schemafy-schema-locked')) {
                    return;
                }

                if (renderedType !== schemaType) {
                    self.showNotification(
                        (schemafy_ajax.i18n && schemafy_ajax.i18n.form_loading_timeout) || 'Form loading timeout. Opening in Manual JSON instead.',
                        'warning'
                    );
                    self.loadSchemaInManualEditor(schema);
                    return;
                }

                setTimeout(function() {
                    self.populateSmartBuilderFields(schemaData, schema);
                }, 100);
            };

            // Fallback: if the callback is never fired (e.g., AJAX error), clean up after 15s.
            const fallbackTimer = setTimeout(function() {
                if (typeof window._schemafyBuilderFormReady === 'function') {
                    window._schemafyBuilderFormReady = null;
                    const $lockedCard = $(`.schemafy-schema-type-card[data-type="${self.escapeSelector(schemaType)}"]`);
                    if ($lockedCard.length && $lockedCard.hasClass('schemafy-schema-locked')) {
                        return;
                    }
                    self.showNotification(
                        (schemafy_ajax.i18n && schemafy_ajax.i18n.form_loading_timeout) || 'Form loading timeout. Opening in Manual JSON instead.',
                        'warning'
                    );
                    self.loadSchemaInManualEditor(schema);
                }
            }, 15000);

            // Cancel fallback once the callback fires
            const origReady = window._schemafyBuilderFormReady;
            window._schemafyBuilderFormReady = function(renderedType) {
                clearTimeout(fallbackTimer);
                origReady(renderedType);
            };
        },

        /**
         * Populate Smart Builder fields with schema data
         * @param {Object} schemaData - Parsed schema data
         * @param {Object} schema - Original schema object
         */
        populateSmartBuilderFields: function(schemaData, schema) {
            const $form = $('#schemafy-dynamic-schema-form');

            if (!$form.length) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.form_not_found) || 'Form not found', 'error');
                return;
            }

            const target = this.resolveSmartBuilderTarget(schemaData['@type'] || schema.schema_type, schema.schema_type);
            const editableSchemaData = this.normalizeSchemaForSmartBuilder(schemaData);

            // Set the URL first
            this.populateUrlField(schema.url);

            // Set schema name
            const $schemaNameField = $form.find('[name="schema_name"]');
            if ($schemaNameField.length) {
                $schemaNameField.val(schema.schema_name || '');
            }

            // Store editing ID
            $form.data('editing-id', schema.id);
            $form.data('original-schema-data', this.deepClone(schemaData));
            $form.data('original-schema-type', this.deepClone(schemaData['@type'] || schema.schema_type));
            $form.data('storage-schema-type', target.schemaType || schema.schema_type);
            $form.data('schema-builder-type', target.cardType || target.loadType || target.schemaType || schema.schema_type);

            // Update save button to show "Update" instead of "Save"
            this.updateSaveButtonForEditing(schema.schema_name);

            // Method 1: Use direct path mappings for known schema structures
            // This is more reliable for complex nested schemas
            this.populateFieldsFromSchemaPaths(editableSchemaData, $form);

            // Method 2: Flatten the schema data and try to match fields
            // This catches any fields not in the explicit mapping
            const flattenedData = this.flattenSchemaData(editableSchemaData);
            this.populateRegularFields($form, flattenedData);

            // Method 3: Handle array fields (FAQs, reviews, steps, etc.)
            this.populateArrayFields(editableSchemaData, $form);

            // Preserve original @type for subtypes (e.g., NewsArticle, Restaurant, AboutPage)
            // The builder card sets schema_type to the parent type (e.g., "Article"),
            // but we need to keep the original subtype so generateSchemaFromForm produces
            // the correct @type in the output JSON.
            if (target.schemaType) {
                $form.find('[name="schema_type"]').val(target.schemaType);
            }

            // Update JSON preview after all fields are populated
            var self = this;
            setTimeout(function() {
                if (window.SchemafyBuilder && typeof window.SchemafyBuilder.updateBuilderPreview === 'function') {
                    window.SchemafyBuilder.updateBuilderPreview();
                }

                // Trigger input events on all filled fields to ensure validation/preview updates
                $form.find('input, textarea, select').each(function() {
                    if ($(this).val()) {
                        $(this).trigger('change');
                    }
                });

                // Lock URL controls in edit mode
                self.lockBuilderUrlControls();
            }, 300);

            this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.schema_loaded_smart_builder) || 'Schema loaded in Smart Builder for editing', 'success');
        },

        /**
         * Populate URL field in Smart Builder using the chip system
         * Waits for the chip container to be available before adding the URL
         * @param {string} url - URL to set
         */
        populateUrlField: function(url) {
            if (!url) return;

            const maxAttempts = 30;
            let attempts = 0;

            function tryAddChip() {
                const $container = $('#schemafy-selected-urls');

                if (!$container.length) {
                    if (attempts < maxAttempts) {
                        attempts++;
                        setTimeout(tryAddChip, 100);
                    }
                    return;
                }

                // Wait for URL dropdown to finish loading
                const $urlSelect = $('#schemafy-url-select');
                if ($urlSelect.length) {
                    const firstOptionText = $urlSelect.find('option:first').text();
                    const isLoading = firstOptionText.indexOf('Loading') !== -1;
                    if (isLoading && attempts < maxAttempts) {
                        attempts++;
                        setTimeout(tryAddChip, 100);
                        return;
                    }
                }

                // Add URL as a chip via SchemafyBuilder
                if (window.SchemafyBuilder && typeof window.SchemafyBuilder.addUrlChip === 'function') {
                    window.SchemafyBuilder.addUrlChip(url);
                }
            }

            tryAddChip();
        },

        deepClone: function(value) {
            if (value === undefined || value === null) {
                return value;
            }
            try {
                return JSON.parse(JSON.stringify(value));
            } catch (e) {
                return value;
            }
        },

        normalizeSchemaForSmartBuilder: function(schemaData) {
            const normalized = this.deepClone(schemaData) || {};

            ['image', 'logo', 'primaryImageOfPage', 'thumbnailUrl'].forEach(function(key) {
                if (Array.isArray(normalized[key])) {
                    normalized[key] = normalized[key][0] || '';
                }
                if (normalized[key] && typeof normalized[key] === 'object' && normalized[key].url) {
                    normalized[key] = normalized[key].url;
                }
            });

            if (normalized.publisher && normalized.publisher.logo && typeof normalized.publisher.logo === 'object' && normalized.publisher.logo.url) {
                normalized.publisher.logo = normalized.publisher.logo.url;
            }

            if (normalized.openingHoursSpecification && !Array.isArray(normalized.openingHoursSpecification)) {
                normalized.openingHoursSpecification = [normalized.openingHoursSpecification];
            }

            if (typeof normalized.sameAs === 'string') {
                normalized.sameAs = [normalized.sameAs];
            }

            return normalized;
        },

        schemaValueToFieldValue: function(value) {
            if (Array.isArray(value)) {
                if (!value.length) {
                    return '';
                }
                const first = value[0];
                if (first && typeof first === 'object') {
                    return first.url || first.name || '';
                }
                return first;
            }

            if (value && typeof value === 'object') {
                return value.url || value.name || '';
            }

            return value;
        },

        /**
         * Populate URL field in Manual JSON editor using chip system
         * @param {string} url - URL to set
         */
        populateManualEditorUrl: function(url) {
            if (!url) return;

            const maxAttempts = 30;
            let attempts = 0;

            function tryAddChip() {
                const $container = $('#schemafy-manual-selected-urls');

                if (!$container.length) {
                    if (attempts < maxAttempts) {
                        attempts++;
                        setTimeout(tryAddChip, 100);
                    }
                    return;
                }

                // Wait for combobox to finish loading (placeholder changes from "Loading…")
                const $combo = $('#schemafy-manual-url-combobox');
                if ($combo.length && ($combo.attr('placeholder') || '').indexOf('Loading') !== -1) {
                    if (attempts < maxAttempts) {
                        attempts++;
                        setTimeout(tryAddChip, 100);
                        return;
                    }
                }

                // Add URL as a chip via SchemafyEditor
                if (window.SchemafyEditor && typeof window.SchemafyEditor.addManualUrlChip === 'function') {
                    window.SchemafyEditor.addManualUrlChip(url);
                }
            }

            tryAddChip();
        },

        /**
         * Visually lock URL controls in Smart Builder during edit mode
         */
        lockBuilderUrlControls: function() {
            $('#schemafy-url-select').prop('disabled', true);
            $('#schemafy-url-manual').prop('disabled', true);
            $('#schemafy-add-url').prop('disabled', true).css('opacity', '0.5');
            $('.schemafy-toggle-manual-url').prop('disabled', true).css('opacity', '0.5');
            $('#schemafy-selected-urls .schemafy-chip-remove').css('display', 'none');
        },

        /**
         * Visually lock URL controls in Manual JSON editor during edit mode
         */
        lockManualUrlControls: function() {
            $('#schemafy-manual-url-combobox').prop('disabled', true).css('opacity', '0.5');
            $('#schemafy-manual-selected-urls .schemafy-chip-remove').css('display', 'none');
        },

        /**
         * Populate regular (non-array) fields
         * @param {jQuery} $form - Form element
         * @param {Object} flattenedData - Flattened schema data
         */
        populateRegularFields: function($form, flattenedData) {
            const self = this;
            const populatedFields = new Set(); // Track which fields have been populated

            Object.entries(flattenedData).forEach(function([fieldName, value]) {
                if (fieldName.startsWith('@') || value === null || value === undefined) return;

                // Skip if value is empty string
                if (typeof value === 'string' && value.trim() === '') return;

                // Convert value to string for form fields
                const stringValue = String(value);

                // Generate all possible field name variations
                const variations = self.generateFieldNameVariations(fieldName);

                // Build comprehensive list of selectors to try
                const selectors = [];

                variations.forEach(function(variation) {
                    const escaped = self.escapeSelector(variation);
                    selectors.push(`[name="${escaped}"]`);
                    selectors.push(`[name="${escaped.toLowerCase()}"]`);
                    selectors.push(`#schemafy-field-${escaped}`);
                    selectors.push(`[data-schema-path="${escaped}"]`);
                });

                // Also try some common field naming patterns
                const parts = fieldName.split('.');
                if (parts.length >= 2) {
                    // e.g., author.name -> authorName
                    const camelCase = self.pathToCamelCase(fieldName);
                    selectors.push(`[name="${self.escapeSelector(camelCase)}"]`);

                    // e.g., publisher.logo.url -> publisherLogo (for image fields)
                    if (parts.length === 3 && parts[2] === 'url') {
                        const parentCamel = parts[0] + parts[1].charAt(0).toUpperCase() + parts[1].slice(1);
                        selectors.push(`[name="${self.escapeSelector(parentCamel)}"]`);
                    }
                }

                // Try each selector until we find a match
                for (const selector of selectors) {
                    try {
                        const $field = $form.find(selector);
                        if ($field.length && !populatedFields.has($field.attr('name'))) {
                            const fieldNameAttr = $field.attr('name');

                            // Date inputs only accept YYYY-MM-DD; strip time from ISO datetimes
                            let setVal = stringValue;
                            if ($field.attr('type') === 'date' && stringValue.includes('T')) {
                                setVal = stringValue.split('T')[0];
                            }

                            // Set the value
                            $field.val(setVal).trigger('input').trigger('change');
                            populatedFields.add(fieldNameAttr);

                            // Handle image preview
                            if ($field.hasClass('schemafy-image-url-field') && stringValue) {
                                self.showImagePreview($field, stringValue);
                                $field.closest('.schemafy-image-field-wrapper').find('.schemafy-image-url-text').text(stringValue || (schemafy_ajax.i18n && schemafy_ajax.i18n.no_image_selected) || 'No image selected');
                            }

                            // Handle select fields - check if value exists as option
                            if ($field.is('select')) {
                                // For selects with object options (value/label pairs)
                                if ($field.find(`option[value="${self.escapeSelector(stringValue)}"]`).length === 0) {
                                    // Try matching by label text
                                    let matched = false;
                                    $field.find('option').each(function() {
                                        if ($(this).text().toLowerCase() === stringValue.toLowerCase()) {
                                            $field.val($(this).val());
                                            matched = true;
                                            return false;
                                        }
                                    });

                                    // Still unmatched (e.g. a currency code outside the
                                    // common list): append it so the stored value is not
                                    // silently dropped on save.
                                    if (!matched && stringValue) {
                                        $field.append(
                                            $('<option>').attr('value', stringValue).text(stringValue)
                                        );
                                        $field.val(stringValue);
                                    }
                                }
                            }

                            break;
                        }
                    } catch (e) {
                        // Invalid selector, continue to next
                        continue;
                    }
                }
            });
        },

        /**
         * Populate fields directly from schema data using schema paths
         * This is a more direct approach for complex nested schemas
         * @param {Object} schemaData - Original schema data
         * @param {jQuery} $form - Form element
         */
        populateFieldsFromSchemaPaths: function(schemaData, $form) {
            const self = this;

            // Common schema path to field name mappings
            // Field names use dot-notation matching auto-prefixed group sub-fields
            const pathMappings = {
                // Common direct properties
                'headline': 'headline',
                'description': 'description',
                'image': 'image',
                'name': 'name',
                'telephone': 'telephone',
                'email': 'email',
                'url': 'url',
                'logo': 'logo',
                'datePublished': 'datePublished',
                'dateModified': 'dateModified',
                'keywords': 'keywords',
                'inLanguage': 'inLanguage',

                // Author (dot-notation for group fields)
                'author.name': 'author.name',
                'author.url': 'author.url',
                'author.image': 'author.image',
                'author.@type': 'author.type',

                // Publisher
                'publisher.name': 'publisher.name',
                'publisher.url': 'publisher.url',
                'publisher.logo.url': 'publisher.logo',
                'publisher.@type': 'publisher.type',

                // Article-specific
                'articleSection': 'articleSection',
                'articleBody': 'articleBody',
                'wordCount': 'wordCount',
                'mainEntityOfPage': 'mainEntityOfPage',

                // Address (LocalBusiness, Organization, Person)
                'address.streetAddress': 'address.streetAddress',
                'address.addressLocality': 'address.addressLocality',
                'address.addressRegion': 'address.addressRegion',
                'address.postalCode': 'address.postalCode',
                'address.addressCountry': 'address.addressCountry',

                // LocalBusiness
                'priceRange': 'priceRange',
                'openingHours': 'openingHours',
                'paymentAccepted': 'paymentAccepted',
                'currenciesAccepted': 'currenciesAccepted',
                'servesCuisine': 'servesCuisine',
                'hasMap': 'hasMap',
                'geo.latitude': 'geo.latitude',
                'geo.longitude': 'geo.longitude',

                // Aggregate Rating
                'aggregateRating.ratingValue': 'aggregateRating.ratingValue',
                'aggregateRating.reviewCount': 'aggregateRating.reviewCount',
                'aggregateRating.bestRating': 'aggregateRating.bestRating',
                'aggregateRating.worstRating': 'aggregateRating.worstRating',

                // Event
                'startDate': 'startDate',
                'endDate': 'endDate',
                'eventStatus': 'eventStatus',
                'eventAttendanceMode': 'eventAttendanceMode',
                'location.name': 'location.name',
                'location.address.streetAddress': 'location.streetAddress',
                'location.address.addressLocality': 'location.addressLocality',
                'location.address.addressRegion': 'location.addressRegion',
                'location.address.postalCode': 'location.postalCode',
                'location.address.addressCountry': 'location.addressCountry',
                'organizer.name': 'organizer.name',
                'organizer.url': 'organizer.url',
                'organizer.@type': 'organizer.type',
                'performer.name': 'performer.name',
                'performer.@type': 'performer.type',

                // Offers (Event, Product, Course, Service)
                'offers.price': 'offers.price',
                'offers.priceCurrency': 'offers.priceCurrency',
                'offers.availability': 'offers.availability',
                'offers.url': 'offers.url',
                'offers.validFrom': 'offers.validFrom',
                'offers.category': 'offers.category',
                'offers.priceSpecification.name': 'offers.priceSpecification',

                // Product
                'brand.name': 'brand',
                'sku': 'sku',

                // Organization ContactPoint
                'contactPoint.telephone': 'contactPoint.telephone',
                'contactPoint.contactType': 'contactPoint.contactType',
                'contactPoint.email': 'contactPoint.email',
                'contactPoint.areaServed': 'contactPoint.areaServed',
                'contactPoint.availableLanguage': 'contactPoint.availableLanguage',

                // Video
                'contentUrl': 'contentUrl',
                'embedUrl': 'embedUrl',
                'thumbnailUrl': 'thumbnailUrl',
                'uploadDate': 'uploadDate',
                'duration': 'duration',
                'transcript': 'transcript',
                'interactionStatistic.userInteractionCount': 'interactionStatistic.viewCount',

                // Recipe
                'prepTime': 'prepTime',
                'cookTime': 'cookTime',
                'totalTime': 'totalTime',
                'recipeYield': 'recipeYield',
                'recipeCategory': 'recipeCategory',
                'recipeCuisine': 'recipeCuisine',
                'suitableForDiet': 'suitableForDiet',
                'nutrition.calories': 'nutrition.calories',
                'nutrition.carbohydrateContent': 'nutrition.carbohydrateContent',
                'nutrition.proteinContent': 'nutrition.proteinContent',
                'nutrition.fatContent': 'nutrition.fatContent',
                'nutrition.fiberContent': 'nutrition.fiberContent',
                'nutrition.sugarContent': 'nutrition.sugarContent',
                'nutrition.sodiumContent': 'nutrition.sodiumContent',

                // HowTo
                'estimatedCost.value': 'estimatedCost.value',
                'estimatedCost.currency': 'estimatedCost.currency',
                'video.name': 'video.name',
                'video.description': 'video.description',
                'video.thumbnailUrl': 'video.thumbnailUrl',
                'video.contentUrl': 'video.contentUrl',
                'video.uploadDate': 'video.uploadDate',

                // Course
                'courseCode': 'courseCode',
                'coursePrerequisites': 'coursePrerequisites',
                'educationalLevel': 'educationalLevel',
                'timeRequired': 'timeRequired',
                'numberOfCredits': 'numberOfCredits',
                'provider.name': 'provider.name',
                'provider.url': 'provider.url',

                // Service
                'serviceType': 'serviceType',
                'areaServed.name': 'areaServed.name',
                'areaServed.geo.latitude': 'areaServed.geo.latitude',
                'areaServed.geo.longitude': 'areaServed.geo.longitude',
                'provider.telephone': 'provider.telephone',
                'provider.email': 'provider.email',

                // Person
                'alternateName': 'alternateName',
                'givenName': 'givenName',
                'familyName': 'familyName',
                'jobTitle': 'jobTitle',
                'birthDate': 'birthDate',
                'birthPlace': 'birthPlace',
                'nationality': 'nationality',
                'gender': 'gender',
                'knowsLanguage': 'knowsLanguage',
                'alumniOf': 'alumniOf',
                'award': 'award',
                'honorificPrefix': 'honorificPrefix',
                'honorificSuffix': 'honorificSuffix',
                'knowsAbout': 'knowsAbout',
                'worksFor.name': 'worksFor.name',
                'worksFor.url': 'worksFor.url',

                // JobPosting
                'title': 'title',
                'datePosted': 'datePosted',
                'employmentType': 'employmentType',
                'validThrough': 'validThrough',
                'hiringOrganization.name': 'hiringOrganization.name',
                'hiringOrganization.sameAs': 'hiringOrganization.sameAs',
                'hiringOrganization.logo': 'hiringOrganization.logo',
                'jobLocation.address.streetAddress': 'jobLocation.streetAddress',
                'jobLocation.address.addressLocality': 'jobLocation.addressLocality',
                'jobLocation.address.addressRegion': 'jobLocation.addressRegion',
                'jobLocation.address.postalCode': 'jobLocation.postalCode',
                'jobLocation.address.addressCountry': 'jobLocation.addressCountry',
                'baseSalary.value.minValue': 'baseSalary.minValue',
                'baseSalary.value.maxValue': 'baseSalary.maxValue',
                'baseSalary.value.unitText': 'baseSalary.unitText',
                'baseSalary.currency': 'baseSalary.currency',
                'applicationContact.email': 'applicationContact.email',
                'applicationContact.telephone': 'applicationContact.telephone',
                'applicationContact.url': 'applicationContact.url',

                // Review
                'itemReviewed.name': 'itemReviewed.name',
                'itemReviewed.@type': 'itemReviewed.type',
                'itemReviewed.image': 'itemReviewed.image',
                'itemReviewed.url': 'itemReviewed.url',
                'reviewRating.ratingValue': 'reviewRating.ratingValue',
                'reviewRating.bestRating': 'reviewRating.bestRating',
                'reviewRating.worstRating': 'reviewRating.worstRating',
                'reviewBody': 'reviewBody',
                'reviewAspect': 'reviewAspect',

                // WebPage
                'primaryImageOfPage': 'primaryImageOfPage',
                'mainEntity': 'mainEntity',
                'speakable.cssSelector': 'speakable.cssSelector',
                'speakable.xpath': 'speakable.xpath'
            };

            // Populate using path mappings
            Object.entries(pathMappings).forEach(function([schemaPath, fieldName]) {
                const value = self.schemaValueToFieldValue(self.getNestedValue(schemaData, schemaPath));
                if (value !== undefined && value !== null && value !== '') {
                    const $field = $form.find(`[name="${self.escapeSelector(fieldName)}"]`);
                    if ($field.length) {
                        let stringValue = String(value);

                        // Date inputs only accept YYYY-MM-DD format
                        // Strip time portion from ISO datetime strings (e.g., "2026-03-15T09:00:00" → "2026-03-15")
                        if ($field.attr('type') === 'date' && stringValue.includes('T')) {
                            stringValue = stringValue.split('T')[0];
                        }

                        $field.val(stringValue).trigger('input').trigger('change');

                        // Handle image preview
                        if ($field.hasClass('schemafy-image-url-field')) {
                            self.showImagePreview($field, stringValue);
                            $field.closest('.schemafy-image-field-wrapper').find('.schemafy-image-url-text').text(stringValue || (schemafy_ajax.i18n && schemafy_ajax.i18n.no_image_selected) || 'No image selected');
                        }
                    }
                }
            });

            // Handle fields where schema path maps to different field names per schema type
            // Recipe: video.contentUrl → flat field "video" (not group "video.contentUrl" like HowTo)
            const videoContentUrl = self.getNestedValue(schemaData, 'video.contentUrl');
            if (videoContentUrl) {
                const $videoFlat = $form.find('[name="video"]');
                if ($videoFlat.length && !$videoFlat.val()) {
                    $videoFlat.val(String(videoContentUrl)).trigger('input').trigger('change');
                }
            }

            // publisher.logo as plain string (some schemas don't wrap in ImageObject)
            if (schemaData.publisher && typeof schemaData.publisher.logo === 'string') {
                const $logoField = $form.find('[name="publisher\\.logo"]');
                if ($logoField.length && !$logoField.val()) {
                    $logoField.val(schemaData.publisher.logo).trigger('input').trigger('change');
                    if ($logoField.hasClass('schemafy-image-url-field')) {
                        self.showImagePreview($logoField, schemaData.publisher.logo);
                        $logoField.closest('.schemafy-image-field-wrapper').find('.schemafy-image-url-text').text(schemaData.publisher.logo || (schemafy_ajax.i18n && schemafy_ajax.i18n.no_image_selected) || 'No image selected');
                    }
                }
            }
        },

        /**
         * Show image preview for image fields
         * @param {jQuery} $field - Image field element
         * @param {string} imageUrl - Image URL
         */
        showImagePreview: function($field, imageUrl) {
            const fieldId = $field.attr('id');
            if (!fieldId) return;

            const $preview = $('#' + fieldId + '-preview');
            if ($preview.length && imageUrl) {
                $preview.find('img').attr('src', imageUrl);
                $preview.show();
            }
        },

        /**
         * Update save button to show editing state
         * @param {string} schemaName - Name of schema being edited
         */
        updateSaveButtonForEditing: function(schemaName) {
            const $saveBtn = $('#schemafy-save-builder-schema');
            if ($saveBtn.length) {
                // Store original HTML for later restoration
                if (!$saveBtn.data('original-html')) {
                    $saveBtn.data('original-html', $saveBtn.html());
                }

                // Change button to show update mode
                $saveBtn.html('<span class="dashicons dashicons-image-rotate" aria-hidden="true"></span> Update Schema');
                $saveBtn.addClass('schemafy-editing-mode');
            }

            // Also add visual indicator that we're in edit mode
            const $previewCard = $('.schemafy-preview-card');
            if ($previewCard.length && !$previewCard.find('.schemafy-editing-indicator').length) {
                const safeName = this.escapeHtml(schemaName || 'Schema');
                $previewCard.find('.schemafy-preview-header').after(
                    `<div class="schemafy-editing-indicator">
                        <span class="dashicons dashicons-edit"></span>
                        <span>Editing: <strong>${safeName}</strong></span>
                        <button type="button" class="schemafy-cancel-edit">Cancel Edit</button>
                    </div>`
                );

                // Bind cancel edit button
                $previewCard.find('.schemafy-cancel-edit').on('click', function() {
                    window.SchemafyOperations.cancelEditing();
                });
            }
        },

        /**
         * Cancel editing mode and reset form
         */
        cancelEditing: function() {
            const $form = $('#schemafy-dynamic-schema-form');
            const $saveBtn = $('#schemafy-save-builder-schema');

            // Remove editing ID
            $form.removeData('editing-id');
            $form.removeData('original-schema-data original-schema-type storage-schema-type schema-builder-type');

            // Restore save button
            if ($saveBtn.length && $saveBtn.data('original-html')) {
                $saveBtn.html($saveBtn.data('original-html'));
                $saveBtn.removeClass('schemafy-editing-mode');
            }

            // Remove editing indicator
            $('.schemafy-editing-indicator').remove();

            // Reset form
            if ($form.length && $form[0]) {
                $form[0].reset();
            }

            // Clear URL chips and restore URL controls
            $('#schemafy-selected-urls').empty();
            $('#schemafy-target-urls').val('[]');
            $('#schemafy-url-select').prop('disabled', false);
            $('#schemafy-url-manual').prop('disabled', false);
            $('#schemafy-add-url').prop('disabled', false).css('opacity', '');
            $('.schemafy-toggle-manual-url').prop('disabled', false).css('opacity', '');

            // Deselect schema type
            $('.schemafy-schema-type-card').removeClass('active');

            // Reset form container
            $('#schemafy-builder-form-container').html(`
                <div class="schemafy-builder-placeholder">
                    <span class="dashicons dashicons-arrow-left-alt"></span>
                    <h3>Select a Schema Type</h3>
                    <p>Choose a schema type from the list to start building</p>
                </div>
            `);

            // Reset JSON preview
            $('#schemafy-json-preview code').text('{\n  "@context": "https://schema.org",\n  "@type": "...",\n  "name": "..."\n}');

            this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.edit_cancelled) || 'Edit cancelled', 'info');
        },

        /**
         * Reset save button to original state (called after successful save)
         */
        resetSaveButton: function() {
            const $saveBtn = $('#schemafy-save-builder-schema');
            if ($saveBtn.length) {
                // Re-enable the button (it gets disabled during save)
                $saveBtn.prop('disabled', false);

                // Restore original HTML if it was stored
                if ($saveBtn.data('original-html')) {
                    $saveBtn.html($saveBtn.data('original-html'));
                    $saveBtn.removeData('original-html');
                }

                $saveBtn.removeClass('schemafy-editing-mode');
            }

            // Remove editing indicator
            $('.schemafy-editing-indicator').remove();
        },

        /**
         * Reset Manual JSON editor to clean state
         */
        resetManualEditor: function() {
            const $form = $('#schemafy-manual-form');

            // Remove editing state
            if ($form.length) {
                $form.removeData('editing-id');

                // Reset the form
                $form[0].reset();
            }

            // Reset save button
            const $saveBtn = $('#schemafy-manual-form button[type="submit"]');
            if ($saveBtn.length) {
                $saveBtn.prop('disabled', false);
                if ($saveBtn.data('original-html')) {
                    $saveBtn.html($saveBtn.data('original-html'));
                    $saveBtn.removeData('original-html');
                } else {
                    $saveBtn.html('<span class="dashicons dashicons-cloud-saved"></span> Save Schema');
                }
            }

            // Clear JSON field
            const $jsonField = $('#schemafy-manual-json');
            if ($jsonField.length) {
                $jsonField.val('');
            }

            // Reset URL combobox and chips
            $('#schemafy-manual-url-combobox').val('').prop('disabled', false).css('opacity', '');
            $('#schemafy-manual-selected-urls').empty();
            $('#schemafy-manual-target-urls').val('[]');

            // Reset name and type fields
            $('#schemafy-manual-name').val('');
            $('#schemafy-manual-type').val('');

            // Update line numbers and stats if editor is available
            if (window.SchemafyEditor) {
                if (typeof window.SchemafyEditor.updateLineNumbers === 'function') {
                    window.SchemafyEditor.updateLineNumbers();
                }
                if (typeof window.SchemafyEditor.updateEditorStats === 'function') {
                    window.SchemafyEditor.updateEditorStats();
                }
            }
        },

        /**
         * Reset Smart Builder to clean state
         */
        resetSmartBuilder: function() {
            const $form = $('#schemafy-dynamic-schema-form');

            // Remove editing state
            if ($form.length) {
                $form.removeData('editing-id');
                $form.removeData('original-schema-data original-schema-type storage-schema-type schema-builder-type');
            }

            // Reset save button
            this.resetSaveButton();

            // Deselect schema type cards
            $('.schemafy-schema-type-card').removeClass('active');

            // Reset form container to placeholder
            $('#schemafy-builder-form-container').html(`
                <div class="schemafy-builder-placeholder">
                    <span class="dashicons dashicons-arrow-left-alt"></span>
                    <h3>Select a Schema Type</h3>
                    <p>Choose a schema type from the list to start building</p>
                </div>
            `);

            // Reset JSON preview
            $('#schemafy-json-preview code').text('{\n  "@context": "https://schema.org",\n  "@type": "...",\n  "name": "..."\n}');
        },

        /**
         * Parse schema data (handles both string and object)
         * @param {string|Object} data - Schema data
         * @returns {Object} Parsed schema data
         */
        parseSchemaData: function(data) {
            if (typeof data === 'string') {
                return JSON.parse(data);
            }
            return data;
        },

        /**
         * Smart Builder edits one JSON-LD node at a time. Full @graph or array
         * containers should stay in Manual JSON to avoid dropping sibling nodes.
         * @param {*} data Parsed schema data
         * @returns {boolean} Whether the data is safe for Smart Builder
         */
        isSingleSchemaNode: function(data) {
            return !!data &&
                typeof data === 'object' &&
                !Array.isArray(data) &&
                !Array.isArray(data['@graph']);
        },

        /**
         * Flatten nested schema data to field names with all possible variations
         * Handles deeply nested objects like publisher.logo.url
         * @param {Object} obj - Object to flatten
         * @param {string} prefix - Current prefix for nested keys
         * @param {number} depth - Current depth level
         * @returns {Object} Flattened object with multiple name variations
         */
        flattenSchemaData: function(obj, prefix, depth) {
            prefix = prefix || '';
            depth = depth || 0;
            const result = {};
            const self = this;

            if (!obj || typeof obj !== 'object' || depth > 5) return result;

            Object.entries(obj).forEach(function([key, value]) {
                // Skip @type, @context, etc.
                if (key.startsWith('@')) return;

                // Build path variations
                const dotPath = prefix ? `${prefix}.${key}` : key;
                const camelPath = prefix
                    ? prefix + key.charAt(0).toUpperCase() + key.slice(1)
                    : key;

                if (Array.isArray(value)) {
                    // Arrays are handled separately in populateArrayFields
                    return;
                } else if (value !== null && typeof value === 'object') {
                    // Recursively flatten nested objects
                    const nested = self.flattenSchemaData(value, key, depth + 1);

                    // Add all nested results
                    Object.entries(nested).forEach(function([nestedKey, nestedValue]) {
                        // Add with current prefix
                        if (prefix) {
                            // e.g., publisher.logo.url -> publisherLogoUrl
                            result[prefix + self.pathToCamelCase(nestedKey)] = nestedValue;
                            // e.g., publisher.logo.url
                            result[prefix + '.' + nestedKey] = nestedValue;
                        }
                        // Add without prefix
                        result[nestedKey] = nestedValue;
                        // Add camelCase version
                        result[self.pathToCamelCase(nestedKey)] = nestedValue;
                    });
                } else {
                    // Primitive value - add multiple name variations
                    result[dotPath] = value;
                    result[camelPath] = value;
                    result[key] = value;

                    // Also add with prefix variations
                    if (prefix) {
                        // e.g., "author.name" -> "authorName"
                        const parts = dotPath.split('.');
                        if (parts.length >= 2) {
                            result[self.pathToCamelCase(dotPath)] = value;
                        }
                    }
                }
            });

            return result;
        },

        /**
         * Convert dot path to camelCase
         * e.g., "publisher.logo.url" -> "publisherLogoUrl"
         * @param {string} path - Dot-separated path
         * @returns {string} CamelCase string
         */
        pathToCamelCase: function(path) {
            if (!path || typeof path !== 'string') return '';
            const parts = path.split('.');
            return parts.map(function(part, index) {
                if (index === 0) return part;
                return part.charAt(0).toUpperCase() + part.slice(1);
            }).join('');
        },

        /**
         * Convert camelCase to field name format
         * @param {string} str - String to convert
         * @returns {string} Converted string
         */
        camelToFieldName: function(str) {
            if (!str || typeof str !== 'string') return '';
            return str.replace(/([A-Z])/g, '.$1').toLowerCase().replace(/^\./, '');
        },

        /**
         * Get value from nested object using dot path
         * e.g., getNestedValue(obj, "publisher.logo.url")
         * @param {Object} obj - Object to search
         * @param {string} path - Dot-separated path
         * @returns {*} Value at path or undefined
         */
        getNestedValue: function(obj, path) {
            if (!obj || !path) return undefined;
            const parts = path.split('.');
            let current = obj;

            for (let i = 0; i < parts.length; i++) {
                if (current === null || current === undefined) return undefined;
                current = current[parts[i]];
            }

            return current;
        },

        /**
         * Generate all possible field name variations for a schema path
         * @param {string} schemaPath - Original schema path (e.g., "author.name")
         * @returns {Array} Array of possible field names
         */
        generateFieldNameVariations: function(schemaPath) {
            if (!schemaPath) return [];

            const variations = [];
            const parts = schemaPath.split('.');

            // Original path
            variations.push(schemaPath);

            // CamelCase version (author.name -> authorName)
            variations.push(this.pathToCamelCase(schemaPath));

            // Last part only (author.name -> name)
            if (parts.length > 1) {
                variations.push(parts[parts.length - 1]);
            }

            // First + Last as camelCase (publisher.logo.url -> publisherUrl)
            if (parts.length > 2) {
                variations.push(parts[0] + parts[parts.length - 1].charAt(0).toUpperCase() + parts[parts.length - 1].slice(1));
            }

            // Full path without dots (publisherlogourl)
            variations.push(parts.join('').toLowerCase());

            // With underscores (publisher_logo_url)
            variations.push(parts.join('_'));

            return variations;
        },

        /**
         * Populate array fields (FAQs, reviews, steps, ingredients, etc.)
         * @param {Object} schemaData - Schema data object
         * @param {jQuery} $form - Form element
         */
        populateArrayFields: function(schemaData, $form) {
            const self = this;

            if (!schemaData || typeof schemaData !== 'object') return;

            // Map of schema array properties to form field configurations
            const arrayMappings = {
                'mainEntity': {
                    fieldName: 'mainEntity',
                    itemFields: function(item) {
                        return {
                            'questionName': (item && item.name) || '',
                            'acceptedAnswer': (item && item.acceptedAnswer && item.acceptedAnswer.text) || ''
                        };
                    }
                },
                'review': {
                    fieldName: 'review',
                    itemFields: function(item) {
                        return {
                            'name': (item && item.name) || '',
                            'author': (item && item.author && item.author.name) || '',
                            'reviewRating': (item && item.reviewRating && item.reviewRating.ratingValue) || '',
                            'reviewBody': (item && item.reviewBody) || '',
                            'datePublished': (item && item.datePublished) || ''
                        };
                    }
                },
                'recipeIngredient': {
                    fieldName: 'recipeIngredient',
                    itemFields: function(item) {
                        return {
                            'ingredient': typeof item === 'string' ? item : ((item && item.ingredient) || '')
                        };
                    }
                },
                'recipeInstructions': {
                    fieldName: 'recipeInstructions',
                    itemFields: function(item) {
                        return {
                            'name': (item && item.name) || '',
                            'text': (item && item.text) || '',
                            'image': (item && item.image) || ''
                        };
                    }
                },
                'step': {
                    fieldName: 'step',
                    itemFields: function(item) {
                        return {
                            'name': (item && item.name) || '',
                            'text': (item && item.text) || '',
                            'image': (item && item.image) || '',
                            'url': (item && item.url) || ''
                        };
                    }
                },
                'supply': {
                    fieldName: 'supply',
                    itemFields: function(item) {
                        return {
                            'name': (item && item.name) || '',
                            'image': (item && item.image) || '',
                            'estimatedCost': (item && item.estimatedCost && item.estimatedCost.value) || ''
                        };
                    }
                },
                'tool': {
                    fieldName: 'tool',
                    itemFields: function(item) {
                        return {
                            'name': (item && item.name) || '',
                            'image': (item && item.image) || ''
                        };
                    }
                },
                'itemListElement': {
                    fieldName: 'itemListElement',
                    itemFields: function(item) {
                        return {
                            'itemName': (item && item.name) || '',
                            'itemUrl': (item && item.item) || ''
                        };
                    }
                },
                'hasPart': {
                    fieldName: 'hasPart',
                    itemFields: function(item) {
                        return {
                            'name': (item && item.name) || '',
                            'startOffset': (item && item.startOffset) || '',
                            'endOffset': (item && item.endOffset) || '',
                            'url': (item && item.url) || ''
                        };
                    }
                },
                'sameAs': {
                    fieldName: 'sameAs',
                    itemFields: function(item) {
                        return {
                            'url': typeof item === 'string' ? item : ((item && item.url) || '')
                        };
                    }
                },
                'hasCourseInstance': {
                    fieldName: 'hasCourseInstance',
                    itemFields: function(item) {
                        return {
                            'courseMode': (item && item.courseMode) || '',
                            'startDate': (item && item.startDate) || '',
                            'endDate': (item && item.endDate) || '',
                            'instructor': (item && item.instructor && item.instructor.name) || ''
                        };
                    }
                },
                'openingHoursSpecification': {
                    fieldName: 'openingHoursSpecification',
                    itemFields: function(item) {
                        return {
                            'dayOfWeek': (item && item.dayOfWeek) || '',
                            'opens': (item && item.opens) || '',
                            'closes': (item && item.closes) || ''
                        };
                    }
                },
                'breadcrumb': {
                    fieldName: 'breadcrumb',
                    itemFields: function(item) {
                        return {
                            'position': (item && item.position) || '',
                            'name': (item && item.name) || '',
                            'item': (item && item.item) || ''
                        };
                    }
                },
                'significantLink': {
                    fieldName: 'significantLink',
                    itemFields: function(item) {
                        return {
                            'url': typeof item === 'string' ? item : ((item && item.url) || '')
                        };
                    }
                }
            };

            // Process each array field
            Object.entries(arrayMappings).forEach(function([schemaKey, config]) {
                let arrayData = schemaData[schemaKey];

                // Handle breadcrumb: can be an object {itemListElement: [...]} or an array
                if (schemaKey === 'breadcrumb' && arrayData && !Array.isArray(arrayData)) {
                    arrayData = arrayData.itemListElement || [];
                }

                if (arrayData && !Array.isArray(arrayData)) {
                    arrayData = [arrayData];
                }

                if (schemaKey === 'openingHoursSpecification' && Array.isArray(arrayData)) {
                    arrayData = self.expandOpeningHoursForBuilder(arrayData);
                }

                if (!arrayData || !Array.isArray(arrayData) || arrayData.length === 0) return;

                self.populateSingleArrayField(config, arrayData, $form);
            });

            // Handle positiveNotes and negativeNotes (Review pros/cons)
            self.populateNotesFields(schemaData, $form);
        },

        expandOpeningHoursForBuilder: function(items) {
            const expanded = [];

            items.forEach(function(item) {
                if (!item) {
                    return;
                }

                const days = Array.isArray(item.dayOfWeek) ? item.dayOfWeek : [item.dayOfWeek];
                days.filter(Boolean).forEach(function(day) {
                    expanded.push({
                        '@type': item['@type'] || 'OpeningHoursSpecification',
                        dayOfWeek: day,
                        opens: item.opens || '',
                        closes: item.closes || ''
                    });
                });
            });

            return expanded;
        },

        /**
         * Populate a single array field
         * @param {Object} config - Field configuration
         * @param {Array} arrayData - Array data to populate
         * @param {jQuery} $form - Form element
         */
        populateSingleArrayField: function(config, arrayData, $form) {
            const $container = $(`#schemafy-array-${config.fieldName}`);
            if (!$container.length) return;

            const $addBtn = $(`.schemafy-add-array-item[data-field-name="${config.fieldName}"]`);

            arrayData.forEach(function(item, index) {
                // Add more items if needed (first item already exists)
                if (index > 0 && $addBtn.length) {
                    $addBtn.trigger('click');
                }

                // Wait for DOM to update
                setTimeout(function() {
                    const itemFields = config.itemFields(item);
                    Object.entries(itemFields).forEach(function([fieldKey, fieldValue]) {
                        if (!fieldValue) return;

                        const fieldSelector = `[name="${config.fieldName}[${index}][${fieldKey}]"]`;
                        const $field = $form.find(fieldSelector);
                        if ($field.length) {
                            $field.val(String(fieldValue)).trigger('input');
                        }
                    });
                }, index * 60);
            });
        },

        /**
         * Populate positiveNotes and negativeNotes fields
         * @param {Object} schemaData - Schema data
         * @param {jQuery} $form - Form element
         */
        populateNotesFields: function(schemaData, $form) {
            ['positiveNotes', 'negativeNotes'].forEach(function(notesKey) {
                const notes = schemaData[notesKey];
                if (!notes || !notes.itemListElement || !Array.isArray(notes.itemListElement)) return;

                const $container = $(`#schemafy-array-${notesKey}`);
                if (!$container.length) return;

                const $addBtn = $(`.schemafy-add-array-item[data-field-name="${notesKey}"]`);

                notes.itemListElement.forEach(function(item, index) {
                    if (index > 0 && $addBtn.length) {
                        $addBtn.trigger('click');
                    }

                    setTimeout(function() {
                        const fieldSelector = `[name="${notesKey}[${index}][name]"]`;
                        const $field = $form.find(fieldSelector);
                        if ($field.length && item && item.name) {
                            $field.val(String(item.name)).trigger('input');
                        }
                    }, index * 60);
                });
            });
        },

        /**
         * Load schema in Manual JSON editor
         * @param {Object} schema - Schema data object
         */
        loadSchemaInManualEditor: function(schema) {
            const self = this;

            // Validate schema
            if (!schema) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_data) || 'Invalid schema data', 'error');
                return;
            }

            const $manualTab = $('[data-tab="manual"]');
            if (!$manualTab.length) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.manual_json_tab_not_found) || 'Manual JSON tab not found', 'error');
                return;
            }

            $manualTab.trigger('click');

            setTimeout(function() {
                // Populate URL field with waiting for AJAX to complete
                self.populateManualEditorUrl(schema.url);

                // Set schema name - correct ID is schemafy-manual-name
                const $nameField = $('#schemafy-manual-name');
                if ($nameField.length && schema.schema_name) {
                    $nameField.val(schema.schema_name);
                }

                // Set schema type - ID is schemafy-manual-type
                const $typeField = $('#schemafy-manual-type');
                if ($typeField.length && schema.schema_type) {
                    $typeField.val(schema.schema_type).trigger('change');
                }

                // Parse JSON and set schema type from @type if not set
                let jsonData;
                try {
                    jsonData = self.parseSchemaData(schema.schema_data);

                    if (!schema.schema_type && jsonData && jsonData['@type']) {
                        const schemaType = jsonData['@type'];
                        if ($typeField.length) {
                            $typeField.val(schemaType).trigger('change');
                        }
                    }
                } catch (e) {
                    jsonData = schema.schema_data;
                }

                // Set JSON content
                const $jsonField = $('#schemafy-manual-json');
                if ($jsonField.length) {
                    const jsonString = typeof jsonData === 'object'
                        ? JSON.stringify(jsonData, null, 2)
                        : String(jsonData);
                    $jsonField.val(jsonString);
                }

                // Update editor UI
                if (window.SchemafyEditor) {
                    if (typeof window.SchemafyEditor.updateLineNumbers === 'function') {
                        window.SchemafyEditor.updateLineNumbers();
                    }
                    if (typeof window.SchemafyEditor.updateEditorStats === 'function') {
                        window.SchemafyEditor.updateEditorStats();
                    }
                }

                // Store editing ID
                const $form = $('#schemafy-manual-form');
                if ($form.length) {
                    $form.data('editing-id', schema.id);
                }

                // Show edit banner
                if (window.SchemafyEditor && typeof window.SchemafyEditor.showEditBanner === 'function') {
                    window.SchemafyEditor.showEditBanner(schema.schema_name || 'Unnamed');
                }

                // Update save button text to "Update"
                const $saveBtn = $('#schemafy-manual-form button[type="submit"]');
                if ($saveBtn.length) {
                    if (!$saveBtn.data('original-html')) {
                        $saveBtn.data('original-html', $saveBtn.html());
                    }
                    $saveBtn.html('<span class="dashicons dashicons-image-rotate"></span> Update Schema');
                }

                // Lock URL controls in edit mode
                setTimeout(function() {
                    self.lockManualUrlControls();
                }, 500);

                window.scrollTo({ top: 0, behavior: 'smooth' });
                self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.schema_loaded_manual_json) || 'Schema loaded in Manual JSON for editing', 'success');
            }, 150);
        },

        /**
         * Escape HTML to prevent XSS
         * @param {string} str - String to escape
         * @returns {string} Escaped string
         */
        // Delegates so there is one escaping implementation, and one that
        // escapes quotes — these values land in attributes as well as text.
        escapeHtml: function(str) {
            return window.SchemafyUtilities.escapeHtml(str);
        },

        /**
         * Escape string for use in jQuery selector
         * @param {string} str - String to escape
         * @returns {string} Escaped string
         */
        escapeSelector: function(str) {
            if (!str || typeof str !== 'string') return '';
            return str.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, '\\$1');
        },

        /**
         * Show notification (wrapper for SchemafyUtilities)
         * @param {string} message - Notification message
         * @param {string} type - Notification type
         */
        showNotification: function(message, type) {
            if (window.SchemafyUtilities && typeof window.SchemafyUtilities.showNotification === 'function') {
                window.SchemafyUtilities.showNotification(message, type);
            } else {
            }
        },

        /**
         * Toggle schema active status
         * @param {number|string} id - Schema ID
         */
        toggleSchema: function(id) {
            const self = this;

            if (!id) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_id) || 'Invalid schema ID', 'error');
                return;
            }

            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_toggle_schema',
                    id: id,
                    nonce: schemafy_ajax.nonce
                },
                success: function(response) {
                    if (response && response.success) {
                        self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.schema_status_toggled) || 'Schema status toggled', 'success');
                        if (window.SchemafyBulk && typeof window.SchemafyBulk.loadBulkSchemas === 'function') {
                            window.SchemafyBulk.loadBulkSchemas();
                        }
                    } else {
                        self.showNotification((response && response.data) || (schemafy_ajax.i18n && schemafy_ajax.i18n.error_toggling) || 'Error toggling schema', 'error');
                    }
                },
                error: function() {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.network_error_toggling) || 'Network error toggling schema', 'error');
                }
            });
        },

        /**
         * Delete a schema
         * @param {number|string} id - Schema ID
         */
        deleteSchema: function(id) {
            const self = this;

            if (!id) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_id) || 'Invalid schema ID', 'error');
                return;
            }

            if (!window.SchemafyUtilities || typeof window.SchemafyUtilities.confirm !== 'function') {
                if (!confirm('Are you sure you want to delete this schema?')) return;
                self.performDelete(id);
                return;
            }

            window.SchemafyUtilities.confirm(
                'Are you sure you want to delete this schema?',
                function() {
                    self.performDelete(id);
                }
            );
        },

        /**
         * Perform the actual delete operation
         * @param {number|string} id - Schema ID
         */
        performDelete: function(id) {
            const self = this;
            const parsedId = parseInt(id);

            // Optimistic UI update: remove row immediately and update local data
            $(`tr[data-id="${parsedId}"]`).fadeOut(200, function() { $(this).remove(); });
            if (window.SchemafyBulk && window.SchemafyBulk.allBulkSchemas) {
                window.SchemafyBulk.allBulkSchemas = window.SchemafyBulk.allBulkSchemas.filter(s => parseInt(s.id) !== parsedId);
                window.SchemafyBulk.updateBulkStats(window.SchemafyBulk.allBulkSchemas);
            }

            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_delete_schema',
                    id: id,
                    nonce: schemafy_ajax.nonce
                },
                success: function(response) {
                    if (response && response.success) {
                        self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.schema_deleted) || 'Schema deleted successfully', 'success');
                        // Refresh health stats in bulk tab
                        if (window.SchemafyBulk && typeof window.SchemafyBulk.refreshBulkHealthStats === 'function') {
                            window.SchemafyBulk.refreshBulkHealthStats();
                        }
                        if (window.SchemafyDashboard && typeof window.SchemafyDashboard.refreshStats === 'function') {
                            window.SchemafyDashboard.refreshStats();
                        }
                    } else {
                        self.showNotification((response && response.data) || (schemafy_ajax.i18n && schemafy_ajax.i18n.error_deleting) || 'Error deleting schema', 'error');
                        // Revert: reload from server since delete failed
                        if (window.SchemafyBulk && typeof window.SchemafyBulk.loadBulkSchemas === 'function') {
                            window.SchemafyBulk.loadBulkSchemas();
                        }
                    }
                },
                error: function() {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.network_error_deleting) || 'Network error while deleting schema', 'error');
                    if (window.SchemafyBulk && typeof window.SchemafyBulk.loadBulkSchemas === 'function') {
                        window.SchemafyBulk.loadBulkSchemas();
                    }
                }
            });
        },

        /**
         * Toggle schema from URL detail view
         * @param {number|string} id - Schema ID
         */
        toggleSchemaFromUrlView: function(id) {
            this.toggleSchema(id);
        },

        /**
         * Duplicate schema from URL view
         * @param {number|string} id - Schema ID
         */
        duplicateSchemaFromUrlView: function(id) {
            const self = this;

            if (!id) {
                this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.invalid_schema_id) || 'Invalid schema ID', 'error');
                return;
            }

            this.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.duplicating_schema) || 'Duplicating schema...', 'info');

            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_duplicate_schema',
                    id: id,
                    nonce: schemafy_ajax.nonce
                },
                success: function(response) {
                    if (response && response.success) {
                        self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.schema_duplicated) || 'Schema duplicated successfully', 'success');
                        if (window.SchemafyBulk && typeof window.SchemafyBulk.loadBulkSchemas === 'function') {
                            window.SchemafyBulk.loadBulkSchemas();
                        }
                    } else {
                        self.showNotification((response && response.data) || (schemafy_ajax.i18n && schemafy_ajax.i18n.error_duplicating) || 'Error duplicating schema', 'error');
                    }
                },
                error: function() {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.network_error) || 'Network error', 'error');
                }
            });
        },

        /**
         * Delete schema from URL view
         * @param {number|string} id - Schema ID
         */
        deleteSchemaFromUrlView: function(id) {
            this.deleteSchema(id);
        },

        /**
         * Handle duplicate schema scenario
         * @param {string} url - Target URL
         * @param {string} schemaType - Schema type
         * @param {string} schemaName - Schema name
         * @param {Array} existingSchemas - Existing schemas of same type
         * @param {Object} formData - Form data
         * @param {string} json - JSON string
         */
        handleDuplicateSchema: function(url, schemaType, schemaName, existingSchemas, formData, json) {
            const self = this;

            if (!existingSchemas || !existingSchemas.length) {
                this.proceedToSaveSchema(url, schemaName, schemaType, formData, json);
                return;
            }

            const existing = existingSchemas[0];
            const safeSchemaType = this.escapeHtml(schemaType);
            const safeSchemaName = this.escapeHtml(existing.schema_name || 'Unknown');
            const safeCreatedAt = this.escapeHtml(existing.created_at || 'Unknown');

            const dupI18n = (schemafy_ajax && schemafy_ajax.i18n) || {};
            const modalHtml = `
                <div class="schemafy-duplicate-modal-overlay" id="schemafy-duplicate-modal" role="dialog" aria-modal="true">
                    <div class="schemafy-duplicate-modal">
                        <div class="schemafy-duplicate-modal-header">
                            <h3>${dupI18n.duplicate_detected_title || 'Duplicate Schema Detected'}</h3>
                        </div>
                        <div class="schemafy-duplicate-modal-body">
                            <p><strong>${(dupI18n.duplicate_exists_msg || 'A schema of type "%s" already exists for this URL:').replace('%s', safeSchemaType)}</strong></p>
                            <div class="schemafy-existing-schema-info">
                                <div class="schemafy-info-row">
                                    <span class="schemafy-info-label">${dupI18n.name_label || 'Name:'}</span>
                                    <span class="schemafy-info-value">${safeSchemaName}</span>
                                </div>
                                <div class="schemafy-info-row">
                                    <span class="schemafy-info-label">${dupI18n.created_label || 'Created:'}</span>
                                    <span class="schemafy-info-value">${safeCreatedAt}</span>
                                </div>
                                <div class="schemafy-info-row">
                                    <span class="schemafy-info-label">${dupI18n.status_label || 'Status:'}</span>
                                    <span class="schemafy-info-value">${existing.is_active ? (dupI18n.status_active || 'Active') : (dupI18n.status_inactive || 'Inactive')}</span>
                                </div>
                            </div>

                            <div class="schemafy-seo-warning">
                                <strong>${dupI18n.seo_recommendation_label || 'SEO Recommendation:'}</strong>
                                <p>${dupI18n.duplicate_seo_warning || 'Having multiple schemas of the same type for a URL can confuse search engines and negatively affect your SEO.'}</p>
                            </div>

                            <p><strong>${dupI18n.what_to_do || 'What would you like to do?'}</strong></p>
                        </div>
                        <div class="schemafy-duplicate-modal-actions">
                            <button class="schemafy-btn schemafy-btn-primary" data-action="replace">
                                <span class="dashicons dashicons-image-rotate"></span> ${dupI18n.replace_recommended || 'Replace (Recommended)'}
                            </button>
                            <button class="schemafy-btn schemafy-btn-secondary" data-action="add">
                                <span class="dashicons dashicons-plus-alt"></span> ${dupI18n.add_anyway || 'Add Anyway'}
                            </button>
                            <button class="schemafy-btn schemafy-btn-tertiary" data-action="cancel">
                                <span class="dashicons dashicons-no"></span> ${dupI18n.cancel_label || 'Cancel'}
                            </button>
                        </div>
                    </div>
                </div>
            `;

            // Remove existing modal
            $('#schemafy-duplicate-modal').remove();
            $('body').append(modalHtml);

            const $modal = $('#schemafy-duplicate-modal');

            $modal.on('click', '[data-action]', function() {
                const action = $(this).data('action');

                if (action === 'replace') {
                    self.deleteSchemaAndSave(existing.id, url, schemaName, schemaType, formData, json);
                } else if (action === 'add') {
                    self.proceedToSaveSchema(url, schemaName, schemaType, formData, json);
                } else {
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.operation_cancelled) || 'Operation cancelled', 'info');
                }

                $modal.fadeOut(200, function() {
                    $(this).remove();
                });
            });

            // Close on Escape
            $(document).on('keydown.schemafyDuplicateModal', function(e) {
                if (e.key === 'Escape' || e.keyCode === 27) {
                    $modal.fadeOut(200, function() {
                        $(this).remove();
                    });
                    $(document).off('keydown.schemafyDuplicateModal');
                }
            });
        },

        /**
         * Delete existing and save new schema
         * @param {number|string} schemaId - Schema ID to delete
         * @param {string} url - Target URL
         * @param {string} schemaName - Schema name
         * @param {string} schemaType - Schema type
         * @param {Object} formData - Form data
         * @param {string} json - JSON string
         */
        deleteSchemaAndSave: function(schemaId, url, schemaName, schemaType, formData, json) {
            const self = this;
            const $saveBtn = $('#schemafy-save-builder-schema');
            const originalText = $saveBtn.length ? $saveBtn.html() : '';

            if ($saveBtn.length) {
                $saveBtn.prop('disabled', true).html('<span class="dashicons dashicons-update-alt schemafy-spin"></span> ' + ((schemafy_ajax.i18n && schemafy_ajax.i18n.replacing) || 'Replacing...'));
            }

            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_delete_schema',
                    nonce: schemafy_ajax.nonce,
                    id: schemaId
                },
                success: function() {
                    self.proceedToSaveSchema(url, schemaName, schemaType, formData, json);
                },
                error: function() {
                    if ($saveBtn.length) {
                        $saveBtn.prop('disabled', false).html(originalText);
                    }
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.error_deleting_existing) || 'Error deleting existing schema', 'error');
                }
            });
        },

        /**
         * Save schema to database
         * @param {string} url - Target URL
         * @param {string} schemaName - Schema name
         * @param {string} schemaType - Schema type
         * @param {Object} _formData - Form data (unused but kept for API compatibility)
         * @param {string} json - JSON string
         */
        proceedToSaveSchema: function(url, schemaName, schemaType, _formData, json) {
            const self = this;
            const $saveBtn = $('#schemafy-save-builder-schema');
            const originalText = $saveBtn.length ? $saveBtn.html() : '';

            // Set saving flag to prevent duplicate submissions
            if (window.SchemafyBuilder) {
                window.SchemafyBuilder.isSavingSchema = true;
            }

            if ($saveBtn.length) {
                $saveBtn.prop('disabled', true).addClass('schemafy-saving').html('<span class="dashicons dashicons-update-alt schemafy-spin"></span> ' + ((schemafy_ajax.i18n && schemafy_ajax.i18n.saving) || 'Saving...'));
            }

            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_save_schema',
                    nonce: schemafy_ajax.nonce,
                    url: url,
                    schema_name: schemaName,
                    schema_type: schemaType,
                    schema_data: json,
                    meta_title: '',
                    meta_description: ''
                },
                success: function(response) {
                    // Reset saving flag
                    if (window.SchemafyBuilder) {
                        window.SchemafyBuilder.isSavingSchema = false;
                    }

                    if (response && response.success) {
                        self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.schema_saved) || 'Schema saved successfully!', 'success');

                        // Re-enable and restore save button immediately
                        if ($saveBtn.length) {
                            $saveBtn.prop('disabled', false).removeClass('schemafy-saving').html(originalText);
                        }

                        // Mark this form with a token so the timeout can detect if the user
                        // already started a new schema before the 500ms cleanup fires.
                        const savedToken = Date.now();
                        const $formAtSave = $('#schemafy-dynamic-schema-form');
                        if ($formAtSave.length) {
                            $formAtSave.data('saved-token', savedToken);
                        }

                        setTimeout(function() {
                            // If user has already clicked a new schema type, the form container
                            // will have a different form (no saved-token or a different token).
                            // In that case, skip the cleanup to avoid destroying the new form.
                            const $currentForm = $('#schemafy-dynamic-schema-form');
                            const currentToken = $currentForm.data('saved-token');
                            if ($currentForm.length && currentToken !== savedToken) {
                                // User started a new schema — only refresh data, don't touch UI
                                if (window.SchemafyDashboard && typeof window.SchemafyDashboard.refreshStats === 'function') {
                                    window.SchemafyDashboard.refreshStats();
                                }
                                if (window.SchemafyBulk && typeof window.SchemafyBulk.loadBulkSchemas === 'function') {
                                    window.SchemafyBulk.loadBulkSchemas();
                                }
                                return;
                            }

                            if ($currentForm.length && $currentForm[0]) {
                                $currentForm[0].reset();
                            }
                            $('.schemafy-schema-type-card').removeClass('active');
                            const richTestUrl = 'https://search.google.com/test/rich-results?url=' + encodeURIComponent(url);
                            $('#schemafy-builder-form-container').html(`
                                <div class="schemafy-builder-placeholder">
                                    <span class="dashicons dashicons-yes-alt" style="color: var(--schemafy-success);"></span>
                                    <h3>${(schemafy_ajax.i18n && schemafy_ajax.i18n.schema_saved_title) || 'Schema Saved Successfully!'}</h3>
                                    <p style="margin-bottom: 12px;">
                                        <a href="${url}" target="_blank" rel="noopener" style="color: var(--schemafy-primary); text-decoration: underline; font-weight: 500;">
                                            <span class="dashicons dashicons-external" style="font-size: 14px; vertical-align: middle;"></span>
                                            View page
                                        </a>
                                        &nbsp;&bull;&nbsp;
                                        <a href="${richTestUrl}" target="_blank" rel="noopener" style="color: var(--schemafy-primary); text-decoration: underline; font-weight: 500;">
                                            <span class="dashicons dashicons-search" style="font-size: 14px; vertical-align: middle;"></span>
                                            Test in Rich Results
                                        </a>
                                    </p>
                                    <p>${(schemafy_ajax.i18n && schemafy_ajax.i18n.select_another_type) || 'Select another type to create a new schema'}</p>
                                </div>
                            `);
                            $('#schemafy-json-preview code').text('{\n  "@context": "https://schema.org",\n  "@type": "...",\n  "name": "..."\n}');

                            // Refresh dashboard and bulk manager
                            if (window.SchemafyDashboard && typeof window.SchemafyDashboard.refreshStats === 'function') {
                                window.SchemafyDashboard.refreshStats();
                            }
                            if (window.SchemafyBulk && typeof window.SchemafyBulk.loadBulkSchemas === 'function') {
                                window.SchemafyBulk.loadBulkSchemas();
                            }
                        }, 500);
                    } else {
                        self.showNotification((response && response.data) || (schemafy_ajax.i18n && schemafy_ajax.i18n.error_saving) || 'Error saving schema', 'error');
                        if ($saveBtn.length) {
                            $saveBtn.prop('disabled', false).removeClass('schemafy-saving').html(originalText);
                        }
                    }
                },
                error: function() {
                    // Reset saving flag
                    if (window.SchemafyBuilder) {
                        window.SchemafyBuilder.isSavingSchema = false;
                    }

                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.network_error_saving) || 'Network error: Could not save schema', 'error');
                    if ($saveBtn.length) {
                        $saveBtn.prop('disabled', false).removeClass('schemafy-saving').html(originalText);
                    }
                }
            });
        },

        /**
         * Save schema to multiple URLs at once (bulk save)
         * @param {Array} urls - Array of URL strings
         * @param {string} schemaName - Schema name
         * @param {string} schemaType - Schema type
         * @param {Object} _formData - Form data (unused, kept for API compat)
         * @param {string} json - JSON-LD string
         * @param {Function} [onSuccess] - Optional callback on success
         */
        proceedToSaveSchemaBulk: function(urls, schemaName, schemaType, _formData, json, onSuccess) {
            const self = this;
            const $saveBtn = $('#schemafy-save-builder-schema');
            const originalText = $saveBtn.length ? $saveBtn.html() : '';
            const urlCount = urls.length;

            // Set saving flag
            if (window.SchemafyBuilder) {
                window.SchemafyBuilder.isSavingSchema = true;
            }

            if ($saveBtn.length) {
                $saveBtn.prop('disabled', true).addClass('schemafy-saving')
                    .html(`<span class="dashicons dashicons-update-alt schemafy-spin"></span> Saving to ${urlCount} URLs...`);
            }

            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_save_schema_bulk',
                    nonce: schemafy_ajax.nonce,
                    'urls[]': urls,
                    schema_name: schemaName,
                    schema_type: schemaType,
                    schema_data: json,
                    meta_title: '',
                    meta_description: ''
                },
                traditional: true,
                success: function(response) {
                    if (window.SchemafyBuilder) {
                        window.SchemafyBuilder.isSavingSchema = false;
                    }

                    if (response && response.success) {
                        const count = response.data.count || urlCount;
                        const failed = response.data.failed || [];
                        let message = `Schema saved to ${count} URL${count !== 1 ? 's' : ''} successfully!`;
                        if (failed.length > 0) {
                            message += ` (${failed.length} failed)`;
                        }
                        self.showNotification(message, 'success');

                        if ($saveBtn.length) {
                            $saveBtn.prop('disabled', false).removeClass('schemafy-saving').html(originalText);
                        }

                        // Call custom callback if provided
                        if (typeof onSuccess === 'function') {
                            onSuccess(response.data);
                            return;
                        }

                        // Default: reset Smart Builder
                        setTimeout(function() {
                            const $form = $('#schemafy-dynamic-schema-form');
                            if ($form.length && $form[0]) {
                                $form[0].reset();
                            }
                            // Clear URL chips
                            $('#schemafy-selected-urls').empty();
                            $('#schemafy-target-urls').val('[]');

                            $('.schemafy-schema-type-card').removeClass('active');
                            $('#schemafy-builder-form-container').html(`
                                <div class="schemafy-builder-placeholder">
                                    <span class="dashicons dashicons-yes-alt" style="color: var(--schemafy-success);"></span>
                                    <h3>Schema Saved to ${count} URLs!</h3>
                                    <p>Select another type to create a new schema</p>
                                </div>
                            `);
                            $('#schemafy-json-preview code').text('{\n  "@context": "https://schema.org",\n  "@type": "...",\n  "name": "..."\n}');

                            if (window.SchemafyDashboard && typeof window.SchemafyDashboard.refreshStats === 'function') {
                                window.SchemafyDashboard.refreshStats();
                            }
                            if (window.SchemafyBulk && typeof window.SchemafyBulk.loadBulkSchemas === 'function') {
                                window.SchemafyBulk.loadBulkSchemas();
                            }
                        }, 500);
                    } else {
                        self.showNotification((response && response.data) || (schemafy_ajax.i18n && schemafy_ajax.i18n.error_saving_schemas) || 'Error saving schemas', 'error');
                        if ($saveBtn.length) {
                            $saveBtn.prop('disabled', false).removeClass('schemafy-saving').html(originalText);
                        }
                    }
                },
                error: function() {
                    if (window.SchemafyBuilder) {
                        window.SchemafyBuilder.isSavingSchema = false;
                    }
                    self.showNotification((schemafy_ajax.i18n && schemafy_ajax.i18n.network_error_saving_schemas) || 'Network error: Could not save schemas', 'error');
                    if ($saveBtn.length) {
                        $saveBtn.prop('disabled', false).removeClass('schemafy-saving').html(originalText);
                    }
                }
            });
        },

        /**
         * Get all related schema types for duplicate detection.
         * For example, "Article" returns ["Article", "NewsArticle", "BlogPosting"]
         * so we can detect existing schemas of any subtype in the same family.
         * @param {string} schemaType - Schema type to check
         * @returns {Array} Array of related type strings
         */
        getRelatedSchemaTypes: function(schemaType) {
            // Build reverse mapping: card type -> all schema types that map to it
            const familyMap = {};
            const schemaTypeMap = this.getSchemaTypeMap();
            Object.entries(schemaTypeMap).forEach(function([subtype, cardType]) {
                if (!familyMap[cardType]) familyMap[cardType] = [cardType];
                familyMap[cardType].push(subtype);
            });

            // The input type might be a card type (e.g., "Article") or a schema type (e.g., "NewsArticle")
            const cardType = schemaTypeMap[schemaType] || schemaType;
            return familyMap[cardType] || [schemaType];
        },

        /**
         * Check if schema of same type (or related subtype) exists for URL
         * @param {string} url - Target URL
         * @param {string} schemaType - Schema type
         * @param {Function} callback - Callback with array of matching schemas
         */
        checkExistingSchema: function(url, schemaType, callback) {
            const self = this;

            if (typeof callback !== 'function') {
                return;
            }

            if (!url || !schemaType) {
                callback([]);
                return;
            }

            // Get all related types (e.g., Article + NewsArticle + BlogPosting)
            const relatedTypes = self.getRelatedSchemaTypes(schemaType);

            $.ajax({
                url: schemafy_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'schemafy_get_schemas_by_url',
                    nonce: schemafy_ajax.nonce,
                    url: url
                },
                success: function(response) {
                    if (response && response.success && response.data && response.data.schemas) {
                        const sameType = response.data.schemas.filter(function(s) {
                            return s && relatedTypes.indexOf(s.schema_type) !== -1;
                        });
                        callback(sameType);
                    } else {
                        callback([]);
                    }
                },
                error: function() {
                    callback([]);
                }
            });
        }
    };

})(jQuery);
