<?php

namespace Bricksforge;

if (!defined('ABSPATH')) {
    exit;
}

require_once(BRICKSFORGE_PATH . '/includes/elements/pro-forms/ProFormsLogger.php');

use Bricksforge\ProForms\ProFormsLogger;

/**
 * CPT Sync Handler for External API Loops
 * 
 * This class handles the synchronization of API data to WordPress Custom Post Types.
 */
class CptSync
{
    /**
     * ExternalApiLoops instance
     *
     * @var ExternalApiLoops|null
     */
    private $external_api_loops = null;

    /**
     * Child CPTs to add as sub-menu items
     *
     * @var array
     */
    private $child_cpts_for_menu = [];

    /**
     * Constructor
     */
    public function __construct()
    {
        // Don't initialize ExternalApiLoops here - it will be loaded lazily when needed

        // Register custom cron schedules
        add_filter('cron_schedules', [$this, 'add_custom_cron_schedules']);

        // Hook into WordPress init to register custom post types
        add_action('init', [$this, 'register_custom_post_types']);

        // Hook for scheduled sync
        add_action('bricksforge_cpt_sync', [$this, 'run_scheduled_sync'], 10, 1);

        // Schedule cron jobs on plugin init
        add_action('init', [$this, 'schedule_all_cron_jobs']);

        // Cleanup unused custom parent menus
        add_action('init', [$this, 'cleanup_unused_parent_menus'], 99);

        // Admin notices removed - errors are now logged to the Logs system

        // Add metabox for displaying API custom fields
        add_action('add_meta_boxes', [$this, 'add_api_fields_metabox'], 1);

        // Handle cascading delete of child posts when parent is deleted
        add_action('before_delete_post', [$this, 'delete_child_posts_on_parent_delete'], 10, 2);

        // Add child CPTs as sub-menu items under parent CPT
        add_action('admin_menu', [$this, 'add_child_cpt_submenu_items'], 20);

        // Remove "Add New" submenu items for CPT Sync post types
        add_action('admin_menu', [$this, 'remove_add_new_submenu_items'], 25);

        // Filter Child CPT admin list by parent post ID
        add_action('pre_get_posts', [$this, 'filter_child_cpt_by_parent']);
    }

    /**
     * Add custom cron schedules dynamically based on cron expressions
     *
     * @param array $schedules Existing cron schedules
     * @return array Modified cron schedules
     */
    public function add_custom_cron_schedules($schedules)
    {
        // Always add minutely schedule (every minute) if it doesn't exist
        if (!isset($schedules['minutely'])) {
            $schedules['minutely'] = [
                'interval' => 60, // 60 seconds
                'display' => __('Every Minute', 'bricksforge')
            ];
        }

        // Get all items to check for custom cron expressions
        $items = get_option('brf_external_api_loops', []);

        if (empty($items)) {
            return $schedules;
        }

        // Parse each item's cron expression and create custom schedules as needed
        foreach ($items as $item) {
            $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);

            if (!$enableCptSync) {
                continue;
            }

            // Get cron expression
            if (is_array($item)) {
                $cron_expression = isset($item['cptSettings']['cronExpression']) ? $item['cptSettings']['cronExpression'] : '0 0 * * *';
            } else {
                $cron_expression = isset($item->cptSettings->cronExpression) ? $item->cptSettings->cronExpression : '0 0 * * *';
            }

            // Skip manual sync items
            if ($cron_expression === 'manual' || $cron_expression === '' || $cron_expression === null) {
                continue;
            }

            // Parse cron expression to get interval in seconds
            $interval = $this->parse_cron_expression_to_interval($cron_expression);

            if ($interval && $interval > 0) {
                // For 60 seconds, use 'minutely' instead of custom schedule
                if ($interval === 60) {
                    continue; // Already added above
                }

                // Create a unique schedule name based on interval
                $schedule_name = 'brf_custom_' . $interval;

                // Only add if it doesn't exist and interval is valid
                if (!isset($schedules[$schedule_name]) && $interval >= 60) {
                    $schedules[$schedule_name] = [
                        'interval' => $interval,
                        'display' => sprintf(__('Every %s', 'bricksforge'), $this->format_interval($interval))
                    ];
                }
            }
        }

        return $schedules;
    }

    /**
     * Format interval in seconds to human-readable format
     *
     * @param int $seconds Interval in seconds
     * @return string Human-readable interval
     */
    private function format_interval($seconds)
    {
        if ($seconds < 60) {
            return $seconds . ' ' . __('second(s)', 'bricksforge');
        } elseif ($seconds < 3600) {
            $minutes = floor($seconds / 60);
            return $minutes . ' ' . __('minute(s)', 'bricksforge');
        } elseif ($seconds < 86400) {
            $hours = floor($seconds / 3600);
            return $hours . ' ' . __('hour(s)', 'bricksforge');
        } else {
            $days = floor($seconds / 86400);
            return $days . ' ' . __('day(s)', 'bricksforge');
        }
    }

    /**
     * Get a property from an item (handles both array and object formats)
     *
     * @param mixed $item The item (array or object)
     * @param string $path The property path (e.g., 'id', 'cptSettings.cptSlug')
     * @param mixed $default Default value if not found
     * @return mixed The property value or default
     */
    private function get_item_property($item, $path, $default = null)
    {
        $parts = explode('.', $path);
        $current = $item;

        foreach ($parts as $part) {
            if (is_array($current)) {
                if (!isset($current[$part])) {
                    return $default;
                }
                $current = $current[$part];
            } elseif (is_object($current)) {
                if (!isset($current->$part)) {
                    return $default;
                }
                $current = $current->$part;
            } else {
                return $default;
            }
        }

        return $current;
    }

    /**
     * Get ExternalApiLoops instance (lazy loading)
     *
     * @return ExternalApiLoops
     */
    private function get_external_api_loops()
    {
        if ($this->external_api_loops === null) {
            $this->external_api_loops = new ExternalApiLoops(true);
        }
        return $this->external_api_loops;
    }

    /**
     * Generate CPT slug with proper length limit
     * WordPress requires post type names to be max 20 characters
     *
     * @param string $user_slug The user-defined slug
     * @return string The final CPT slug (max 20 chars)
     */
    private function get_cpt_slug($user_slug)
    {
        // Use shorter prefix: 'bfa-' (4 chars) instead of 'brf-api-' (8 chars)
        $prefix = 'bfa-';
        $max_length = 20;
        $max_user_length = $max_length - strlen($prefix); // 16 chars for user slug

        // Sanitize and truncate user slug
        $sanitized = sanitize_title($user_slug);
        $truncated = substr($sanitized, 0, $max_user_length);

        return $prefix . $truncated;
    }

    /**
     * Register Custom Post Types for all active API items with CPT sync enabled
     *
     * @return void
     */
    public function register_custom_post_types()
    {
        // Clear cache before loading to ensure we get fresh data
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');

        $items = get_option('brf_external_api_loops', []);

        if (empty($items)) {
            return;
        }

        // Reset child CPTs for menu
        $this->child_cpts_for_menu = [];

        // Track custom parents to register them
        $custom_parents = [];

        foreach ($items as $item) {
            // Handle both array and object formats
            $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);

            if (!$enableCptSync) {
                continue;
            }

            $cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
            if (empty($cpt_slug_value)) {
                continue;
            }

            $cpt_slug = $this->get_cpt_slug($cpt_slug_value);
            $label = $this->get_item_property($item, 'label', 'API Data');

            // Get menu settings
            $menu_settings = is_array($item) ? (isset($item['cptSettings']['menuSettings']) ? $item['cptSettings']['menuSettings'] : null) : (isset($item->cptSettings->menuSettings) ? $item->cptSettings->menuSettings : null);

            if (is_array($menu_settings)) {
                $create_menu_item = isset($menu_settings['createMenuItem']) ? $menu_settings['createMenuItem'] : true;
                $menu_position = isset($menu_settings['menuPosition']) ? $menu_settings['menuPosition'] : 'parent';
                $menu_parent = isset($menu_settings['menuParent']) ? $menu_settings['menuParent'] : '';
                $menu_parent_label = isset($menu_settings['menuParentLabel']) ? $menu_settings['menuParentLabel'] : '';
            } else {
                $create_menu_item = isset($menu_settings->createMenuItem) ? $menu_settings->createMenuItem : true;
                $menu_position = isset($menu_settings->menuPosition) ? $menu_settings->menuPosition : 'parent';
                $menu_parent = isset($menu_settings->menuParent) ? $menu_settings->menuParent : '';
                $menu_parent_label = isset($menu_settings->menuParentLabel) ? $menu_settings->menuParentLabel : '';
            }

            // Determine menu configuration
            $show_in_menu = true;
            $menu_icon = 'dashicons-database'; // API marker icon
            $menu_pos = 20;

            if (!$create_menu_item) {
                $show_in_menu = false;
            } elseif ($menu_position === 'sub' && !empty($menu_parent)) {
                // Show as submenu under existing menu
                $show_in_menu = $menu_parent;
                $menu_icon = null; // Submenu items don't need icons
            } elseif ($menu_position === 'custom_parent' && !empty($menu_parent_label)) {
                // Track custom parent for later registration
                $parent_slug = 'brf-api-parent-' . sanitize_title($menu_parent_label);
                $custom_parents[$parent_slug] = $menu_parent_label;
                $show_in_menu = $parent_slug;
                $menu_icon = null;
            }

            $cpt_args = [
                'labels' => [
                    'name' => $label, // API marker in label
                    'singular_name' => $label,
                    'add_new' => 'Add New ' . $label,
                    'add_new_item' => 'Add New ' . $label,
                    'edit_item' => 'Edit ' . $label,
                    'new_item' => 'New ' . $label,
                    'view_item' => 'View ' . $label,
                    'search_items' => 'Search ' . $label,
                    'not_found' => 'No ' . $label . ' found',
                    'not_found_in_trash' => 'No ' . $label . ' found in Trash',
                ],
                'public' => true,
                'publicly_queryable' => true,
                'show_ui' => true,
                'show_in_menu' => $show_in_menu,
                'show_in_rest' => true,
                'query_var' => true,
                'rewrite' => ['slug' => $cpt_slug],
                'capability_type' => 'post',
                'has_archive' => true,
                'hierarchical' => false,
                'supports' => ['title', 'editor', 'custom-fields'],
                'show_in_nav_menus' => true,
            ];

            // Only add menu_position and menu_icon for top-level menus
            if ($menu_position === 'parent') {
                $cpt_args['menu_position'] = $menu_pos;
                $cpt_args['menu_icon'] = $menu_icon;
            }

            register_post_type($cpt_slug, $cpt_args);

            // Register Child CPTs for Nested Arrays configured as Child CPTs
            // Pass show_in_menu value so child CPTs can appear as sub-items
            $this->register_nested_array_cpts($item, $cpt_slug, $label, $show_in_menu);
        }

        // Register custom parent pages if any
        foreach ($custom_parents as $parent_slug => $parent_label) {
            $this->register_custom_parent_menu($parent_slug, $parent_label);
        }

        // Flush rewrite rules if CPT registration changed
        $this->maybe_flush_rewrite_rules();
    }

    /**
     * Flush rewrite rules if needed
     *
     * @return void
     */
    private function maybe_flush_rewrite_rules()
    {
        // Check if we need to flush rewrite rules
        $needs_flush = get_transient('brf_cpt_sync_needs_flush');

        if ($needs_flush) {
            flush_rewrite_rules();
            delete_transient('brf_cpt_sync_needs_flush');
        }
    }

    /**
     * Register a custom parent menu page for grouping CPTs
     *
     * @param string $parent_slug The parent menu slug
     * @param string $parent_label The parent menu label
     * @return void
     */
    private function register_custom_parent_menu($parent_slug, $parent_label)
    {
        // Check if this parent page is already registered
        if (get_option('brf-cpt-parent-' . $parent_slug)) {
            return;
        }

        add_menu_page(
            $parent_label,                      // Page title
            $parent_label . ' 🔗',              // Menu title with API marker
            'edit_posts',                       // Capability
            $parent_slug,                       // Menu slug
            [$this, 'render_parent_page'],      // Callback function
            'dashicons-database-view',          // Icon
            20                                  // Position
        );

        // Mark as registered
        update_option('brf-cpt-parent-' . $parent_slug, true);
    }

    /**
     * Register Child CPTs for Nested Arrays configured as Child CPTs
     *
     * @param mixed $item The API item configuration
     * @param string $parent_cpt_slug The parent CPT slug
     * @param string $parent_label The parent label
     * @param mixed $parent_show_in_menu The parent's show_in_menu value (true, false, or menu slug)
     * @return void
     */
    private function register_nested_array_cpts($item, $parent_cpt_slug, $parent_label, $parent_show_in_menu = true)
    {
        $childCpts = $this->get_item_property($item, 'cptSettings.childCpts', []);

        if (empty($childCpts) || !is_array($childCpts)) {
            return;
        }

        foreach ($childCpts as $child_config) {
            $array_key = $this->get_item_property($child_config, 'arrayKey', '');
            $childCptLabel = $this->get_item_property($child_config, 'label', '');
            $type = $this->get_item_property($child_config, 'type', 'cpt'); // Default to 'cpt' for backwards compatibility

            if (empty($array_key)) {
                continue;
            }

            // If type is taxonomy, register taxonomy instead of CPT
            if ($type === 'taxonomy') {
                $this->register_taxonomy_for_parent($item, $parent_cpt_slug, $parent_label, $child_config);
                continue;
            }

            // Generate child CPT slug: bfa-{parent_slug}-{array_key}
            // Need to ensure it's max 20 characters
            $child_slug_base = $parent_cpt_slug . '-' . sanitize_title($array_key);
            // Truncate if needed
            if (strlen($child_slug_base) > 20) {
                $child_slug_base = substr($child_slug_base, 0, 20);
            }
            $child_cpt_slug = $child_slug_base;

            // Use custom label or generate one
            if (empty($childCptLabel)) {
                $childCptLabel = $parent_label . ' ' . ucfirst($array_key);
            }

            // Register the child CPT (hidden from menu, we'll add it manually as sub-item)
            $child_cpt_args = [
                'labels' => [
                    'name' => $childCptLabel,
                    'singular_name' => $childCptLabel,
                    'add_new' => 'Add New',
                    'add_new_item' => 'Add New ' . $childCptLabel,
                    'edit_item' => 'Edit ' . $childCptLabel,
                    'new_item' => 'New ' . $childCptLabel,
                    'view_item' => 'View ' . $childCptLabel,
                    'search_items' => 'Search ' . $childCptLabel,
                    'not_found' => 'No items found',
                    'not_found_in_trash' => 'No items found in Trash',
                ],
                'public' => true,
                'publicly_queryable' => true,
                'show_ui' => true,
                'show_in_menu' => false, // Hidden from menu, we'll add manually as sub-item
                'show_in_rest' => true,
                'query_var' => true,
                'rewrite' => ['slug' => $child_cpt_slug],
                'capability_type' => 'post',
                'has_archive' => false,
                'hierarchical' => false,
                'supports' => ['title', 'editor', 'custom-fields'],
                'show_in_nav_menus' => false,
            ];

            register_post_type($child_cpt_slug, $child_cpt_args);

            // Store child CPT info for later menu registration
            // We'll use this in admin_menu hook to add sub-menu items
            if (!isset($this->child_cpts_for_menu)) {
                $this->child_cpts_for_menu = [];
            }
            $this->child_cpts_for_menu[] = [
                'child_cpt_slug' => $child_cpt_slug,
                'child_cpt_label' => $childCptLabel,
                'parent_cpt_slug' => $parent_cpt_slug,
                'parent_show_in_menu' => $parent_show_in_menu,
            ];
        }
    }

    /**
     * Register a taxonomy for parent CPT from nested array configuration
     *
     * @param array $item The API item configuration
     * @param string $parent_cpt_slug The parent CPT slug
     * @param string $parent_label The parent CPT label
     * @param array $child_config The child configuration for this taxonomy
     * @return void
     */
    private function register_taxonomy_for_parent($item, $parent_cpt_slug, $parent_label, $child_config)
    {
        $array_key = $this->get_item_property($child_config, 'arrayKey', '');
        $label = $this->get_item_property($child_config, 'label', '');
        $hierarchical = $this->get_item_property($child_config, 'hierarchical', false);

        if (empty($array_key)) {
            return;
        }

        // Use custom label or generate one
        if (empty($label)) {
            $label = $parent_label . ' ' . ucfirst($array_key);
        }

        // Generate taxonomy slug: {parent_slug}-{array_key}
        // WordPress taxonomy limit is 32 characters
        $tax_slug = $parent_cpt_slug . '-' . sanitize_title($array_key);
        if (strlen($tax_slug) > 32) {
            $tax_slug = substr($tax_slug, 0, 32);
        }

        // Generate plural label for taxonomies
        $plural_label = $label;
        if (substr($label, -1) !== 's') {
            $plural_label = $label . 's';
        }

        // Taxonomy labels
        $labels = [
            'name' => $plural_label,
            'singular_name' => $label,
            'search_items' => 'Search ' . $plural_label,
            'popular_items' => 'Popular ' . $plural_label,
            'all_items' => 'All ' . $plural_label,
            'parent_item' => 'Parent ' . $label,
            'parent_item_colon' => 'Parent ' . $label . ':',
            'edit_item' => 'Edit ' . $label,
            'update_item' => 'Update ' . $label,
            'add_new_item' => 'Add New ' . $label,
            'new_item_name' => 'New ' . $label . ' Name',
            'separate_items_with_commas' => 'Separate ' . strtolower($plural_label) . ' with commas',
            'add_or_remove_items' => 'Add or remove ' . strtolower($plural_label),
            'choose_from_most_used' => 'Choose from most used ' . strtolower($plural_label),
            'not_found' => 'No ' . strtolower($plural_label) . ' found',
            'menu_name' => $plural_label,
        ];

        // Taxonomy arguments
        $args = [
            'labels' => $labels,
            'hierarchical' => $hierarchical,
            'public' => true,
            'show_ui' => true,
            'show_admin_column' => true,
            'show_in_rest' => true,
            'show_in_nav_menus' => true,
            'show_tagcloud' => !$hierarchical, // Show tag cloud only for non-hierarchical
            'rewrite' => [
                'slug' => $tax_slug,
                'with_front' => false,
                'hierarchical' => $hierarchical,
            ],
            'query_var' => $tax_slug,
            'show_in_quick_edit' => true,
        ];

        // Register the taxonomy for the parent CPT
        register_taxonomy($tax_slug, [$parent_cpt_slug], $args);
    }

    /**
     * Add child CPTs as sub-menu items under parent CPT
     *
     * @return void
     */
    public function add_child_cpt_submenu_items()
    {
        // Check if we have any child CPTs to add
        if (!isset($this->child_cpts_for_menu) || empty($this->child_cpts_for_menu)) {
            return;
        }

        foreach ($this->child_cpts_for_menu as $child_info) {
            $child_cpt_slug = $child_info['child_cpt_slug'];
            $child_cpt_label = $child_info['child_cpt_label'];
            $parent_cpt_slug = $child_info['parent_cpt_slug'];
            $parent_show_in_menu = $child_info['parent_show_in_menu'];

            // Only add sub-menu if parent is shown in menu
            if ($parent_show_in_menu === false || $parent_show_in_menu === null) {
                continue;
            }

            // Determine parent menu slug
            $parent_menu_slug = null;
            if ($parent_show_in_menu === true) {
                // Parent is top-level menu item, use CPT slug
                $parent_menu_slug = 'edit.php?post_type=' . $parent_cpt_slug;
            } elseif (is_string($parent_show_in_menu)) {
                // Parent is under custom menu, use that slug
                $parent_menu_slug = $parent_show_in_menu;
            }

            if (!$parent_menu_slug) {
                continue;
            }

            // Add sub-menu item for child CPT
            // The menu slug should be the post type slug, WordPress will handle the URL
            $child_menu_title = $child_cpt_label;

            // Use add_submenu_page to add child CPT as sub-item
            // Parent slug is the CPT edit URL, child slug is just the post type slug
            add_submenu_page(
                $parent_menu_slug,           // Parent menu slug (edit.php?post_type=parent_cpt_slug)
                $child_menu_title,            // Page title
                $child_menu_title,            // Menu title
                'edit_posts',                 // Capability
                'edit.php?post_type=' . $child_cpt_slug,  // Menu slug (full URL)
                null                          // No callback needed, WordPress will handle CPT list
            );
        }
    }

    /**
     * Remove "Add New" submenu items for CPT Sync post types
     *
     * @return void
     */
    public function remove_add_new_submenu_items()
    {
        // Get all registered API CPTs
        $items = get_option('brf_external_api_loops', []);

        if (empty($items)) {
            return;
        }

        foreach ($items as $item) {
            // Handle both array and object formats
            $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);

            if (!$enableCptSync) {
                continue;
            }

            $cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
            if (empty($cpt_slug_value)) {
                continue;
            }

            $cpt_slug = $this->get_cpt_slug($cpt_slug_value);
            $parent_menu_slug = 'edit.php?post_type=' . $cpt_slug;
            $add_new_slug = 'post-new.php?post_type=' . $cpt_slug;

            // Remove "Add New" submenu item
            remove_submenu_page($parent_menu_slug, $add_new_slug);

            // Also remove for child CPTs
            $childCpts = $this->get_item_property($item, 'cptSettings.childCpts', []);
            if (!empty($childCpts) && is_array($childCpts)) {
                foreach ($childCpts as $child_config) {
                    $array_key = $this->get_item_property($child_config, 'arrayKey', '');
                    if (empty($array_key)) {
                        continue;
                    }

                    // Generate child CPT slug
                    $child_slug_base = $cpt_slug . '-' . sanitize_title($array_key);
                    if (strlen($child_slug_base) > 20) {
                        $child_slug_base = substr($child_slug_base, 0, 20);
                    }
                    $child_cpt_slug = $child_slug_base;

                    $child_parent_menu_slug = 'edit.php?post_type=' . $child_cpt_slug;
                    $child_add_new_slug = 'post-new.php?post_type=' . $child_cpt_slug;

                    // Remove "Add New" submenu item for child CPT
                    remove_submenu_page($child_parent_menu_slug, $child_add_new_slug);
                }
            }
        }
    }

    /**
     * Render the parent menu page (placeholder)
     *
     * @return void
     */
    public function render_parent_page()
    {
        echo '<div class="wrap">';
        echo '<h1>API Data</h1>';
        echo '<p>Select a submenu item to manage your API-synced content.</p>';
        echo '</div>';
    }

    /**
     * Get list of registered API CPTs (for debugging)
     *
     * @return array List of registered CPT slugs
     */
    public function get_registered_api_cpts()
    {
        $registered = [];
        $items = get_option('brf_external_api_loops', []);

        foreach ($items as $item) {
            $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);

            if ($enableCptSync) {
                $cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
                if (!empty($cpt_slug_value)) {
                    $cpt_slug = $this->get_cpt_slug($cpt_slug_value);
                    $label = $this->get_item_property($item, 'label', 'Unknown');
                    $registered[] = [
                        'slug' => $cpt_slug,
                        'label' => $label,
                        'exists' => post_type_exists($cpt_slug),
                    ];
                }
            }
        }

        return $registered;
    }

    /**
     * Main sync method - syncs API data to CPT
     *
     * @param string $item_id The API item ID
     * @param bool $force_fetch Whether to force a fresh API fetch (default: false)
     * @return array Sync statistics
     */
    public function sync_api_to_cpt($item_id, $force_fetch = false)
    {
        $item_id = sanitize_text_field($item_id);

        // Get the item
        // Clear cache before loading to ensure we get fresh data
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');

        $items = get_option('brf_external_api_loops', []);
        $item = null;

        foreach ($items as $i) {
            // Handle both array and object formats
            $current_id = is_array($i) ? (isset($i['id']) ? $i['id'] : null) : (isset($i->id) ? $i->id : null);

            if ($current_id === $item_id) {
                $item = $i;
                break;
            }
        }

        if (!$item) {
            return new \WP_Error('item_not_found', 'API item not found', ['status' => 404]);
        }

        // Handle both array and object formats
        $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);

        if (!$enableCptSync) {
            return new \WP_Error('cpt_sync_disabled', 'CPT Sync is not enabled for this item', ['status' => 400]);
        }

        // Validate required settings
        $uniqueIdField = is_array($item) ? (isset($item['cptSettings']['uniqueIdField']) ? $item['cptSettings']['uniqueIdField'] : '') : (isset($item->cptSettings->uniqueIdField) ? $item->cptSettings->uniqueIdField : '');

        if (empty($uniqueIdField)) {
            return new \WP_Error('missing_unique_field', 'Unique ID field is not configured. Please select a unique identifier field in the settings.', ['status' => 400]);
        }

        // Initialize stats
        $stats = [
            'added' => 0,
            'updated' => 0,
            'deleted' => 0,
            'errors' => []
        ];

        // Initialize progress
        $this->update_sync_progress($item_id, 'Starting sync...', 0, 0);

        try {
            // Check if pagination is enabled
            $pagination = $this->get_item_property($item, 'cptSettings.pagination', []);
            $pagination_enabled = !empty($pagination) && isset($pagination['enabled']) && $pagination['enabled'];

            // If force_fetch is true, always fetch fresh data from API
            if ($force_fetch) {
                $this->update_sync_progress($item_id, 'Fetching data from API...', 0, 0);

                // Clear cache before fetching
                \Bricksforge\ExternalApiLoops::clear_cache($item_id);

                // Check if pagination is enabled and fetch all pages
                if ($pagination_enabled) {
                    $api_data = $this->fetch_all_paginated_pages($item_id, $item);

                    if (is_wp_error($api_data)) {
                        $stats['errors'][] = $api_data->get_error_message();
                        return $stats;
                    }

                    // If pagination returned null, fall back to regular fetch
                    if ($api_data === null) {
                        $external_api_loops = $this->get_external_api_loops();
                        $external_api_loops->get_items();
                        $external_api_loops->collect_active_items();
                        $api_data = $external_api_loops->run_api_request($item_id, null, true);
                    }
                } else {
                    // Get API data (lazy load ExternalApiLoops)
                    $external_api_loops = $this->get_external_api_loops();
                    $external_api_loops->get_items();
                    $external_api_loops->collect_active_items();
                    $api_data = $external_api_loops->run_api_request($item_id, null, true);
                }

                if (is_wp_error($api_data)) {
                    $stats['errors'][] = $api_data->get_error_message();
                    return $stats;
                }

                // Update apiResponse in the item so the UI shows the latest data
                if (!empty($api_data)) {
                    $this->update_api_response($item_id, $api_data);
                }
            } else {
                // If pagination is enabled, we need to fetch all pages even if force_fetch is false
                // because cached data only contains the first page
                if ($pagination_enabled) {
                    $api_data = $this->fetch_all_paginated_pages($item_id, $item);

                    if (is_wp_error($api_data)) {
                        $stats['errors'][] = $api_data->get_error_message();
                        return $stats;
                    }

                    // If pagination returned null, fall back to regular fetch
                    if ($api_data === null) {
                        // Get API data (lazy load ExternalApiLoops)
                        $external_api_loops = $this->get_external_api_loops();
                        $external_api_loops->get_items();
                        $external_api_loops->collect_active_items();
                        $api_data = $external_api_loops->run_api_request($item_id, null, true);
                    }
                } else {
                    // First, try to use already fetched data from the item
                    $api_data = null;
                    if (is_array($item)) {
                        $api_data = isset($item['apiResponse']) ? $item['apiResponse'] : null;
                    } else {
                        $api_data = isset($item->apiResponse) ? $item->apiResponse : null;
                    }

                    // If no cached data, fetch from API
                    if (empty($api_data)) {
                        // Get API data (lazy load ExternalApiLoops)
                        $external_api_loops = $this->get_external_api_loops();
                        $external_api_loops->get_items();
                        $external_api_loops->collect_active_items();
                        $api_data = $external_api_loops->run_api_request($item_id, null, true);

                        if (is_wp_error($api_data)) {
                            $stats['errors'][] = $api_data->get_error_message();
                            return $stats;
                        }
                    }
                }
            }

            if (empty($api_data)) {
                $stats['errors'][] = 'No data received from API';
                return $stats;
            }

            // Get unique ID field
            $unique_id_field = is_array($item) ? (isset($item['cptSettings']['uniqueIdField']) ? $item['cptSettings']['uniqueIdField'] : '') : (isset($item->cptSettings->uniqueIdField) ? $item->cptSettings->uniqueIdField : '');
            $cpt_slug_value = is_array($item) ? (isset($item['cptSettings']['cptSlug']) ? $item['cptSettings']['cptSlug'] : '') : (isset($item->cptSettings->cptSlug) ? $item->cptSettings->cptSlug : '');
            $cpt_slug = $this->get_cpt_slug($cpt_slug_value);

            // Get all existing posts for this CPT
            $existing_posts = get_posts([
                'post_type' => $cpt_slug,
                'posts_per_page' => -1,
                'post_status' => 'any',
                'meta_key' => '_brf_api_source_id',
                'meta_value' => $item_id
            ]);

            $existing_unique_ids = [];
            $existing_posts_by_unique_id = [];

            foreach ($existing_posts as $post) {
                $unique_id = get_post_meta($post->ID, '_brf_api_unique_id', true);
                if ($unique_id) {
                    $existing_unique_ids[] = $unique_id;
                    $existing_posts_by_unique_id[$unique_id] = $post->ID;
                }
            }

            // Current unique IDs from API
            $current_unique_ids = [];
            $total_items = count($api_data);
            $processed_items = 0;

            // Update progress: Processing items
            $this->update_sync_progress($item_id, sprintf('Processing items (%d total)...', $total_items), 0, $total_items);

            // Process each API item
            foreach ($api_data as $data_item) {
                $processed_items++;
                if ($processed_items % 10 === 0 || $processed_items === $total_items) {
                    // Update progress every 10 items or on last item
                    $this->update_sync_progress($item_id, sprintf('Processing item %d of %d...', $processed_items, $total_items), $processed_items, $total_items);
                }
                $result = $this->create_or_update_post($item, $data_item, $unique_id_field, $existing_posts_by_unique_id);

                if (is_wp_error($result)) {
                    $stats['errors'][] = $result->get_error_message();
                    continue;
                }

                if ($result['action'] === 'created') {
                    $stats['added']++;
                } elseif ($result['action'] === 'updated') {
                    $stats['updated']++;
                }

                $current_unique_ids[] = $result['unique_id'];
            }

            // Delete posts that are no longer in API
            $deleted_count = $this->delete_missing_posts($item, $existing_posts_by_unique_id, $current_unique_ids);
            $stats['deleted'] = $deleted_count;

            // Update last sync time and stats in item settings
            $this->update_sync_stats($item_id, $stats);

            // Clear progress on success
            $this->clear_sync_progress($item_id);
        } catch (\Exception $e) {
            $stats['errors'][] = $e->getMessage();
            // Clear progress on error
            $this->clear_sync_progress($item_id);
        }

        return $stats;
    }

    /**
     * Create or update a post from API data
     *
     * @param object $item The API item configuration
     * @param mixed $api_data The data from API
     * @param string $unique_id_field The field to use as unique identifier
     * @param array $existing_posts_by_unique_id Array of existing posts indexed by unique ID
     * @return array|WP_Error Result with action and unique_id
     */
    private function create_or_update_post($item, $api_data, $unique_id_field, $existing_posts_by_unique_id)
    {
        // Extract unique ID from API data
        $unique_id_value = $this->get_value_from_data($api_data, $unique_id_field);

        if (empty($unique_id_value)) {
            return new \WP_Error('missing_unique_id', "Unique ID field '{$unique_id_field}' not found in API data");
        }

        // Map API data to post structure
        $post_data = $this->map_api_data_to_post($item, $api_data);

        $cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
        $cpt_slug = $this->get_cpt_slug($cpt_slug_value);
        $item_id = $this->get_item_property($item, 'id', '');

        // Check if post exists
        $post_id = isset($existing_posts_by_unique_id[$unique_id_value]) ? $existing_posts_by_unique_id[$unique_id_value] : null;

        if ($post_id) {
            // Update existing post
            $post_data['ID'] = $post_id;
            wp_update_post($post_data);
            $action = 'updated';
        } else {
            // Create new post
            $post_data['post_type'] = $cpt_slug;
            $post_data['post_status'] = 'publish';
            $post_id = wp_insert_post($post_data);

            if (is_wp_error($post_id)) {
                return $post_id;
            }

            $action = 'created';
        }

        // Store meta data
        update_post_meta($post_id, '_brf_api_source_id', $item_id);
        update_post_meta($post_id, '_brf_api_unique_id', $unique_id_value);
        update_post_meta($post_id, '_brf_api_raw_data', json_encode($api_data));
        update_post_meta($post_id, '_brf_api_last_synced', current_time('mysql'));

        // Store all API fields as custom fields
        $this->store_api_fields_as_meta($post_id, $api_data, $item);

        // 1. Create/Update Child CPTs from configured nested arrays
        $childCpts = $this->get_item_property($item, 'cptSettings.childCpts', []);
        if (!empty($childCpts) && is_array($childCpts)) {
            foreach ($childCpts as $child_config) {
                $this->sync_nested_array_as_child_posts($post_id, $api_data, $child_config, $item);
            }
        }

        // 2. Store selected nested arrays as JSON in meta (for dynamic data tags)
        $selectedNestedArrays = $this->get_item_property($item, 'selectedNestedArrays', []);
        if (!empty($selectedNestedArrays) && is_array($selectedNestedArrays)) {
            foreach ($selectedNestedArrays as $array_key) {
                // selectedNestedArrays is now a simple string array
                if (is_string($array_key)) {
                    $nested_data = $this->extract_nested_array($api_data, $array_key);
                    if (!empty($nested_data)) {
                        $meta_key = '_brf_api_nested_' . sanitize_key($array_key);
                        update_post_meta($post_id, $meta_key, json_encode($nested_data));
                    }
                }
            }
        }

        return [
            'action' => $action,
            'unique_id' => $unique_id_value,
            'post_id' => $post_id
        ];
    }

    /**
     * Map API data to WordPress post structure
     *
     * @param object $item The API item configuration
     * @param mixed $api_data The data from API
     * @return array Post data array
     */
    private function map_api_data_to_post($item, $api_data)
    {
        $post_data = [];

        // Map title
        $post_title_field = $this->get_item_property($item, 'cptSettings.fieldMapping.post_title', '');
        if (!empty($post_title_field)) {
            $title_value = $this->get_value_from_data($api_data, $post_title_field);
            $post_data['post_title'] = $title_value ? sanitize_text_field($title_value) : 'Untitled';
        } else {
            $post_data['post_title'] = 'Untitled';
        }

        // Map content
        $post_content_field = $this->get_item_property($item, 'cptSettings.fieldMapping.post_content', '');
        if (!empty($post_content_field)) {
            $content_value = $this->get_value_from_data($api_data, $post_content_field);
            $post_data['post_content'] = $content_value ? wp_kses_post($content_value) : '';
        } else {
            $post_data['post_content'] = '';
        }

        return $post_data;
    }

    /**
     * Store all API fields as post meta
     *
     * @param int $post_id The post ID
     * @param mixed $api_data The API data
     * @param object $item The API item configuration
     * @return void
     */
    private function store_api_fields_as_meta($post_id, $api_data, $item)
    {
        if (!is_array($api_data) && !is_object($api_data)) {
            return;
        }

        $flattened = $this->flatten_array($api_data);

        $post_title_field = $this->get_item_property($item, 'cptSettings.fieldMapping.post_title', '');
        $post_content_field = $this->get_item_property($item, 'cptSettings.fieldMapping.post_content', '');

        foreach ($flattened as $key => $value) {
            // Store ALL fields as custom fields, including title and content
            // This ensures they are available as Custom Fields even though they're also mapped to post_title/post_content
            $meta_key = 'brf_field_' . sanitize_key($key);
            update_post_meta($post_id, $meta_key, $value);
        }
    }

    /**
     * Store nested arrays as JSON in post meta
     *
     * @param int $post_id The post ID
     * @param mixed $api_data The API data
     * @param array $selected_nested_arrays Array of selected nested array keys
     * @param object $item The API item configuration
     * @return void
     */
    private function store_nested_arrays_as_meta($post_id, $api_data, $selected_nested_arrays, $item)
    {
        foreach ($selected_nested_arrays as $array_key) {
            $nested_data = $this->extract_nested_array($api_data, $array_key);

            if (!empty($nested_data)) {
                $meta_key = '_brf_api_nested_' . sanitize_key($array_key);
                update_post_meta($post_id, $meta_key, json_encode($nested_data));
            }
        }
    }

    /**
     * Sync nested array as child posts
     *
     * @param int $parent_post_id The parent post ID
     * @param mixed $api_data The API data for the parent item
     * @param mixed $array_config The configuration for this nested array
     * @param mixed $item The API item configuration
     * @return array Stats (added, updated, deleted)
     */
    private function sync_nested_array_as_child_posts($parent_post_id, $api_data, $array_config, $item)
    {
        $stats = [
            'added' => 0,
            'updated' => 0,
            'deleted' => 0
        ];

        // Get array configuration (new structure: arrayKey, label, uniqueIdField, type)
        $array_key = $this->get_item_property($array_config, 'arrayKey', '');
        $childUniqueIdField = $this->get_item_property($array_config, 'uniqueIdField', '');
        $type = $this->get_item_property($array_config, 'type', 'cpt'); // Default to 'cpt' for backwards compatibility

        if (empty($array_key)) {
            return $stats;
        }

        // If type is taxonomy, sync as taxonomy terms instead
        if ($type === 'taxonomy') {
            return $this->sync_primitive_array_as_taxonomy($parent_post_id, $api_data, $array_config, $item);
        }

        // Extract the nested array from API data
        $nested_array_data = $this->extract_nested_array($api_data, $array_key);

        // Auto-detect primitive arrays and set __value as default if no uniqueIdField is set
        if (empty($childUniqueIdField) && !empty($nested_array_data) && is_array($nested_array_data) && count($nested_array_data) > 0) {
            $first_item = $nested_array_data[0];
            // If first item is a primitive value, auto-set to __value
            if (is_scalar($first_item) && !is_array($first_item)) {
                $childUniqueIdField = '__value';
            }
        }

        if (empty($childUniqueIdField)) {
            return $stats;
        }

        if (empty($nested_array_data) || !is_array($nested_array_data)) {
            return $stats;
        }

        // Get parent CPT slug and generate child CPT slug
        $parent_cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
        $parent_cpt_slug = $this->get_cpt_slug($parent_cpt_slug_value);

        $child_slug_base = $parent_cpt_slug . '-' . sanitize_title($array_key);
        if (strlen($child_slug_base) > 20) {
            $child_slug_base = substr($child_slug_base, 0, 20);
        }
        $child_cpt_slug = $child_slug_base;

        // Get existing child posts for this parent
        $existing_child_posts = get_posts([
            'post_type' => $child_cpt_slug,
            'posts_per_page' => -1,
            'post_status' => 'any',
            'meta_query' => [
                [
                    'key' => '_brf_parent_post_id',
                    'value' => $parent_post_id,
                    'compare' => '='
                ]
            ]
        ]);

        $existing_unique_ids = [];
        $existing_posts_by_unique_id = [];

        foreach ($existing_child_posts as $child_post) {
            $unique_id = get_post_meta($child_post->ID, '_brf_api_unique_id', true);
            if ($unique_id) {
                $existing_unique_ids[] = $unique_id;
                $existing_posts_by_unique_id[$unique_id] = $child_post->ID;
            }
        }

        // Current unique IDs from API
        $current_unique_ids = [];

        // Process each array item
        foreach ($nested_array_data as $index => $array_item) {
            // Handle primitive values (strings, numbers) - arrays like ["Africa", "Asia"]
            if (!is_array($array_item) && !is_object($array_item)) {
                // For primitive arrays, use special handling
                if ($childUniqueIdField === '__index') {
                    $unique_id_value = (string) $index;
                } elseif ($childUniqueIdField === '__value') {
                    $unique_id_value = (string) $array_item;
                } else {
                    // Try to extract from the value itself (for edge cases)
                    $unique_id_value = (string) $array_item;
                }

                if (empty($unique_id_value)) {
                    continue;
                }

                $current_unique_ids[] = $unique_id_value;

                // Check if child post exists
                $child_post_id = isset($existing_posts_by_unique_id[$unique_id_value]) ? $existing_posts_by_unique_id[$unique_id_value] : null;

                // Prepare child post data
                $child_post_data = [
                    'post_title' => sanitize_text_field($unique_id_value),
                    'post_content' => '', // Can be customized if needed
                    'post_type' => $child_cpt_slug,
                    'post_status' => 'publish'
                ];

                if ($child_post_id) {
                    // Update existing child post
                    $child_post_data['ID'] = $child_post_id;
                    wp_update_post($child_post_data);
                    $stats['updated']++;
                } else {
                    // Create new child post
                    $child_post_id = wp_insert_post($child_post_data);
                    if (is_wp_error($child_post_id)) {
                        continue;
                    }
                    $stats['added']++;
                }

                // Store parent reference and unique ID
                update_post_meta($child_post_id, '_brf_parent_post_id', $parent_post_id);
                update_post_meta($child_post_id, '_brf_parent_post_type', $parent_cpt_slug);
                update_post_meta($child_post_id, '_brf_array_key', $array_key);
                update_post_meta($child_post_id, '_brf_api_unique_id', $unique_id_value);

                // Store as visible custom fields for reference
                update_post_meta($child_post_id, 'brf_field_parent_id', $parent_post_id);
                update_post_meta($child_post_id, 'brf_field_parent_post_type', $parent_cpt_slug);

                // Store the primitive value as a custom field
                update_post_meta($child_post_id, 'brf_field_value', $array_item);

                continue;
            }

            // Extract unique ID from array item (for objects/arrays)
            if ($childUniqueIdField === '__index') {
                $unique_id_value = (string) $index;
            } elseif ($childUniqueIdField === '__value') {
                // For objects, try to convert to string representation
                $unique_id_value = is_scalar($array_item) ? (string) $array_item : md5(serialize($array_item));
            } else {
                $unique_id_value = $this->get_value_from_data($array_item, $childUniqueIdField);
            }

            if (empty($unique_id_value)) {
                continue;
            }

            $current_unique_ids[] = $unique_id_value;

            // Check if child post exists
            $child_post_id = isset($existing_posts_by_unique_id[$unique_id_value]) ? $existing_posts_by_unique_id[$unique_id_value] : null;

            // Prepare child post data
            $child_post_data = [
                'post_title' => sanitize_text_field($unique_id_value),
                'post_content' => '', // Can be customized if needed
                'post_type' => $child_cpt_slug,
                'post_status' => 'publish'
            ];

            if ($child_post_id) {
                // Update existing child post
                $child_post_data['ID'] = $child_post_id;
                wp_update_post($child_post_data);
                $stats['updated']++;
            } else {
                // Create new child post
                $child_post_id = wp_insert_post($child_post_data);

                if (is_wp_error($child_post_id)) {
                    continue;
                }

                $stats['added']++;
            }

            // Store child post meta (system fields with _ prefix - hidden)
            update_post_meta($child_post_id, '_brf_parent_post_id', $parent_post_id);
            update_post_meta($child_post_id, '_brf_parent_post_type', $parent_cpt_slug);
            update_post_meta($child_post_id, '_brf_array_key', $array_key);
            update_post_meta($child_post_id, '_brf_api_unique_id', $unique_id_value);
            update_post_meta($child_post_id, '_brf_api_last_synced', current_time('mysql'));

            // Store parent reference as visible custom field (for Bricks Builder Dynamic Data Tags)
            update_post_meta($child_post_id, 'brf_field_parent_id', $parent_post_id);
            update_post_meta($child_post_id, 'brf_field_parent_post_type', $parent_cpt_slug);

            // Store all fields from array item as custom fields
            $flattened = $this->flatten_array($array_item);
            foreach ($flattened as $field_key => $field_value) {
                $meta_key = 'brf_field_' . sanitize_key($field_key);
                update_post_meta($child_post_id, $meta_key, $field_value);
            }
        }

        // Delete child posts that are no longer in the API data
        $deleted_count = $this->delete_missing_child_posts($existing_posts_by_unique_id, $current_unique_ids);
        $stats['deleted'] = $deleted_count;

        return $stats;
    }

    /**
     * Sync primitive array as taxonomy terms
     *
     * @param int $parent_post_id The parent post ID
     * @param mixed $api_data The API data for the parent item
     * @param array $array_config The configuration for this nested array
     * @param mixed $item The API item configuration
     * @return array Stats (added, updated, deleted)
     */
    private function sync_primitive_array_as_taxonomy($parent_post_id, $api_data, $array_config, $item)
    {
        $stats = [
            'added' => 0,
            'updated' => 0,
            'deleted' => 0
        ];

        $array_key = $this->get_item_property($array_config, 'arrayKey', '');

        if (empty($array_key)) {
            return $stats;
        }

        // Extract the nested array from API data
        $nested_array_data = $this->extract_nested_array($api_data, $array_key);

        if (empty($nested_array_data) || !is_array($nested_array_data)) {
            // If no data, clear all terms for this taxonomy
            $parent_cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
            $parent_cpt_slug = $this->get_cpt_slug($parent_cpt_slug_value);
            $tax_slug = $parent_cpt_slug . '-' . sanitize_title($array_key);
            if (strlen($tax_slug) > 32) {
                $tax_slug = substr($tax_slug, 0, 32);
            }

            wp_set_object_terms($parent_post_id, [], $tax_slug, false);
            return $stats;
        }

        // Generate taxonomy slug
        $parent_cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
        $parent_cpt_slug = $this->get_cpt_slug($parent_cpt_slug_value);
        $tax_slug = $parent_cpt_slug . '-' . sanitize_title($array_key);
        if (strlen($tax_slug) > 32) {
            $tax_slug = substr($tax_slug, 0, 32);
        }

        // Get existing terms for this post
        $existing_terms = wp_get_object_terms($parent_post_id, $tax_slug, ['fields' => 'names']);
        if (is_wp_error($existing_terms)) {
            $existing_terms = [];
        }

        // Convert primitive values to term IDs
        $term_ids = [];
        $current_term_names = [];

        // Ensure we have an array and it's not empty
        if (!is_array($nested_array_data)) {
            $nested_array_data = [];
        }

        // Reset array keys to ensure we iterate over values, not keys
        // This is important for associative arrays
        $nested_array_data = array_values($nested_array_data);

        foreach ($nested_array_data as $index => $value) {
            // Skip non-scalar values (objects, arrays, etc.)
            if (!is_scalar($value)) {
                continue;
            }

            // Convert value to string and trim whitespace
            // This ensures numbers are converted to strings properly
            $term_name = trim((string) $value);

            // Skip empty values
            if (empty($term_name) && $term_name !== '0') {
                continue;
            }

            $current_term_names[] = $term_name;

            // Check if term already exists by name (not by ID!)
            // term_exists() can return array with term_id and term_taxonomy_id, or just term_id, or false
            $term_check = term_exists($term_name, $tax_slug);

            // Also check by slug in case name doesn't match exactly
            $term_slug = sanitize_title($term_name);
            $term_by_slug = get_term_by('slug', $term_slug, $tax_slug);

            // Use term_by_slug if found, otherwise use term_check
            $term = null;
            $term_id = null;

            if ($term_by_slug && !is_wp_error($term_by_slug)) {
                $term = $term_by_slug;
                $term_id = $term->term_id;
            } elseif ($term_check) {
                if (is_array($term_check) && isset($term_check['term_id'])) {
                    $term_id = $term_check['term_id'];
                } elseif (is_numeric($term_check)) {
                    $term_id = $term_check;
                }
            }

            if (!$term_id) {
                // Create new term with the name as both name and slug
                $result = wp_insert_term($term_name, $tax_slug, [
                    'slug' => $term_slug
                ]);

                if (is_wp_error($result)) {
                    // If error is duplicate term, try to get it by name
                    if ($result->get_error_code() === 'term_exists') {
                        $term = get_term_by('name', $term_name, $tax_slug);
                        if ($term && !is_wp_error($term)) {
                            $term_id = $term->term_id;
                        }
                    }
                    if (!$term_id) {
                        continue;
                    }
                } else {
                    if (isset($result['term_id'])) {
                        $term_id = $result['term_id'];
                        $stats['added']++;
                    } else {
                        continue;
                    }
                }
            }

            // Add term ID to list
            if ($term_id) {
                $term_ids[] = $term_id;
            }
        }

        // Set terms for this post (replaces existing terms)
        $result = wp_set_object_terms($parent_post_id, $term_ids, $tax_slug, false);

        // Count deleted terms (terms that were on the post but are no longer in the API data)
        $deleted_terms = array_diff($existing_terms, $current_term_names);
        $stats['deleted'] = count($deleted_terms);

        return $stats;
    }

    /**
     * Delete child posts that are no longer in the API data
     *
     * @param array $existing_posts_by_unique_id Existing posts indexed by unique ID
     * @param array $current_unique_ids Current unique IDs from API
     * @return int Number of deleted posts
     */
    private function delete_missing_child_posts($existing_posts_by_unique_id, $current_unique_ids)
    {
        $deleted_count = 0;

        foreach ($existing_posts_by_unique_id as $unique_id => $post_id) {
            if (!in_array($unique_id, $current_unique_ids)) {
                wp_delete_post($post_id, true); // Force delete (skip trash)
                $deleted_count++;
            }
        }

        return $deleted_count;
    }

    /**
     * Delete all child posts when parent post is deleted (cascading delete)
     *
     * @param int $post_id The post ID being deleted
     * @param WP_Post $post The post object
     * @return void
     */
    public function delete_child_posts_on_parent_delete($post_id, $post)
    {
        // Only handle our API CPTs
        if (strpos($post->post_type, 'bfa-') !== 0) {
            return;
        }

        // Find all child posts that reference this parent
        $child_posts = get_posts([
            'post_type' => 'any',
            'posts_per_page' => -1,
            'post_status' => 'any',
            'meta_query' => [
                [
                    'key' => '_brf_parent_post_id',
                    'value' => $post_id,
                    'compare' => '='
                ]
            ]
        ]);

        if (!empty($child_posts)) {
            foreach ($child_posts as $child_post) {
                wp_delete_post($child_post->ID, true); // Force delete (skip trash)
            }
        }
    }

    /**
     * Extract nested array from data
     *
     * @param mixed $data The data to search
     * @param string $key The array key to find
     * @return array The nested array data
     */
    private function extract_nested_array($data, $key)
    {
        if (!is_array($data) && !is_object($data)) {
            return [];
        }

        // Convert object to array for easier handling
        if (is_object($data)) {
            $data = (array) $data;
        }

        // Direct match
        if (isset($data[$key])) {
            $result = $data[$key];
            // Ensure we return an array
            if (is_array($result)) {
                return $result;
            }
            // If it's a single value, wrap it in an array
            return [$result];
        }

        // Recursive search
        foreach ($data as $item_key => $value) {
            if ($item_key === $key) {
                if (is_array($value)) {
                    return $value;
                }
                // If it's a single value, wrap it in an array
                if (is_scalar($value)) {
                    return [$value];
                }
            }

            if (is_array($value) || is_object($value)) {
                $result = $this->extract_nested_array($value, $key);
                if (!empty($result) && is_array($result)) {
                    return $result;
                }
            }
        }

        return [];
    }

    /**
     * Flatten a multidimensional array
     *
     * @param mixed $array The array to flatten
     * @param string $prefix Key prefix
     * @return array Flattened array
     */
    private function flatten_array($array, $prefix = '')
    {
        $result = [];

        foreach ($array as $key => $value) {
            // Normalize key: lowercase, replace non-alphanumeric with underscore
            $normalized_key = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $key));
            $new_key = $prefix === '' ? $normalized_key : $prefix . '_' . $normalized_key;

            if (is_array($value) || is_object($value)) {
                // Check if it's an indexed array (list)
                $is_indexed = is_array($value) && array_keys($value) === range(0, count($value) - 1);

                if ($is_indexed) {
                    // Store indexed arrays as JSON
                    $result[$new_key] = json_encode($value);
                } else {
                    // Recursively flatten associative arrays/objects
                    $result = array_merge($result, $this->flatten_array($value, $new_key));
                }
            } else {
                $result[$new_key] = $value;
            }
        }

        return $result;
    }

    /**
     * Get value from data by key
     *
     * @param mixed $data The data to search
     * @param string $key The key to find (normalized: lowercase with underscores)
     * @return mixed The value
     */
    private function get_value_from_data($data, $key)
    {
        // Normalize the search key
        $normalized_key = strtolower(str_replace(['-', ' '], '_', $key));

        // Direct match (exact key)
        if (is_array($data) && isset($data[$key])) {
            return $data[$key];
        }

        if (is_object($data) && isset($data->$key)) {
            return $data->$key;
        }

        // Try to find by normalized key (case-insensitive)
        if (is_array($data)) {
            foreach ($data as $k => $v) {
                $normalized_k = strtolower(str_replace(['-', ' '], '_', $k));
                if ($normalized_k === $normalized_key) {
                    return $v;
                }
            }
        }

        if (is_object($data)) {
            foreach (get_object_vars($data) as $k => $v) {
                $normalized_k = strtolower(str_replace(['-', ' '], '_', $k));
                if ($normalized_k === $normalized_key) {
                    return $v;
                }
            }
        }

        // Handle nested keys with underscore notation (e.g., "attributes_weight_kg")
        // The underscore is ambiguous - it could be a separator between nested keys
        // OR it could be part of the original key (e.g., "Item Name" becomes "item_name")
        // We need to try different split combinations
        if (strpos($normalized_key, '_') !== false) {
            $result = $this->find_nested_value_with_ambiguous_separator($data, $normalized_key);
            if ($result !== null) {
                return $result;
            }
        }

        return null;
    }

    /**
     * Find a nested value when the underscore separator is ambiguous
     * Tries all possible split combinations to find the correct path
     *
     * @param mixed $data The data to search
     * @param string $key The normalized key (lowercase with underscores)
     * @return mixed The value or null if not found
     */
    private function find_nested_value_with_ambiguous_separator($data, $key)
    {
        $parts = explode('_', $key);
        $num_parts = count($parts);

        // Try all possible ways to combine the parts
        // For "fields_item_name", we try:
        // 1. fields -> item -> name (original behavior)
        // 2. fields -> item_name (combining last two parts)
        // 3. fields_item -> name (combining first two parts)
        // 4. fields_item_name (all as one key)
        $combinations = $this->generate_key_combinations($parts);

        foreach ($combinations as $path) {
            $result = $this->traverse_path($data, $path);
            if ($result !== null) {
                return $result;
            }
        }

        return null;
    }

    /**
     * Generate all possible combinations of key parts
     *
     * @param array $parts The split parts of the key
     * @return array Array of possible paths, each path is an array of keys
     */
    private function generate_key_combinations($parts)
    {
        $combinations = [];
        $num_parts = count($parts);

        // Generate combinations using binary representation
        // For n parts, there are 2^(n-1) ways to combine them
        $num_combinations = pow(2, $num_parts - 1);

        for ($i = 0; $i < $num_combinations; $i++) {
            $path = [];
            $current_segment = $parts[0];

            for ($j = 0; $j < $num_parts - 1; $j++) {
                // Check if we should split at position j
                if ($i & (1 << $j)) {
                    // Split here - add current segment to path and start new segment
                    $path[] = $current_segment;
                    $current_segment = $parts[$j + 1];
                } else {
                    // Don't split - combine with next part
                    $current_segment .= '_' . $parts[$j + 1];
                }
            }
            $path[] = $current_segment;
            $combinations[] = $path;
        }

        // Sort by number of segments (prefer fewer segments = more combined keys)
        usort($combinations, function ($a, $b) {
            return count($a) - count($b);
        });

        return $combinations;
    }

    /**
     * Traverse a path in the data structure
     *
     * @param mixed $data The data to search
     * @param array $path Array of keys to traverse
     * @return mixed The value or null if path not found
     */
    private function traverse_path($data, $path)
    {
        $current = $data;

        foreach ($path as $part) {
            $found = false;

            if (is_array($current)) {
                // Try direct match first
                if (isset($current[$part])) {
                    $current = $current[$part];
                    $found = true;
                } else {
                    // Try case-insensitive match with normalized key comparison
                    foreach ($current as $k => $v) {
                        $normalized_k = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $k));
                        if ($normalized_k === $part || strtolower($k) === $part) {
                            $current = $v;
                            $found = true;
                            break;
                        }
                    }
                }
            } elseif (is_object($current)) {
                // Try direct match first
                if (isset($current->$part)) {
                    $current = $current->$part;
                    $found = true;
                } else {
                    // Try case-insensitive match with normalized key comparison
                    foreach (get_object_vars($current) as $k => $v) {
                        $normalized_k = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $k));
                        if ($normalized_k === $part || strtolower($k) === $part) {
                            $current = $v;
                            $found = true;
                            break;
                        }
                    }
                }
            }

            if (!$found) {
                return null;
            }
        }

        return $current;
    }

    /**
     * Delete posts that are no longer in API data
     *
     * @param object $item The API item configuration
     * @param array $existing_posts_by_unique_id Existing posts indexed by unique ID
     * @param array $current_unique_ids Current unique IDs from API
     * @return int Number of deleted posts
     */
    private function delete_missing_posts($item, $existing_posts_by_unique_id, $current_unique_ids)
    {
        $deleted_count = 0;

        foreach ($existing_posts_by_unique_id as $unique_id => $post_id) {
            if (!in_array($unique_id, $current_unique_ids)) {
                // Delete permanently
                $result = wp_delete_post($post_id, true);

                if ($result) {
                    $deleted_count++;
                }
            }
        }

        return $deleted_count;
    }

    /**
     * Update sync stats in item settings
     *
     * @param string $item_id The API item ID
     * @param array $stats Sync statistics
     * @return void
     */
    private function update_sync_stats($item_id, $stats)
    {
        global $wpdb;

        // CRITICAL: Read directly from DB to avoid cache issues
        // This prevents overwriting data that was just saved
        // Flush ALL caches first to ensure we get the absolute latest data
        wp_cache_flush();
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');

        // Read directly from DB - wait a tiny bit to ensure any pending writes are committed
        // This is a workaround for race conditions where save_option and update_sync_stats run simultaneously
        usleep(100000); // 100ms delay to let any pending DB writes complete

        $db_value = $wpdb->get_var($wpdb->prepare(
            "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
            'brf_external_api_loops'
        ));

        if ($db_value === null) {
            return;
        }

        $items = maybe_unserialize($db_value);

        // Convert stdClass to array if needed
        if (is_object($items)) {
            $items = json_decode(json_encode($items), true);
        }

        if (!is_array($items)) {
            return;
        }

        $updated = false;
        foreach ($items as &$item) {
            $current_id = is_array($item) ? (isset($item['id']) ? $item['id'] : null) : (isset($item->id) ? $item->id : null);

            if ($current_id === $item_id) {
                // Ensure item is array format for consistent handling
                if (!is_array($item)) {
                    $item = json_decode(json_encode($item), true);
                }

                if (!isset($item['cptSettings'])) {
                    $item['cptSettings'] = [];
                }
                $item['cptSettings']['lastSync'] = current_time('mysql');
                $item['cptSettings']['syncStats'] = $stats;
                $updated = true;
                break;
            }
        }

        if (!$updated) {
            return;
        }

        // CRITICAL: Use update_option instead of direct DB write
        // This ensures WordPress cache is properly updated
        wp_cache_flush();
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');

        // Use update_option to ensure proper cache handling
        $result = update_option('brf_external_api_loops', $items);

        if (!$result) {
            // Fallback to direct DB update if update_option fails
            $serialized_value = maybe_serialize($items);
            $wpdb->query($wpdb->prepare(
                "UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s",
                $serialized_value,
                'brf_external_api_loops'
            ));
        }

        // Clear cache after saving
        wp_cache_flush();
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');
    }

    /**
     * Update apiResponse for an item
     *
     * @param string $item_id The API item ID
     * @param mixed $api_data The API response data
     * @return void
     */
    private function update_api_response($item_id, $api_data)
    {
        global $wpdb;

        // CRITICAL: Read directly from DB to avoid cache issues
        // This prevents overwriting data that was just saved
        // Flush ALL caches first to ensure we get the absolute latest data
        wp_cache_flush();
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');

        // Read directly from DB - wait a tiny bit to ensure any pending writes are committed
        // This is a workaround for race conditions where save_option and update_api_response run simultaneously
        usleep(100000); // 100ms delay to let any pending DB writes complete

        $db_value = $wpdb->get_var($wpdb->prepare(
            "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
            'brf_external_api_loops'
        ));

        if ($db_value === null) {
            return;
        }

        $items = maybe_unserialize($db_value);

        // Convert stdClass to array if needed
        if (is_object($items)) {
            $items = json_decode(json_encode($items), true);
        }

        if (!is_array($items)) {
            return;
        }

        $updated = false;
        foreach ($items as &$item) {
            $current_id = is_array($item) ? (isset($item['id']) ? $item['id'] : null) : (isset($item->id) ? $item->id : null);

            if ($current_id === $item_id) {
                // Ensure item is array format for consistent handling
                if (!is_array($item)) {
                    $item = json_decode(json_encode($item), true);
                }

                $item['apiResponse'] = $api_data;
                $item['apiStatus'] = 'success';
                $updated = true;
                break;
            }
        }

        if (!$updated) {
            return;
        }

        // CRITICAL: Use update_option instead of direct DB write
        // This ensures WordPress cache is properly updated
        wp_cache_flush();
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');

        // Use update_option to ensure proper cache handling
        $result = update_option('brf_external_api_loops', $items);

        if (!$result) {
            // Fallback to direct DB update if update_option fails
            $serialized_value = maybe_serialize($items);
            $wpdb->query($wpdb->prepare(
                "UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s",
                $serialized_value,
                'brf_external_api_loops'
            ));
        }

        // Clear cache after saving
        wp_cache_flush();
        wp_cache_delete('brf_external_api_loops', 'options');
        wp_cache_delete('alloptions', 'options');
    }

    /**
     * Schedule cron job for an item
     *
     * @param object $item The API item
     * @return void
     */
    public function schedule_cron_job($item)
    {
        // Handle both array and object formats
        $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);
        $item_id = is_array($item) ? (isset($item['id']) ? $item['id'] : null) : (isset($item->id) ? $item->id : null);

        if (!$enableCptSync || !$item_id) {
            return;
        }

        // Use transient lock to prevent concurrent scheduling attempts
        $lock_key = 'brf_cpt_sync_scheduling_' . $item_id;
        if (get_transient($lock_key)) {
            // Another process is already scheduling this cron job
            return;
        }

        // Set lock for 5 seconds
        set_transient($lock_key, true, 5);

        try {
            $hook_name = 'bricksforge_cpt_sync';

            // Get cron expression
            if (is_array($item)) {
                $cron_expression = isset($item['cptSettings']['cronExpression']) ? $item['cptSettings']['cronExpression'] : '0 0 * * *';
            } else {
                $cron_expression = isset($item->cptSettings->cronExpression) ? $item->cptSettings->cronExpression : '0 0 * * *';
            }

            // If manual sync only, unschedule any existing cron jobs and return
            if ($cron_expression === 'manual' || $cron_expression === '' || $cron_expression === null) {
                wp_clear_scheduled_hook($hook_name, [$item_id]);
                delete_transient($lock_key);
                return;
            }

            // Parse cron expression to get interval in seconds
            $interval = $this->parse_cron_expression_to_interval($cron_expression);

            // Determine schedule name based on interval
            $schedule = $this->get_schedule_name_from_interval($interval, $cron_expression);

            // Ensure custom schedule exists if needed (must be done before wp_get_schedules)
            if (strpos($schedule, 'brf_custom_') === 0) {
                $this->ensure_custom_schedule_exists($interval);
            }

            // Validate schedule name (refresh schedules after potential filter addition)
            $valid_schedules = wp_get_schedules();
            if (!isset($valid_schedules[$schedule])) {
                $schedule = 'hourly';
            }

            // Check if cron job is already scheduled with the correct schedule
            $next_scheduled = wp_next_scheduled($hook_name, [$item_id]);
            if ($next_scheduled !== false) {
                // Get the current cron array to check the schedule
                $cron_array = _get_cron_array();
                $current_schedule = null;

                if (isset($cron_array[$next_scheduled][$hook_name])) {
                    foreach ($cron_array[$next_scheduled][$hook_name] as $event) {
                        if (isset($event['args'][0]) && $event['args'][0] === $item_id) {
                            // Get the schedule from the recurrence
                            $recurrence = isset($event['schedule']) ? $event['schedule'] : null;
                            if ($recurrence === $schedule) {
                                // Cron job is already scheduled correctly, no need to reschedule
                                delete_transient($lock_key);
                                return;
                            }
                            break;
                        }
                    }
                }
            }

            // Clear all existing scheduled events for this hook and item_id
            // This ensures we don't have duplicate cron jobs
            wp_clear_scheduled_hook($hook_name, [$item_id]);

            // For minutely schedules, ensure we schedule for the next minute boundary
            // WordPress cron works better when scheduled at minute boundaries
            $current_time = time();
            if ($schedule === 'minutely' || $interval === 60) {
                // Schedule for the next minute (round up to next minute)
                $next_minute = ceil($current_time / 60) * 60;
                $schedule_time = $next_minute;
            } else {
                $schedule_time = $current_time;
            }

            // Schedule new event with error handling
            $result = wp_schedule_event($schedule_time, $schedule, $hook_name, [$item_id]);

            // Check if scheduling was successful
            if ($result === false) {
                // If scheduling failed, try to clear the cron array and retry once
                _get_cron_array(true); // Force refresh of cron array
                $result = wp_schedule_event(time(), $schedule, $hook_name, [$item_id]);

                if ($result === false) {
                    delete_transient($lock_key);
                    return;
                }
            }

            // Log successful scheduling with detailed info
            $next_run = wp_next_scheduled($hook_name, [$item_id]);
            $schedules = wp_get_schedules();
            $schedule_interval = isset($schedules[$schedule]) ? $schedules[$schedule]['interval'] : 'unknown';
        } finally {
            // Always release the lock
            delete_transient($lock_key);
        }
    }

    /**
     * Parse cron expression to interval in seconds
     *
     * @param string $cron_expression The cron expression (format: minute hour day month weekday)
     * @return int|false Interval in seconds or false if invalid
     */
    private function parse_cron_expression_to_interval($cron_expression)
    {
        // Trim whitespace
        $cron_expression = trim($cron_expression);

        // Split into parts
        $parts = preg_split('/\s+/', $cron_expression);

        if (count($parts) !== 5) {
            return false;
        }

        list($minute, $hour, $day, $month, $weekday) = $parts;

        // Check for "every minute" patterns
        if ($minute === '*' || $minute === '*/1') {
            return 60; // 1 minute
        }

        // Check for "every X minutes" pattern (e.g., */5, */10, */15, */30)
        if (preg_match('/^\*\/(\d+)$/', $minute, $matches)) {
            $minutes = (int)$matches[1];
            if ($minutes > 0 && $minutes <= 59) {
                return $minutes * 60;
            }
        }

        // Check for specific minute values (e.g., "0" = every hour at minute 0)
        if (is_numeric($minute) && $hour === '*') {
            // This means "at minute X of every hour", which is hourly
            return 3600; // 1 hour
        }

        // Check for hourly pattern (0 * * * *)
        if ($minute === '0' && $hour === '*') {
            return 3600; // 1 hour
        }

        // Check for daily pattern (0 0 * * *)
        if ($minute === '0' && $hour === '0' && $day === '*' && $month === '*') {
            return 86400; // 24 hours
        }

        // Check for weekly pattern (0 0 * * 0)
        if ($minute === '0' && $hour === '0' && $day === '*' && $month === '*' && $weekday === '0') {
            return 604800; // 7 days
        }

        // Check for "every X hours" pattern (0 */2 * * * = every 2 hours)
        if ($minute === '0' && preg_match('/^\*\/(\d+)$/', $hour, $matches)) {
            $hours = (int)$matches[1];
            if ($hours > 0 && $hours <= 23) {
                return $hours * 3600;
            }
        }

        // Default: if we can't parse it, return false
        return false;
    }

    /**
     * Get WordPress schedule name from interval
     *
     * @param int|false $interval Interval in seconds
     * @param string $cron_expression Original cron expression
     * @return string WordPress schedule name
     */
    private function get_schedule_name_from_interval($interval, $cron_expression)
    {
        // Standard WordPress schedules
        if ($interval === 60) {
            return 'minutely';
        } elseif ($interval === 3600) {
            return 'hourly';
        } elseif ($interval === 7200) {
            return 'twicedaily';
        } elseif ($interval === 86400) {
            return 'daily';
        } elseif ($interval === 604800) {
            return 'weekly';
        }

        // For custom intervals, create a custom schedule name
        if ($interval && $interval >= 60) {
            return 'brf_custom_' . $interval;
        }

        // Fallback to hourly if we can't determine
        return 'hourly';
    }

    /**
     * Ensure custom schedule exists for given interval
     *
     * @param int $interval Interval in seconds
     * @return void
     */
    private function ensure_custom_schedule_exists($interval)
    {
        if (!$interval || $interval < 60) {
            return;
        }

        $schedule_name = 'brf_custom_' . $interval;
        $schedules = wp_get_schedules();

        // If schedule doesn't exist, add it via filter
        if (!isset($schedules[$schedule_name])) {
            $display_text = $this->format_interval($interval);
            add_filter('cron_schedules', function ($existing_schedules) use ($schedule_name, $interval, $display_text) {
                if (!isset($existing_schedules[$schedule_name])) {
                    $existing_schedules[$schedule_name] = [
                        'interval' => $interval,
                        'display' => sprintf(__('Every %s', 'bricksforge'), $display_text)
                    ];
                }
                return $existing_schedules;
            }, 10, 1);
        }
    }

    /**
     * Unschedule cron job for an item
     *
     * @param object $item The API item
     * @return void
     */
    public function unschedule_cron_job($item)
    {
        // Handle both array and object formats
        $item_id = is_array($item) ? (isset($item['id']) ? $item['id'] : null) : (isset($item->id) ? $item->id : null);

        if (!$item_id) {
            return;
        }

        $hook_name = 'bricksforge_cpt_sync';

        // Clear all scheduled events for this hook and item_id
        // This is more reliable than checking for a specific timestamp
        wp_clear_scheduled_hook($hook_name, [$item_id]);
    }

    /**
     * Schedule all cron jobs for items with CPT sync enabled
     *
     * @return void
     */
    public function schedule_all_cron_jobs()
    {
        // Only run in admin or during cron execution to avoid performance issues
        if (!is_admin() && !defined('DOING_CRON')) {
            return;
        }

        // Use transient lock to prevent concurrent execution
        $lock_key = 'brf_cpt_sync_scheduling_all';
        if (get_transient($lock_key)) {
            // Another process is already scheduling all cron jobs
            return;
        }

        // Set lock for 10 seconds
        set_transient($lock_key, true, 10);

        try {
            $items = get_option('brf_external_api_loops', []);

            if (empty($items)) {
                return;
            }

            foreach ($items as $item) {
                // Handle both array and object formats
                $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);
                $item_id = is_array($item) ? (isset($item['id']) ? $item['id'] : null) : (isset($item->id) ? $item->id : null);

                if (!$item_id) {
                    continue;
                }

                if ($enableCptSync) {
                    $this->schedule_cron_job($item);
                } else {
                    // Unschedule if CPT sync is disabled
                    $this->unschedule_cron_job($item);
                }
            }
        } finally {
            // Always release the lock
            delete_transient($lock_key);
        }
    }

    /**
     * Update sync progress
     *
     * @param string $item_id The API item ID
     * @param string $step Current step description
     * @param int $current Current progress (e.g., current page)
     * @param int $total Total progress (e.g., total pages)
     * @return void
     */
    private function update_sync_progress($item_id, $step, $current = 0, $total = 0)
    {
        $progress_key = 'brf_cpt_sync_progress_' . $item_id;
        $progress = [
            'syncing' => true,
            'step' => $step,
            'current' => $current,
            'total' => $total,
        ];
        // Store for 5 minutes (sync should not take longer)
        set_transient($progress_key, $progress, 300);
    }

    /**
     * Clear sync progress
     *
     * @param string $item_id The API item ID
     * @return void
     */
    private function clear_sync_progress($item_id)
    {
        $progress_key = 'brf_cpt_sync_progress_' . $item_id;
        delete_transient($progress_key);
    }

    /**
     * Fetch all paginated pages from API
     *
     * @param string $item_id The API item ID
     * @param array|object $item The API item configuration
     * @return array|WP_Error Combined data from all pages or WP_Error on failure
     */
    private function fetch_all_paginated_pages($item_id, $item)
    {
        $pagination = $this->get_item_property($item, 'cptSettings.pagination', []);

        if (empty($pagination) || !isset($pagination['enabled']) || !$pagination['enabled']) {
            // Pagination not enabled, return null to use regular fetch
            return null;
        }

        $strategy = isset($pagination['strategy']) ? $pagination['strategy'] : 'auto';
        $max_pages = isset($pagination['maxPages']) ? $pagination['maxPages'] : 'all';
        $all_data = [];
        $errors = [];
        $start_time = time();
        $max_execution_time = 30; // Max 30 seconds for pagination fetch
        $rate_limit_delay = 0.1; // 100ms delay between requests

        // Update progress: Starting pagination
        $this->update_sync_progress($item_id, 'Fetching paginated data...', 0, 0);

        // Get ExternalApiLoops instance
        $external_api_loops = $this->get_external_api_loops();
        $external_api_loops->get_items();
        $external_api_loops->collect_active_items();

        // Auto-detect strategy if not set or set to 'auto'
        if ($strategy === 'auto' || empty($strategy)) {
            $page_key = isset($pagination['pageKey']) ? $pagination['pageKey'] : '';
            $total_pages_key = isset($pagination['totalPagesKey']) ? $pagination['totalPagesKey'] : '';
            $offset_key = isset($pagination['offsetKey']) ? $pagination['offsetKey'] : '';
            $limit_key = isset($pagination['limitKey']) ? $pagination['limitKey'] : '';

            // If page-based keys are present, use page-based
            if (!empty($page_key) && !empty($total_pages_key)) {
                $strategy = 'page';
            }
            // If offset-based keys are present, use offset-based
            elseif (!empty($offset_key) && !empty($limit_key)) {
                $strategy = 'offset';
            }
            // Default to page-based if we can't determine
            else {
                $strategy = 'page';
            }
        }

        // Determine strategy
        if ($strategy === 'page') {
            // Page-based pagination
            $page_key = isset($pagination['pageKey']) ? $pagination['pageKey'] : 'page';
            $per_page_key = isset($pagination['perPageKey']) ? $pagination['perPageKey'] : 'per_page';
            $total_pages_key = isset($pagination['totalPagesKey']) ? $pagination['totalPagesKey'] : 'total_pages';

            // Fetch first page to determine total pages
            $first_page_params = [$page_key => 1];
            if (!empty($per_page_key)) {
                $items_per_page = isset($pagination['itemsPerPage']) ? intval($pagination['itemsPerPage']) : 20;
                $first_page_params[$per_page_key] = $items_per_page;
            }

            $first_page = $external_api_loops->run_api_request($item_id, null, true, [], $first_page_params);

            if (is_wp_error($first_page)) {
                return $first_page;
            }

            // Extract data array from response
            $first_page_data = $this->extract_data_array($first_page, $item);
            if (!empty($first_page_data)) {
                $all_data = array_merge($all_data, $first_page_data);
            }

            // Determine total pages
            $total_pages = $this->get_nested_value($first_page, $total_pages_key);
            if (!$total_pages || !is_numeric($total_pages)) {
                // Try to calculate from total items and per page
                $total_items = $this->get_nested_value($first_page, isset($pagination['totalItemsKey']) ? $pagination['totalItemsKey'] : 'total');
                $per_page = $this->get_nested_value($first_page, $per_page_key);
                if ($total_items && $per_page) {
                    $total_pages = ceil($total_items / $per_page);
                } else {
                    // Can't determine total pages, return first page only
                    return $first_page_data ? $first_page_data : $first_page;
                }
            }

            // Apply max pages limit
            // Handle both string and numeric values (e.g., "5" or 5)
            if ($max_pages !== 'all' && $max_pages !== 'custom') {
                $max_pages_int = is_numeric($max_pages) ? intval($max_pages) : 0;
                if ($max_pages_int > 0) {
                    $total_pages = min(intval($total_pages), $max_pages_int);
                }
            }

            // Update progress: Total pages determined
            $this->update_sync_progress($item_id, sprintf('Fetching page %d of %d...', 1, $total_pages), 1, $total_pages);

            // Fetch remaining pages
            for ($page = 2; $page <= intval($total_pages); $page++) {
                // Update progress
                $this->update_sync_progress($item_id, sprintf('Fetching page %d of %d...', $page, $total_pages), $page, $total_pages);
                // Check timeout
                if (time() - $start_time > $max_execution_time) {
                    $errors[] = sprintf('Pagination timeout: Only fetched %d of %d pages', $page - 1, $total_pages);
                    break;
                }

                // Rate limiting
                usleep($rate_limit_delay * 1000000); // Convert to microseconds

                $page_params = [$page_key => $page];
                if (!empty($per_page_key)) {
                    $items_per_page = isset($pagination['itemsPerPage']) ? intval($pagination['itemsPerPage']) : 20;
                    $page_params[$per_page_key] = $items_per_page;
                }

                $page_data = $external_api_loops->run_api_request($item_id, null, true, [], $page_params);

                if (is_wp_error($page_data)) {
                    $errors[] = sprintf('Error fetching page %d: %s', $page, $page_data->get_error_message());
                    continue;
                }

                $page_items = $this->extract_data_array($page_data, $item);
                if (empty($page_items)) {
                    // No more data, stop
                    break;
                }

                $all_data = array_merge($all_data, $page_items);
            }
        } else if ($strategy === 'offset') {
            // Offset-based pagination
            $offset_key = isset($pagination['offsetKey']) ? $pagination['offsetKey'] : 'offset';
            $limit_key = isset($pagination['limitKey']) ? $pagination['limitKey'] : 'limit';
            $items_per_page = isset($pagination['itemsPerPage']) ? intval($pagination['itemsPerPage']) : 20;

            $offset = 0;
            $page = 1;
            // Handle both string and numeric values (e.g., "5" or 5)
            if ($max_pages === 'all' || $max_pages === 'custom') {
                $max_pages_int = PHP_INT_MAX;
            } else {
                $max_pages_int = is_numeric($max_pages) ? intval($max_pages) : PHP_INT_MAX;
            }

            // Update progress: Starting offset-based pagination
            $this->update_sync_progress($item_id, 'Fetching paginated data (offset-based)...', 0, $max_pages_int !== PHP_INT_MAX ? $max_pages_int : 0);

            while ($page <= $max_pages_int) {
                // Update progress
                if ($max_pages_int !== PHP_INT_MAX) {
                    $this->update_sync_progress($item_id, sprintf('Fetching page %d of %d (offset %d)...', $page, $max_pages_int, $offset), $page, $max_pages_int);
                } else {
                    $this->update_sync_progress($item_id, sprintf('Fetching page %d (offset %d)...', $page, $offset), $page, 0);
                }

                // Check timeout
                if (time() - $start_time > $max_execution_time) {
                    $errors[] = sprintf('Pagination timeout: Fetched %d pages', $page - 1);
                    break;
                }

                // Rate limiting
                if ($page > 1) {
                    usleep($rate_limit_delay * 1000000);
                }

                $offset_params = [
                    $offset_key => $offset,
                    $limit_key => $items_per_page
                ];

                $page_data = $external_api_loops->run_api_request($item_id, null, true, [], $offset_params);

                if (is_wp_error($page_data)) {
                    $errors[] = sprintf('Error fetching page %d (offset %d): %s', $page, $offset, $page_data->get_error_message());
                    break;
                }

                $page_items = $this->extract_data_array($page_data, $item);

                if (empty($page_items)) {
                    // No more data, stop
                    break;
                }

                $all_data = array_merge($all_data, $page_items);

                // If we got fewer items than requested, we've reached the end
                if (count($page_items) < $items_per_page) {
                    break;
                }

                $offset += $items_per_page;
                $page++;
            }
        } else {
            // Manual strategy - user must configure keys manually
            // For now, return null to use regular fetch
            return null;
        }

        // If we have errors but also data, log errors but return data
        if (!empty($errors)) {
            foreach ($errors as $error) {
                $this->store_sync_error($item_id, $error);
            }
        }

        // Return combined data in same format as original response
        // Try to preserve response structure
        if (!empty($all_data)) {
            // If first page had a wrapper structure, try to preserve it
            $page_key = isset($pagination['pageKey']) ? $pagination['pageKey'] : 'page';
            $first_page = $external_api_loops->run_api_request($item_id, null, true, [], [$page_key => 1]);
            if (!is_wp_error($first_page) && !$this->is_flat_array($first_page)) {
                // Response has structure, try to preserve it
                $wrapped_response = $first_page;
                $data_key = $this->find_data_key($first_page);
                if ($data_key) {
                    $wrapped_response[$data_key] = $all_data;
                    // Update pagination metadata if present
                    $total_pages_key = isset($pagination['totalPagesKey']) ? $pagination['totalPagesKey'] : 'total_pages';
                    if (isset($wrapped_response[$total_pages_key])) {
                        $items_per_page = isset($pagination['itemsPerPage']) ? intval($pagination['itemsPerPage']) : 20;
                        $wrapped_response[$total_pages_key] = count($all_data) > 0 ? ceil(count($all_data) / $items_per_page) : 1;
                    }
                    return $wrapped_response;
                }
            }
            // Return flat array if no structure found
            return $all_data;
        }

        return empty($all_data) ? new \WP_Error('no_data', 'No data fetched from paginated API') : $all_data;
    }

    /**
     * Extract data array from API response
     * Handles various response structures (flat array, wrapped in data key, etc.)
     *
     * @param mixed $response API response
     * @param array|object $item API item configuration
     * @return array Extracted data array
     */
    private function extract_data_array($response, $item)
    {
        if (empty($response)) {
            return [];
        }

        // If response is already a flat array, return it
        if (is_array($response) && $this->is_flat_array($response)) {
            return $response;
        }

        // Try common data keys
        $data_keys = ['data', 'results', 'items', 'records', 'content'];
        foreach ($data_keys as $key) {
            if (isset($response[$key]) && is_array($response[$key])) {
                return $response[$key];
            }
        }

        // If response is object, convert to array and try again
        if (is_object($response)) {
            $response = json_decode(json_encode($response), true);
            foreach ($data_keys as $key) {
                if (isset($response[$key]) && is_array($response[$key])) {
                    return $response[$key];
                }
            }
        }

        // If still not found, return response as-is if it's an array
        return is_array($response) ? $response : [];
    }

    /**
     * Check if array is flat (not nested objects/arrays)
     *
     * @param mixed $data Data to check
     * @return bool True if flat array
     */
    private function is_flat_array($data)
    {
        if (!is_array($data)) {
            return false;
        }

        // Check if array is indexed (0, 1, 2...) and contains objects/arrays
        $keys = array_keys($data);
        $is_indexed = array_keys($keys) === $keys; // Check if keys are 0, 1, 2...

        if ($is_indexed && !empty($data)) {
            // Check first element - if it's an object/array, it's likely a data array
            $first = reset($data);
            return is_array($first) || is_object($first);
        }

        return false;
    }

    /**
     * Find the key that contains the data array in response
     *
     * @param mixed $response API response
     * @return string|null Key name or null
     */
    private function find_data_key($response)
    {
        $data_keys = ['data', 'results', 'items', 'records', 'content'];
        foreach ($data_keys as $key) {
            if (isset($response[$key]) && is_array($response[$key])) {
                return $key;
            }
        }
        return null;
    }

    /**
     * Get nested value from array/object using dot notation
     *
     * @param mixed $data Data structure
     * @param string $key Dot-notation key (e.g., 'meta.pagination.page')
     * @return mixed Value or null
     */
    private function get_nested_value($data, $key)
    {
        if (empty($key)) {
            return null;
        }

        // Handle object
        if (is_object($data)) {
            $data = json_decode(json_encode($data), true);
        }

        if (!is_array($data)) {
            return null;
        }

        // Try direct key first
        if (isset($data[$key])) {
            return $data[$key];
        }

        // Try case-insensitive match
        foreach (array_keys($data) as $data_key) {
            if (strtolower($data_key) === strtolower($key)) {
                return $data[$data_key];
            }
        }

        // Try dot notation
        $parts = explode('.', $key);
        $current = $data;
        foreach ($parts as $part) {
            if (!is_array($current) || !isset($current[$part])) {
                // Try case-insensitive
                $found = false;
                foreach (array_keys($current) as $current_key) {
                    if (strtolower($current_key) === strtolower($part)) {
                        $current = $current[$current_key];
                        $found = true;
                        break;
                    }
                }
                if (!$found) {
                    return null;
                }
            } else {
                $current = $current[$part];
            }
        }

        return $current;
    }

    /**
     * Run scheduled sync
     *
     * @param string $item_id The API item ID
     * @return void
     */
    public function run_scheduled_sync($item_id)
    {
        // Use transient lock to prevent concurrent sync executions
        $lock_key = 'brf_cpt_sync_running_' . $item_id;
        if (get_transient($lock_key)) {
            // Another sync is already running for this item, skip this execution
            return;
        }

        // Set lock for 5 minutes (sync should not take longer than this)
        set_transient($lock_key, true, 300);

        // Log that sync started

        try {
            // Always force fetch when running scheduled sync to ensure we have the latest data
            $result = $this->sync_api_to_cpt($item_id, true);

            // Handle sync result
            if (is_wp_error($result)) {
                $error_message = $result->get_error_message();
                $this->store_sync_error($item_id, $error_message);
            } elseif (isset($result['errors']) && !empty($result['errors'])) {
                $error_message = implode(', ', $result['errors']);
                $this->store_sync_error($item_id, $error_message);
            } else {
                // Log successful sync with stats
                $stats = isset($result['stats']) ? $result['stats'] : [];
                $created = isset($stats['created']) ? $stats['created'] : 0;
                $updated = isset($stats['updated']) ? $stats['updated'] : 0;
                $deleted = isset($stats['deleted']) ? $stats['deleted'] : 0;

                // Success - errors are logged to the Logs system, no need to clear anything
            }
        } catch (\Exception $e) {
            $error_message = $e->getMessage();
            $this->store_sync_error($item_id, $error_message);
        } finally {
            // Always release the lock
            delete_transient($lock_key);
        }
    }

    /**
     * Store sync error in the logging system
     *
     * @param string $item_id The API item ID
     * @param string $error_message The error message
     * @return void
     */
    private function store_sync_error($item_id, $error_message)
    {
        // Get item information for better log message
        $items = get_option('brf_external_api_loops', []);
        $item_label = 'Unknown API';

        foreach ($items as $item) {
            $current_id = $this->get_item_property($item, 'id', '');
            if ($current_id === $item_id) {
                $item_label = $this->get_item_property($item, 'label', 'Unknown API');
                break;
            }
        }

        // Log error to the logging system
        $log_message = sprintf('CPT Sync failed for "%s"', $item_label);
        $log_context = [
            'item_id' => $item_id,
            'item_label' => $item_label,
            'error_message' => $error_message
        ];

        ProFormsLogger::log_error('cpt_sync', $log_message, $log_context);
    }

    /**
     * Display sync errors in admin notices
     * 
     * DEPRECATED: This method is no longer used. Errors are now logged to the Logs system
     * and can be viewed in the Logs page (src/admin/pages/Logs.vue).
     *
     * @return void
     */
    public function display_sync_errors()
    {
        // This method is kept for backwards compatibility but does nothing
        // Errors are now logged directly to the Logs system via store_sync_error()
        return;
    }

    /**
     * Get sync status for an item
     *
     * @param string $item_id The API item ID
     * @return array Sync status information
     */
    public function get_sync_status($item_id)
    {
        $item = null;
        $items = get_option('brf_external_api_loops', []);

        foreach ($items as $i) {
            $current_id = is_array($i) ? (isset($i['id']) ? $i['id'] : null) : (isset($i->id) ? $i->id : null);

            if ($current_id === $item_id) {
                $item = $i;
                break;
            }
        }

        if (!$item) {
            return ['error' => 'Item not found'];
        }

        $status = [
            'enabled' => $this->get_item_property($item, 'enableCptSync', false),
            'last_sync' => $this->get_item_property($item, 'cptSettings.lastSync', null),
            'stats' => $this->get_item_property($item, 'cptSettings.syncStats', null),
            'next_scheduled' => null,
            'has_error' => false,
            'error_message' => null
        ];

        // Check for scheduled sync
        $next_scheduled = wp_next_scheduled('bricksforge_cpt_sync', [$item_id]);
        if ($next_scheduled) {
            $status['next_scheduled'] = date('Y-m-d H:i:s', $next_scheduled);
        }

        // Errors are now logged to the Logs system (see Logs page)
        // Check for recent errors in logs (last hour)
        $logs = ProFormsLogger::get_logs(10, 'error', 'cpt_sync');
        foreach ($logs as $log) {
            if (isset($log['context']['item_id']) && $log['context']['item_id'] === $item_id) {
                $log_time = strtotime($log['timestamp']);
                $one_hour_ago = strtotime('-1 hour');
                if ($log_time > $one_hour_ago) {
                    $status['has_error'] = true;
                    $status['error_message'] = $log['context']['error_message'] ?? $log['message'] ?? null;
                    break;
                }
            }
        }

        return $status;
    }

    /**
     * Cleanup custom parent menu options that are no longer in use
     *
     * @return void
     */
    public function cleanup_unused_parent_menus()
    {
        global $wpdb;

        // Get all custom parent options
        $parent_options = $wpdb->get_results(
            "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'brf-cpt-parent-%'",
            ARRAY_A
        );

        if (empty($parent_options)) {
            return;
        }

        // Get all active parent slugs
        $items = get_option('brf_external_api_loops', []);
        $active_parents = [];

        foreach ($items as $item) {
            $enableCptSync = is_array($item) ? (isset($item['enableCptSync']) ? $item['enableCptSync'] : false) : (isset($item->enableCptSync) ? $item->enableCptSync : false);

            if (!$enableCptSync) {
                continue;
            }

            $menu_settings = is_array($item) ? (isset($item['cptSettings']['menuSettings']) ? $item['cptSettings']['menuSettings'] : null) : (isset($item->cptSettings->menuSettings) ? $item->cptSettings->menuSettings : null);

            if (is_array($menu_settings)) {
                $menu_position = isset($menu_settings['menuPosition']) ? $menu_settings['menuPosition'] : 'parent';
                $menu_parent_label = isset($menu_settings['menuParentLabel']) ? $menu_settings['menuParentLabel'] : '';
            } else {
                $menu_position = isset($menu_settings->menuPosition) ? $menu_settings->menuPosition : 'parent';
                $menu_parent_label = isset($menu_settings->menuParentLabel) ? $menu_settings->menuParentLabel : '';
            }

            if ($menu_position === 'custom_parent' && !empty($menu_parent_label)) {
                $parent_slug = 'brf-api-parent-' . sanitize_title($menu_parent_label);
                $active_parents[] = 'brf-cpt-parent-' . str_replace('brf-api-parent-', '', $parent_slug);
            }
        }

        // Delete unused parent options
        foreach ($parent_options as $option) {
            if (!in_array($option['option_name'], $active_parents)) {
                delete_option($option['option_name']);
            }
        }
    }

    /**
     * Add metabox for displaying API custom fields
     *
     * @return void
     */
    public function add_api_fields_metabox()
    {
        global $post;

        if (!$post) {
            return;
        }

        // Only add metabox for our API CPTs (those starting with 'bfa-')
        if (strpos($post->post_type, 'bfa-') === 0) {
            add_meta_box(
                'brf_api_fields',
                'API Data Fields',
                [$this, 'render_api_fields_metabox'],
                $post->post_type,
                'normal',
                'core' // Highest priority to ensure it appears first
            );
        }
    }


    /**
     * Filter Child CPT admin list by parent post ID
     *
     * @param WP_Query $query The WordPress query object
     * @return void
     */
    public function filter_child_cpt_by_parent($query)
    {
        // Only run in admin and for main query
        if (!is_admin() || !$query->is_main_query()) {
            return;
        }

        // Check if brf_parent_id parameter is set
        $parent_id = isset($_GET['brf_parent_id']) ? intval($_GET['brf_parent_id']) : 0;

        if ($parent_id <= 0) {
            return;
        }

        // Only filter for our API CPTs (those starting with 'bfa-')
        $post_type = isset($_GET['post_type']) ? sanitize_text_field($_GET['post_type']) : '';
        if (empty($post_type) || strpos($post_type, 'bfa-') !== 0) {
            return;
        }

        // Add meta query to filter by parent post ID
        $meta_query = $query->get('meta_query');
        if (!is_array($meta_query)) {
            $meta_query = [];
        }

        $meta_query[] = [
            'key' => '_brf_parent_post_id',
            'value' => $parent_id,
            'compare' => '='
        ];

        $query->set('meta_query', $meta_query);
    }

    /**
     * Extract array key from custom field name
     * Custom fields are stored as brf_field_{normalized_key}
     * The key is normalized by flatten_array() then sanitize_key()
     * We need to match them back to the original array key
     *
     * @param string $field_name The custom field name (e.g., brf_field_arraykey)
     * @param string $source_id The API source ID
     * @return string|null The original array key if found, null otherwise
     */
    private function get_array_key_from_field_name($field_name, $source_id)
    {
        // Remove brf_field_ prefix
        $stored_key = str_replace('brf_field_', '', $field_name);

        // The stored_key might have underscores (from flatten_array) or hyphens (from sanitize_key)
        // We need to check both variants

        // Get all items
        $items = get_option('brf_external_api_loops', []);

        foreach ($items as $item) {
            $item_id = is_array($item) ? (isset($item['id']) ? $item['id'] : null) : (isset($item->id) ? $item->id : null);

            if ($item_id !== $source_id) {
                continue;
            }

            // Also check childCpts arrayKey (this is more important for Child CPTs)
            $childCpts = $this->get_item_property($item, 'cptSettings.childCpts', []);
            if (!empty($childCpts) && is_array($childCpts)) {
                foreach ($childCpts as $child_config) {
                    $array_key = $this->get_item_property($child_config, 'arrayKey', '');
                    if (!empty($array_key)) {
                        // Normalize the array key the same way it's stored
                        // First flatten_array normalization, then sanitize_key
                        $normalized_by_flatten = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $array_key));
                        $final_normalized = sanitize_key($normalized_by_flatten);

                        // Also create a version with underscores (before sanitize_key)
                        $normalized_with_underscores = $normalized_by_flatten;

                        // Also check the original array_key (case-insensitive, normalized)
                        $original_normalized = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $array_key));

                        // Exact match (all variants)
                        if (
                            $final_normalized === $stored_key ||
                            $normalized_with_underscores === $stored_key ||
                            $original_normalized === $stored_key
                        ) {
                            return $array_key;
                        }

                        // Check if stored_key starts with the normalized array key (for nested fields)
                        // Handle both underscore and hyphen variants (we now use dashes, but support underscores for backwards compatibility)
                        // e.g., stored_key = "chapters-chapter" (new) or "chapters_chapter" (legacy), array_key = "chapters"
                        $prefixes_to_check = [
                            $final_normalized . '_',
                            $final_normalized . '-',
                            $normalized_with_underscores . '_',
                            $normalized_with_underscores . '-',
                            $original_normalized . '_',
                            $original_normalized . '-'
                        ];

                        foreach ($prefixes_to_check as $prefix) {
                            if (strpos($stored_key, $prefix) === 0) {
                                return $array_key;
                            }
                        }

                        // Also check if stored_key starts with a plural/singular variant of the array key
                        // This handles cases where stored_key = "chapters_chapter" but array_key = "chapter"
                        // We check if stored_key starts with the array_key (possibly pluralized)
                        if (
                            strpos($stored_key, $final_normalized) === 0 ||
                            strpos($stored_key, $normalized_with_underscores) === 0 ||
                            strpos($stored_key, $original_normalized) === 0
                        ) {
                            // Check if it's followed by underscore or hyphen (not just a substring)
                            $key_length = strlen($final_normalized);
                            if (strlen($stored_key) > $key_length) {
                                $next_char = substr($stored_key, $key_length, 1);
                                if ($next_char === '_' || $next_char === '-') {
                                    return $array_key;
                                }
                            }
                        }

                        // Also check if stored_key starts with plural version of array_key
                        // e.g., array_key = "chapter", stored_key = "chapters_chapter"
                        $plural_variants = [
                            $final_normalized . 's',
                            $final_normalized . 'es',
                            $normalized_with_underscores . 's',
                            $normalized_with_underscores . 'es'
                        ];

                        foreach ($plural_variants as $plural) {
                            if (
                                strpos($stored_key, $plural . '_') === 0 ||
                                strpos($stored_key, $plural . '-') === 0
                            ) {
                                return $array_key;
                            }
                        }

                        // Also check if stored_key ends with the array key (for nested fields)
                        // e.g., stored_key = "author_biography_paragraph", array_key = "paragraph"
                        $suffixes_to_check = [
                            '_' . $final_normalized,
                            '-' . $final_normalized,
                            '_' . $normalized_with_underscores,
                            '-' . $normalized_with_underscores,
                            '_' . $original_normalized,
                            '-' . $original_normalized
                        ];

                        foreach ($suffixes_to_check as $suffix) {
                            if (substr($stored_key, -strlen($suffix)) === $suffix) {
                                return $array_key;
                            }
                        }
                    }
                }
            }

            // Check selectedNestedArrays
            $selectedNestedArrays = $this->get_item_property($item, 'selectedNestedArrays', []);
            if (!empty($selectedNestedArrays) && is_array($selectedNestedArrays)) {
                foreach ($selectedNestedArrays as $array_key) {
                    if (is_string($array_key)) {
                        // Normalize the array key the same way it's stored
                        // First flatten_array normalization, then sanitize_key
                        $normalized_by_flatten = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $array_key));
                        $final_normalized = sanitize_key($normalized_by_flatten);
                        $normalized_with_underscores = $normalized_by_flatten;

                        // Exact match (both variants)
                        if ($final_normalized === $stored_key || $normalized_with_underscores === $stored_key) {
                            return $array_key;
                        }

                        // Check if stored_key starts with the normalized array key (for nested fields)
                        $prefixes_to_check = [
                            $final_normalized . '_',
                            $final_normalized . '-',
                            $normalized_with_underscores . '_',
                            $normalized_with_underscores . '-'
                        ];

                        foreach ($prefixes_to_check as $prefix) {
                            if (strpos($stored_key, $prefix) === 0) {
                                return $array_key;
                            }
                        }
                    }
                }
            }
        }

        return null;
    }

    /**
     * Get Child CPT information for a nested array
     *
     * @param string $source_id The API source ID
     * @param string $array_key The nested array key
     * @param string $parent_cpt_slug The parent CPT slug
     * @return array|null Array with 'slug' and 'label' if Child CPT exists, null otherwise
     */
    private function get_child_cpt_info($source_id, $array_key, $parent_cpt_slug)
    {
        if (empty($source_id) || empty($array_key) || empty($parent_cpt_slug)) {
            return null;
        }

        // Get all items
        $items = get_option('brf_external_api_loops', []);

        foreach ($items as $item) {
            $item_id = is_array($item) ? (isset($item['id']) ? $item['id'] : null) : (isset($item->id) ? $item->id : null);

            if ($item_id !== $source_id) {
                continue;
            }

            // Get child CPTs configuration
            $childCpts = $this->get_item_property($item, 'cptSettings.childCpts', []);

            if (empty($childCpts) || !is_array($childCpts)) {
                return null;
            }

            // Find matching child CPT configuration
            foreach ($childCpts as $child_config) {
                $config_array_key = $this->get_item_property($child_config, 'arrayKey', '');

                if ($config_array_key === $array_key) {
                    // Generate child CPT slug
                    $child_slug_base = $parent_cpt_slug . '-' . sanitize_title($array_key);
                    if (strlen($child_slug_base) > 20) {
                        $child_slug_base = substr($child_slug_base, 0, 20);
                    }
                    $child_cpt_slug = $child_slug_base;

                    // Get label
                    $childCptLabel = $this->get_item_property($child_config, 'label', '');
                    $parent_label = $this->get_item_property($item, 'label', 'API Data');
                    if (empty($childCptLabel)) {
                        $childCptLabel = $parent_label . ' ' . ucfirst($array_key);
                    }

                    // Check if CPT exists
                    if (post_type_exists($child_cpt_slug)) {
                        return [
                            'slug' => $child_cpt_slug,
                            'label' => $childCptLabel
                        ];
                    }
                }
            }
        }

        return null;
    }

    /**
     * Render the API fields metabox
     *
     * @param WP_Post $post The post object
     * @return void
     */
    public function render_api_fields_metabox($post)
    {
        // Get all post meta
        $all_meta = get_post_meta($post->ID);

        // Filter for our custom fields (brf_field_ prefix)
        $api_fields = [];
        $nested_arrays = [];
        $system_fields = [];

        foreach ($all_meta as $key => $value) {
            // Skip WordPress internal fields
            if (strpos($key, '_') === 0 && strpos($key, '_brf_api_') !== 0 && strpos($key, '_brf_field_') !== 0 && strpos($key, '_brf_parent_') !== 0) {
                continue;
            }

            // System fields (for display)
            if (strpos($key, '_brf_api_') === 0) {
                $system_fields[$key] = $value[0];
            }
            // Parent reference fields (also show in custom fields)
            elseif (strpos($key, '_brf_parent_') === 0) {
                $system_fields[$key] = $value[0];
                // Also add to custom fields for visibility
                $api_fields[$key] = $value[0];
            }
            // Nested arrays
            elseif (strpos($key, '_brf_api_nested_') === 0) {
                $array_name = str_replace('_brf_api_nested_', '', $key);
                $nested_arrays[$array_name] = $value[0];
            }
            // Custom fields
            elseif (strpos($key, 'brf_field_') === 0) {
                // Store with full meta key for display
                $api_fields[$key] = $value[0];
            }
        }

        // Get raw API data
        $raw_data = isset($system_fields['_brf_api_raw_data']) ? $system_fields['_brf_api_raw_data'] : '';
        $last_synced = isset($system_fields['_brf_api_last_synced']) ? $system_fields['_brf_api_last_synced'] : '';
        $unique_id = isset($system_fields['_brf_api_unique_id']) ? $system_fields['_brf_api_unique_id'] : '';
        $source_id = isset($system_fields['_brf_api_source_id']) ? $system_fields['_brf_api_source_id'] : '';

        // Check if this is a child post
        $parent_post_id = isset($system_fields['_brf_parent_post_id']) ? $system_fields['_brf_parent_post_id'] : '';
        $parent_post_type = isset($system_fields['_brf_parent_post_type']) ? $system_fields['_brf_parent_post_type'] : '';
        $array_key = isset($system_fields['_brf_array_key']) ? $system_fields['_brf_array_key'] : '';
        $is_child_post = !empty($parent_post_id);

?>
        <div class="brf-api-fields-metabox">
            <style>
                .brf-api-fields-metabox {
                    font-size: 13px;
                }

                .brf-field-group {
                    margin-bottom: 20px;
                }

                .brf-field-group-title {
                    font-size: 14px;
                    font-weight: 600;
                    margin-bottom: 12px;
                    padding-bottom: 8px;
                    border-bottom: 1px solid #ddd;
                    color: #23282d;
                }

                .brf-field-item {
                    display: flex;
                    padding: 10px;
                    border-bottom: 1px solid #f0f0f0;
                }

                .brf-field-item:last-child {
                    border-bottom: none;
                }

                .brf-field-label {
                    font-weight: 500;
                    min-width: 150px;
                    color: #555;
                    padding-right: 15px;
                }

                .brf-field-value {
                    flex: 1;
                    word-break: break-word;
                }

                .brf-field-value code {
                    background: #f5f5f5;
                    padding: 2px 6px;
                    border-radius: 3px;
                    font-size: 12px;
                    color: #d63384;
                }

                .brf-json-viewer {
                    background: #f8f9fa;
                    border: 1px solid #dee2e6;
                    border-radius: 4px;
                    padding: 12px;
                    max-height: 400px;
                    overflow-y: auto;
                    font-family: monospace;
                    font-size: 12px;
                    white-space: pre-wrap;
                    word-wrap: break-word;
                }

                .brf-system-info {
                    background: #f8f9fa;
                    padding: 10px;
                    border-radius: 4px;
                    margin-bottom: 15px;
                }

                .brf-system-info-item {
                    margin: 5px 0;
                    font-size: 12px;
                }

                .brf-toggle-raw {
                    margin-top: 10px;
                    cursor: pointer;
                    color: #2271b1;
                    text-decoration: underline;
                    font-size: 12px;
                }

                .brf-toggle-raw:hover {
                    color: #135e96;
                }

                .brf-raw-data-container {
                    display: none;
                    margin-top: 10px;
                }

                .brf-raw-data-container.show {
                    display: block;
                }

                .brf-parent-post-info {
                    background: #e8f4fd;
                    padding: 12px;
                    border-radius: 4px;
                    border-left: 4px solid #2271b1;
                    margin-bottom: 15px;
                }

                .brf-parent-post-link {
                    color: #2271b1;
                    text-decoration: none;
                    font-weight: 600;
                    font-size: 14px;
                }

                .brf-parent-post-link:hover {
                    text-decoration: underline;
                }

                .brf-parent-info-item {
                    margin: 5px 0;
                    font-size: 12px;
                }

                .brf-child-cpt-link {
                    display: inline-flex;
                    align-items: center;
                    margin-top: 8px;
                    padding: 5px 10px;
                    background: #f8f9fa;
                    color: #50575e;
                    text-decoration: none;
                    border: 1px solid #dcdcde;
                    border-radius: 4px;
                    font-size: 12px;
                    font-weight: 500;
                    transition: all 0.15s ease;
                    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.03);
                }

                .brf-child-cpt-link:hover {
                    background: #f0f6fc;
                    border-color: #c3d4e6;
                    color: #2271b1;
                }

                .brf-child-cpt-link .dashicons {
                    font-size: 14px;
                    width: 14px;
                    height: 14px;
                    line-height: 1;
                    margin-right: 5px;
                    opacity: 0.8;
                    transition: opacity 0.15s ease;
                    display: inline-block;
                }

                .brf-child-cpt-link:hover .dashicons {
                    opacity: 1;
                }
            </style>

            <?php if ($is_child_post): ?>
                <div class="brf-field-group">
                    <div class="brf-field-group-title">
                        <i class="dashicons dashicons-admin-links" style="font-size: 18px; vertical-align: middle;"></i>
                        Parent Post Relationship
                    </div>
                    <div class="brf-parent-post-info">
                        <?php
                        $parent_post = get_post($parent_post_id);
                        if ($parent_post):
                            $edit_link = get_edit_post_link($parent_post_id);
                        ?>
                            <div class="brf-parent-info-item">
                                <strong>Parent Post:</strong>
                                <a href="<?php echo esc_url($edit_link); ?>" class="brf-parent-post-link">
                                    <?php echo esc_html($parent_post->post_title); ?>
                                </a>
                            </div>
                            <div class="brf-parent-info-item">
                                <strong>Parent Type:</strong> <code><?php echo esc_html($parent_post_type); ?></code>
                            </div>
                            <?php if ($array_key): ?>
                                <div class="brf-parent-info-item">
                                    <strong>Array Key:</strong> <code><?php echo esc_html($array_key); ?></code>
                                </div>
                            <?php endif; ?>
                            <div class="brf-parent-info-item" style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #c3d4e6;">
                                <strong>Reference Fields:</strong>
                                <div style="margin-top: 5px;">
                                    <div style="margin-bottom: 3px;">
                                        <code style="background: #fff; padding: 2px 6px; border: 1px solid #c3d4e6;">_brf_parent_post_id</code>
                                        <span style="color: #666; font-size: 11px; margin-left: 5px;">
                                            (System field for internal queries)
                                        </span>
                                    </div>
                                    <div>
                                        <code style="background: #fff; padding: 2px 6px; border: 1px solid #c3d4e6;">brf_field_parent_id</code>
                                        <span style="color: #666; font-size: 11px; margin-left: 5px;">
                                            (Custom field for Bricks Builder Dynamic Data Tags)
                                        </span>
                                    </div>
                                </div>
                            </div>
                        <?php else: ?>
                            <div class="brf-parent-info-item" style="color: #d63638;">
                                <strong>Warning:</strong> Parent post not found (ID: <?php echo esc_html($parent_post_id); ?>)
                            </div>
                        <?php endif; ?>
                    </div>
                </div>
            <?php endif; ?>

            <?php if (!empty($system_fields)): ?>
                <div class="brf-field-group">
                    <div class="brf-field-group-title">System Information</div>
                    <div class="brf-system-info">
                        <?php if ($unique_id): ?>
                            <div class="brf-system-info-item"><strong>Unique ID:</strong> <code><?php echo esc_html($unique_id); ?></code></div>
                        <?php endif; ?>
                        <?php if ($last_synced): ?>
                            <div class="brf-system-info-item"><strong>Last Synced:</strong> <?php echo esc_html($last_synced); ?></div>
                        <?php endif; ?>
                        <?php if ($source_id): ?>
                            <div class="brf-system-info-item"><strong>API Source ID:</strong> <code><?php echo esc_html($source_id); ?></code></div>
                        <?php endif; ?>
                    </div>
                </div>
            <?php endif; ?>

            <?php if (!empty($api_fields)): ?>
                <div class="brf-field-group">
                    <div class="brf-field-group-title">Custom Fields</div>
                    <?php foreach ($api_fields as $field_name => $field_value): ?>
                        <?php
                        // Check if this custom field corresponds to a nested array with Child CPT
                        $array_key = null;
                        $child_cpt_info = null;
                        if (!empty($source_id) && !empty($post->post_type) && strpos($field_name, 'brf_field_') === 0) {
                            $array_key = $this->get_array_key_from_field_name($field_name, $source_id);
                            if ($array_key) {
                                $child_cpt_info = $this->get_child_cpt_info($source_id, $array_key, $post->post_type);
                            }
                        }
                        ?>
                        <div class="brf-field-item">
                            <div class="brf-field-label"><?php echo esc_html($field_name); ?>:</div>
                            <div class="brf-field-value">
                                <?php
                                // Check if value is JSON
                                if ($field_value !== null && $field_value !== '') {
                                    $decoded = json_decode($field_value, true);
                                    if (json_last_error() === JSON_ERROR_NONE && (is_array($decoded) || is_object($decoded))) {
                                        echo '<code>' . esc_html(json_encode($decoded, JSON_PRETTY_PRINT)) . '</code>';
                                    } else {
                                        echo esc_html($field_value);
                                    }
                                } else {
                                    echo '<em>Empty</em>';
                                }
                                ?>
                                <?php if ($child_cpt_info): ?>
                                    <?php
                                    // Count child posts for this parent
                                    $child_posts_count = get_posts([
                                        'post_type' => $child_cpt_info['slug'],
                                        'posts_per_page' => -1,
                                        'post_status' => 'any',
                                        'meta_query' => [
                                            [
                                                'key' => '_brf_parent_post_id',
                                                'value' => $post->ID,
                                                'compare' => '='
                                            ]
                                        ],
                                        'fields' => 'ids'
                                    ]);
                                    $count = is_array($child_posts_count) ? count($child_posts_count) : 0;

                                    // Create link to Child CPT list filtered by parent post ID
                                    $child_cpt_list_url = admin_url('edit.php?post_type=' . $child_cpt_info['slug'] . '&brf_parent_id=' . $post->ID);
                                    ?>
                                    <a href="<?php echo esc_url($child_cpt_list_url); ?>" class="brf-child-cpt-link">
                                        <span class="dashicons dashicons-external"></span>
                                        View <?php echo esc_html($child_cpt_info['label']); ?> (<?php echo esc_html($count); ?> <?php echo $count === 1 ? 'item' : 'items'; ?>)
                                    </a>
                                <?php endif; ?>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>

            <?php if (!empty($nested_arrays)): ?>
                <div class="brf-field-group">
                    <div class="brf-field-group-title">Nested Arrays</div>
                    <?php foreach ($nested_arrays as $array_name => $array_data): ?>
                        <?php
                        // Check if Child CPT exists for this nested array
                        $child_cpt_info = null;
                        if (!empty($source_id) && !empty($post->post_type)) {
                            $child_cpt_info = $this->get_child_cpt_info($source_id, $array_name, $post->post_type);
                        }
                        ?>
                        <div class="brf-field-item">
                            <div class="brf-field-label"><?php echo esc_html($array_name); ?>:</div>
                            <div class="brf-field-value">
                                <?php
                                if ($array_data !== null && $array_data !== '') {
                                    $decoded = json_decode($array_data, true);
                                    if ($decoded !== null) {
                                        echo '<div class="brf-json-viewer">' . esc_html(json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) . '</div>';
                                    } else {
                                        echo esc_html($array_data);
                                    }
                                } else {
                                    echo '<em>Empty</em>';
                                }
                                ?>
                                <?php if ($child_cpt_info): ?>
                                    <?php
                                    // Count child posts for this parent
                                    $child_posts_count = get_posts([
                                        'post_type' => $child_cpt_info['slug'],
                                        'posts_per_page' => -1,
                                        'post_status' => 'any',
                                        'meta_query' => [
                                            [
                                                'key' => '_brf_parent_post_id',
                                                'value' => $post->ID,
                                                'compare' => '='
                                            ]
                                        ],
                                        'fields' => 'ids'
                                    ]);
                                    $count = is_array($child_posts_count) ? count($child_posts_count) : 0;

                                    // Create link to Child CPT list filtered by parent post ID
                                    $child_cpt_list_url = admin_url('edit.php?post_type=' . $child_cpt_info['slug'] . '&brf_parent_id=' . $post->ID);
                                    ?>
                                    <a href="<?php echo esc_url($child_cpt_list_url); ?>" class="brf-child-cpt-link">
                                        <span class="dashicons dashicons-external"></span>
                                        View <?php echo esc_html($child_cpt_info['label']); ?> (<?php echo esc_html($count); ?> <?php echo $count === 1 ? 'item' : 'items'; ?>)
                                    </a>
                                <?php endif; ?>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>

            <?php if (!empty($raw_data)): ?>
                <div class="brf-field-group">
                    <div class="brf-field-group-title">Raw API Data</div>
                    <div class="brf-toggle-raw" onclick="this.nextElementSibling.classList.toggle('show'); this.textContent = this.nextElementSibling.classList.contains('show') ? 'Hide Raw Data' : 'Show Raw Data';">Show Raw Data</div>
                    <div class="brf-raw-data-container">
                        <?php
                        if ($raw_data !== null && $raw_data !== '') {
                            $decoded_raw = json_decode($raw_data, true);
                            if ($decoded_raw !== null) {
                                echo '<div class="brf-json-viewer">' . esc_html(json_encode($decoded_raw, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) . '</div>';
                            } else {
                                echo '<div class="brf-json-viewer">' . esc_html($raw_data) . '</div>';
                            }
                        } else {
                            echo '<div class="brf-json-viewer"><em>Empty</em></div>';
                        }
                        ?>
                    </div>
                </div>
            <?php endif; ?>

            <?php if (empty($api_fields) && empty($nested_arrays) && empty($raw_data)): ?>
                <p style="color: #666; font-style: italic;">No API data fields found for this post.</p>
            <?php endif; ?>
        </div>
        <script>
            (function() {
                // Move our metabox to the top of the normal column
                function moveMetaboxToTop() {
                    var metabox = document.getElementById('brf_api_fields');
                    var normalColumn = document.getElementById('normal-sortables');

                    if (metabox && normalColumn && metabox.parentNode !== normalColumn) {
                        normalColumn.insertBefore(metabox, normalColumn.firstChild);
                    } else if (metabox && normalColumn && metabox.parentNode === normalColumn && metabox !== normalColumn.firstChild) {
                        normalColumn.insertBefore(metabox, normalColumn.firstChild);
                    }
                }

                // Run immediately
                if (document.readyState === 'loading') {
                    document.addEventListener('DOMContentLoaded', moveMetaboxToTop);
                } else {
                    moveMetaboxToTop();
                }

                // Also run after a short delay to catch late-loading metaboxes
                setTimeout(moveMetaboxToTop, 100);
            })();
        </script>
<?php
    }

    /**
     * Delete CPTs and their data for a specific API item
     *
     * @param string $item_id The API item ID
     * @return array|WP_Error Stats array with deleted counts or WP_Error on failure
     */
    public function delete_cpts_for_item($item_id)
    {
        global $wpdb;

        $stats = [
            'cpts_deleted' => 0,
            'posts_deleted' => 0,
            'cpt_names' => []
        ];

        try {
            // Get the item to find its CPT slug
            wp_cache_delete('brf_external_api_loops', 'options');
            wp_cache_delete('alloptions', 'options');

            $items = get_option('brf_external_api_loops', []);
            $item = null;

            foreach ($items as $i) {
                $current_id = is_array($i) ? (isset($i['id']) ? $i['id'] : null) : (isset($i->id) ? $i->id : null);
                if ($current_id === $item_id) {
                    $item = $i;
                    break;
                }
            }

            if (!$item) {
                return new \WP_Error('item_not_found', 'API item not found', ['status' => 404]);
            }

            // Get CPT slug for this item
            $cpt_slug_value = $this->get_item_property($item, 'cptSettings.cptSlug', '');
            if (empty($cpt_slug_value)) {
                return $stats; // No CPT configured for this item
            }

            $cpt_slug = $this->get_cpt_slug($cpt_slug_value);
            $stats['cpt_names'][] = $cpt_slug;

            // Get all posts of this CPT type that belong to this item
            $posts = get_posts([
                'post_type' => $cpt_slug,
                'posts_per_page' => -1,
                'post_status' => 'any',
                'fields' => 'ids',
                'meta_query' => [
                    [
                        'key' => '_brf_api_source_id',
                        'value' => $item_id,
                        'compare' => '='
                    ]
                ]
            ]);

            // Delete each post (this will also trigger cascading deletes for child posts)
            foreach ($posts as $post_id) {
                $deleted = wp_delete_post($post_id, true); // true = force delete (skip trash)
                if ($deleted) {
                    $stats['posts_deleted']++;
                }
            }

            // Also delete child CPTs for this item
            // First, get all parent post IDs that belong to this item (before deletion)
            $parent_post_ids = get_posts([
                'post_type' => $cpt_slug,
                'posts_per_page' => -1,
                'post_status' => 'any',
                'fields' => 'ids',
                'meta_query' => [
                    [
                        'key' => '_brf_api_source_id',
                        'value' => $item_id,
                        'compare' => '='
                    ]
                ]
            ]);

            if (!empty($parent_post_ids)) {
                $childCpts = $this->get_item_property($item, 'cptSettings.childCpts', []);
                if (!empty($childCpts) && is_array($childCpts)) {
                    foreach ($childCpts as $child_config) {
                        $array_key = $this->get_item_property($child_config, 'arrayKey', '');
                        if (!empty($array_key)) {
                            // Generate child CPT slug (same logic as in register_nested_array_cpts)
                            $child_slug_base = $cpt_slug . '-' . sanitize_title($array_key);
                            if (strlen($child_slug_base) > 20) {
                                $child_slug_base = substr($child_slug_base, 0, 20);
                            }
                            $child_cpt_slug = $child_slug_base;

                            // Get all child posts for these parents
                            $child_posts = get_posts([
                                'post_type' => $child_cpt_slug,
                                'posts_per_page' => -1,
                                'post_status' => 'any',
                                'fields' => 'ids',
                                'meta_query' => [
                                    [
                                        'key' => '_brf_parent_post_id',
                                        'value' => $parent_post_ids,
                                        'compare' => 'IN'
                                    ]
                                ]
                            ]);

                            // Delete child posts
                            foreach ($child_posts as $child_post_id) {
                                $deleted = wp_delete_post($child_post_id, true);
                                if ($deleted) {
                                    $stats['posts_deleted']++;
                                }
                            }

                            // Track child CPT
                            if (!in_array($child_cpt_slug, $stats['cpt_names'])) {
                                $stats['cpt_names'][] = $child_cpt_slug;
                            }
                        }
                    }
                }
            }

            // Note: We don't unregister the CPT type itself as it might be used by other items
            // The CPT will remain registered but empty

            // Clear rewrite rules to remove CPT slugs from permalinks
            flush_rewrite_rules(false);

            return $stats;
        } catch (\Exception $e) {
            return new \WP_Error('delete_failed', $e->getMessage(), ['status' => 500]);
        }
    }
}
