<?php

namespace Miruni;

use Miruni\Breakdance\Miruni_Breakdance_Service;
use Miruni\Elementor\Miruni_Elementor_Service;
use Miruni\Miruni_Post_Meta_Utils;
use Miruni\Miruni_Preview_Meta_Keys;
use Miruni\BlockWidget\Miruni_Block_Widget_Manager; // Added import
use Miruni\Miruni_Theme_JSON_Manager; // Added import

use Exception;
use WP_Post;
use WP_Error; // Added for type hinting

class Miruni_Draft_Publisher
{

    /**
     * Clean up all temporary data associated with a draft
     * 
     * @param int $draft_id ID of the draft post
     * @return void
     */
    public static function cleanup_draft_data(int $draft_id): void
    {
        // Clean up post meta using enum constants
        $meta_keys = [
            Miruni_Preview_Meta_Keys::OTHER_POST_TITLES,
            Miruni_Preview_Meta_Keys::OTHER_POST_CONTENT,
            Miruni_Preview_Meta_Keys::OTHER_POST_EXCERPTS,
            Miruni_Preview_Meta_Keys::THEME_MODS,
            Miruni_Preview_Meta_Keys::WP_OPTIONS,
            Miruni_Preview_Meta_Keys::ELEMENTOR_JSON,
            Miruni_Preview_Meta_Keys::BREAKDANCE_DATA,
            Miruni_Preview_Meta_Keys::BLOCK_TEMPLATE,
            Miruni_Preview_Meta_Keys::MENU_ITEM_NAME,
            Miruni_Preview_Meta_Keys::MENU_ITEM_PARENT,
            Miruni_Preview_Meta_Keys::MENU_ITEM_URL,
            Miruni_Preview_Meta_Keys::MENU_ITEM_ORDER,
            Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECT_IDS,
            Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECTS,
            Miruni_Preview_Meta_Keys::BLOCK_WIDGET,
            Miruni_Preview_Meta_Keys::THEME_JSON
        ];

        foreach ($meta_keys as $meta_key) {
            delete_post_meta($draft_id, $meta_key);
        }

        // Clean up transients
        $transient_keys = [
            'miruni_preview_titles_' . $draft_id,
            'miruni_preview_content_' . $draft_id,
            'miruni_preview_theme_mods_' . $draft_id
        ];

        foreach ($transient_keys as $transient_key) {
            delete_transient($transient_key);
        }

        # delete post
        $post = get_post($draft_id);

        if ($post instanceof WP_Post && $post->post_status === 'draft') {
            wp_delete_post($draft_id, true);
        }

        do_action('miruni_error', new Exception("Cleaned up temporary data for draft ID: {$draft_id}"), [
            'context' => [
                'draft_id' => $draft_id,
                'meta_keys' => $meta_keys,
                'transient_keys' => $transient_keys
            ],
            'operation' => 'Cleanup Draft Data',
            'user_id' => get_current_user_id()
        ]);
    }

    /**
     * Create a backup of block widgets and custom HTML widgets
     * 
     * @param int $draft_id ID of the draft post
     * @return array<mixed> Backup data
     */
    private static function create_block_widget_backups(int $draft_id): array
    {
        $backup_data = [];

        // Get the existing widget data from the draft
        $widget_updates = get_post_meta($draft_id, Miruni_Preview_Meta_Keys::BLOCK_WIDGET, true);
        if (!is_array($widget_updates) || empty($widget_updates)) {
            return $backup_data;
        }
        $backup_block_widgets = [];
        $backup_custom_html_widgets = [];
        foreach ($widget_updates as $widget_id => $widget_data) {
            if (!is_string($widget_id)) {
                continue;
            }
            if (str_contains($widget_id, 'block-') && empty($backup_block_widgets)) {
                $backup_block_widgets = get_option("widget_block", []);
            } else if (str_contains($widget_id, 'custom_html') && empty($backup_custom_html_widgets)) {
                $backup_custom_html_widgets = get_option("widget_custom_html", []);
            }

        }
        if (!empty($backup_block_widgets)) {
            $backup_data['block_widgets'] = $backup_block_widgets;
        }
        if (!empty($backup_custom_html_widgets)) {
            $backup_data['custom_html_widgets'] = $backup_custom_html_widgets;
        }
        return $backup_data;
    }

    /**
     * Store original state when publishing to enable later reversion
     * 
     * @param int $draft_id ID of the draft being published
     * @param int $parent_id ID of the parent post being updated
     * @return bool Success status
     */
    private static function create_reversion_backup(int $draft_id, int $parent_id): bool
    {
        try {
            $parent_post = get_post($parent_id);

            // Create backup object (excluding Breakdance data initially)
            $backup = [
                'timestamp' => current_time('mysql'),
                'parent_post' => [
                    'ID' => $parent_id,
                    'post_title' => $parent_post instanceof WP_Post ? $parent_post->post_title : null,
                    'post_content' => $parent_post instanceof WP_Post ? $parent_post->post_content : null,
                ],
                'other_titles' => [],
                'other_content' => [],
                'other_excerpts' => [],
                'theme_mods' => [],
                'wp_options' => [],
                'block_template' => [],
                'menu_item_names' => [],
                'menu_item_parents' => [],
                'menu_item_urls' => [],
                'menu_item_object_ids' => [],
                'menu_item_objects' => [],
                'menu_item_orders' => [],
                'theme_json' => [],
            ];

            // Backup parent post's Elementor data if it exists (remains in main backup)
            $elementor_data = get_post_meta($parent_id, '_elementor_data', true);
            if ($elementor_data) {
                $backup['parent_post']['_elementor_data'] = $elementor_data;
            }

            $widget_backups = self::create_block_widget_backups($draft_id);
            $backup = array_merge($backup, $widget_backups);

            // Backup parent post's Breakdance data separately
            $breakdance_backup_success = false;
            $breakdance_data_array = Miruni_Breakdance_Service::get_page_data($parent_id);
            // @phpstan-ignore function.alreadyNarrowedType
            if ($breakdance_data_array !== false && is_array($breakdance_data_array)) {
                error_log("Miruni Draft Publisher: Found Breakdance data array for parent ID $parent_id.");
                $breakdance_json_string = json_encode($breakdance_data_array, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
                if (json_last_error() === JSON_ERROR_NONE) {
                    // Manually apply wp_slash before saving
                    $slashed_json_string = wp_slash($breakdance_json_string);
                    // Store the manually slashed JSON string in its own meta field
                    if (update_post_meta($parent_id, '_miruni_reversion_backup_bd_data', $slashed_json_string)) {
                        $breakdance_backup_success = true;
                        error_log("Miruni Draft Publisher: Stored manually slashed Breakdance data JSON string separately in backup meta for parent ID $parent_id.");
                    }
                } else {
                    error_log("Miruni Draft Publisher: Failed to JSON encode Breakdance data for separate backup for parent ID $parent_id. Error: " . json_last_error_msg());
                }
            } else {
                error_log("Miruni Draft Publisher: No valid Breakdance data array found to backup separately for parent ID $parent_id.");
                // If no data exists, ensure any old separate backup is removed
                delete_post_meta($parent_id, '_miruni_reversion_backup_bd_data');
                $breakdance_backup_success = true; // Consider success if no data needed backing up
            }

            // If Breakdance backup failed (and data existed), consider the whole backup failed
            if (!$breakdance_backup_success && $breakdance_data_array !== false) {
                error_log("Miruni Draft Publisher: Critical failure backing up Breakdance data separately. Aborting backup.");

                return false;
            }

            // Store original titles and content of other posts being modified
            $post_changes = [
                Miruni_Preview_Meta_Keys::OTHER_POST_TITLES => ['field' => 'post_title', 'backup_key' => 'other_titles'],
                Miruni_Preview_Meta_Keys::OTHER_POST_CONTENT => ['field' => 'post_content', 'backup_key' => 'other_content'],
                Miruni_Preview_Meta_Keys::OTHER_POST_EXCERPTS => ['field' => 'post_excerpt', 'backup_key' => 'other_excerpts'],
                Miruni_Preview_Meta_Keys::THEME_MODS => ['field' => 'option_value', 'backup_key' => 'theme_mods'],
                Miruni_Preview_Meta_Keys::MENU_ITEM_NAME => ['field' => 'menu_item_name', 'backup_key' => 'menu_item_names'],
                Miruni_Preview_Meta_Keys::MENU_ITEM_PARENT => ['field' => 'menu_item_parent', 'backup_key' => 'menu_item_parents'],
                Miruni_Preview_Meta_Keys::MENU_ITEM_URL => ['field' => 'menu_item_url', 'backup_key' => 'menu_item_urls'],
                Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECT_IDS => ['field' => 'menu_item_object_id', 'backup_key' => 'menu_item_object_ids'],
                Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECTS => ['field' => 'menu_item_object', 'backup_key' => 'menu_item_objects'],
                Miruni_Preview_Meta_Keys::MENU_ITEM_ORDER => ['field' => 'menu_item_menu_order', 'backup_key' => 'menu_item_orders'],
                Miruni_Preview_Meta_Keys::WP_OPTIONS => ['field' => 'option_value', 'backup_key' => 'wp_options'],
                Miruni_Preview_Meta_Keys::BLOCK_TEMPLATE => ['field' => 'block_template', 'backup_key' => 'block_template'],
                Miruni_Preview_Meta_Keys::BREAKDANCE_DATA => ['field' => 'other_breakdance_meta', 'backup_key' => 'breakdance_data'],
                Miruni_Preview_Meta_Keys::THEME_JSON => ['field' => 'theme_json_data', 'backup_key' => 'theme_json'],
            ];

            foreach ($post_changes as $meta_key => $config) {
                $changes = get_post_meta($draft_id, $meta_key, true);
                if ($changes) {
                    error_log("Miruni Draft Publisher: Found changes for meta key $meta_key in draft ID $draft_id.");
                }


                $transient_changes = get_transient('miruni_preview_' . $meta_key . '_' . $draft_id);

                $changes = is_array($changes) ? $changes : [];
                $transient_changes = is_array($transient_changes) ? $transient_changes : [];

                // Merge the changes properly, preserving keys

                foreach ($transient_changes as $key => $value) {
                    $changes[$key] = $value;
                }

                foreach ($changes as $key => $new_value) {
                    error_log("Miruni Draft Publisher: Processing change for key $key in meta key $meta_key.");
                    $value = null;
                    if ($config['field'] === 'option_value') {
                        $value = get_theme_mod($key);
                    } else if ($config['field'] === "block_template") {
                        $blocks = get_block_templates([
                            'slug_in' => $key,
                        ]);
                        $block = null;
                        if (count($blocks) > 0) {
                            $block = $blocks[0];
                        }

                        if ($block) {
                            $value = $block->content;
                        }
                    } else if ($config['field'] === 'other_breakdance_meta') {
                        $breakdance_data = Miruni_Breakdance_Service::get_page_data($key);
                        // @phpstan-ignore function.alreadyNarrowedType
                        if ($breakdance_data !== false && is_array($breakdance_data)) {
                            $value = wp_slash(json_encode($breakdance_data));
                        }
                    } else if ($config['field'] === 'menu_item_name') {
                        $menu_item_post = get_post($key);
                        if ($menu_item_post instanceof WP_Post) {
                            $value = $menu_item_post->post_title;
                        }
                    } else if ($config['field'] === 'menu_item_menu_order') {
                        error_log("Miruni Draft Publisher: Found changes for menu item order in draft ID $draft_id.");
                        $menu_item_post = get_post($key);
                        error_log("menu_item_post type " . gettype($menu_item_post));
                        if ($menu_item_post instanceof WP_Post) {
                            $value = $menu_item_post->menu_order;
                        }
                    } else if ($config['field'] === 'theme_json_data') {
                        // For theme.json, we backup the current theme.json data
                        $theme_json_data = Miruni_Theme_JSON_Manager::create_theme_json_backup();
                        error_log("Miruni Draft Publisher: Found theme_json_data for key $key in draft ID $draft_id.");
                        error_log("theme_json_data type " . gettype($theme_json_data));
                        error_log("theme_json_data value " . json_encode($theme_json_data));
                        if ($theme_json_data !== null) {
                            $value = $theme_json_data;
                        }
                    } else {
                        $original_post = get_post($key);
                        if ($original_post instanceof WP_Post && isset($original_post->{$config['field']})) {
                            $value = $original_post->{$config['field']};
                        }
                    }

                    if ($value) {
                        error_log("storing backup for $key with value $value");
                        $backup[$config['backup_key']][$key] = $value;
                    }
                }
            }

            // Store original wp_option values
            $wp_option_changes = get_post_meta($draft_id, Miruni_Preview_Meta_Keys::WP_OPTIONS, true);

            if (is_array($wp_option_changes)) {
                foreach ($wp_option_changes as $option_name => $new_value) {
                    // Store the original option value
                    $original_value = get_option($option_name);

                    $backup['wp_options'][$option_name] = $original_value;

                    do_action('miruni_error', new Exception("Backing up WP option: {$option_name}"), [
                        'context' => [
                            'draft_id' => $draft_id,
                            'parent_id' => $parent_id,
                            'option_name' => $option_name,
                            'original_value' => $original_value
                        ],
                        'operation' => 'Backup WordPress Option',
                        'user_id' => get_current_user_id()
                    ]);
                }
            }



            do_action('miruni_error', new Exception("Created reversion backup"), [
                'context' => [
                    'draft_id' => $draft_id,
                    'parent_id' => $parent_id,
                    'backup' => $backup
                ],
                'operation' => 'Create Reversion Backup',
                'user_id' => get_current_user_id()
            ]);

            // Store the main backup (without Breakdance data)
            $update_result = update_post_meta($parent_id, '_miruni_reversion_backup', $backup);
            if (!$update_result) {
                error_log("Miruni Draft Publisher: Failed to update main reversion backup meta for parent ID $parent_id.");
                // Clean up the separate BD backup if the main one failed
                delete_post_meta($parent_id, '_miruni_reversion_backup_bd_data');
                return false;
            }

            // Set an expiration for this backup (optional, for cleanup)
            set_transient('miruni_reversion_' . $parent_id, time(), 24 * 60 * 60 * 7);

            return true;
        } catch (Exception $e) {
            $error_message = $e->getMessage();
            $error_file = $e->getFile();
            $error_line = $e->getLine();
            $error_trace = $e->getTraceAsString();

            do_action('miruni_error', $e, [
                'context' => [
                    'draft_id' => $draft_id,
                    'parent_id' => $parent_id,
                    'error_message' => $error_message,
                    'error_file' => $error_file,
                    'error_line' => $error_line
                ],
                'operation' => 'Failed to create reversion backup',
                'user_id' => get_current_user_id()
            ]);

            // Ensure cleanup if exception occurs
            delete_post_meta($parent_id, '_miruni_reversion_backup_bd_data');
            delete_post_meta($parent_id, '_miruni_reversion_backup');
            return false;
        }
    }

    /**
     * Reverts the main post's title and content.
     *
     * @param int $post_id The ID of the post being reverted.
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_main_post(int $post_id, array $backup, array &$reverted_items): void
    {
        if (isset($backup['parent_post'])) {
            $update_result = wp_update_post([
                'ID' => $post_id,
                'post_content' => $backup['parent_post']['post_content'],
                'post_title' => $backup['parent_post']['post_title']
            ], true);

            if (!is_wp_error($update_result)) {
                $reverted_items[] = 'main_post';
            }
        }
    }

    /**
     * Reverts Elementor data for the post.
     *
     * @param int $post_id The ID of the post being reverted.
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_elementor_data(int $post_id, array $backup, array &$reverted_items): void
    {
        if (isset($backup['parent_post']['_elementor_data'])) {
            update_post_meta($post_id, '_elementor_data', $backup['parent_post']['_elementor_data']);
            $reverted_items[] = 'elementor_data';
        }
        Miruni_Elementor_Service::delete_element_cache($post_id);
    }

    /**
     * Reverts Breakdance data for the main post.
     * Breakdance data for referenced posts (eg nav bar) is updated in _revert_other_breakdance_data.
     *
     * @param int $post_id The ID of the post being reverted.
     * @param string|null $breakdance_backup_slashed_json The slashed JSON string backup for Breakdance.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_breakdance_data(int $post_id, ?string $breakdance_backup_slashed_json, array &$reverted_items): void
    {
        $breakdance_action_taken = false;
        $original_breakdance_data_array = null;

        // Check if the separate backup data exists and is a string
        // @phpstan-ignore function.alreadyNarrowedType
        if (!empty($breakdance_backup_slashed_json) && is_string($breakdance_backup_slashed_json)) {
            error_log("Miruni Draft Publisher: Found separate Breakdance backup slashed JSON string for post ID $post_id.");
            // Manually apply stripslashes to reverse the wp_slash applied during save
            $breakdance_backup_json = $breakdance_backup_slashed_json;
            // Attempt to decode the unslashed JSON string
            $decoded_array = json_decode($breakdance_backup_json, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_array)) {
                $original_breakdance_data_array = $decoded_array;
                error_log("Miruni Draft Publisher: Successfully decoded manually unslashed separate Breakdance backup JSON for post ID $post_id.");
            } else {
                error_log("Miruni Draft Publisher: Failed to decode manually unslashed separate Breakdance backup JSON for post ID $post_id. JSON Error: " . json_last_error_msg());
                // Log the string that failed decoding (potentially truncated for logs)
                error_log("Failing JSON string (potentially truncated): " . substr($breakdance_backup_json, 0, 500));
            }
        } elseif (empty($breakdance_backup_slashed_json)) {
            error_log("Miruni Draft Publisher: No separate Breakdance backup data found (meta key _miruni_reversion_backup_bd_data empty or missing) for post ID $post_id.");
            // This implies the original post had no BD data, so we should ensure current BD data is cleared.
            $original_breakdance_data_array = []; // Set to empty array to trigger clearing
        } else {
            error_log("Miruni Draft Publisher: Separate Breakdance backup data for post ID $post_id was not a string or empty. Type: " . gettype($breakdance_backup_slashed_json));
        }

        // Attempt to update ONLY if we have a valid array (either decoded or explicitly empty)
        if (is_array($original_breakdance_data_array) && !empty($original_breakdance_data_array)) {
            $update_success = Miruni_Breakdance_Service::update_page_data($post_id, $original_breakdance_data_array);
            if ($update_success) {
                $reverted_items[] = 'breakdance_data';
                $breakdance_action_taken = true;
            } else {
                error_log("Miruni Draft Publisher: Failed to revert/clear Breakdance data via service for post ID $post_id. Current data remains unchanged.");
            }
        } else {
            // Invalid backup data (not string, not empty, failed decode). Skip restoration.
            error_log("Miruni Draft Publisher: Skipping Breakdance data restoration for post ID $post_id due to invalid separate backup data.");
        }

        // Only render if the restoration action was successfully performed
        if ($breakdance_action_taken) {
            Miruni_Breakdance_Service::render_breakdance_page(post_id: $post_id);
        }
    }

    /**
     * Reverts titles of other posts.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_other_post_titles(array $backup, array &$reverted_items): void
    {
        if (isset($backup['other_titles']) && is_array($backup['other_titles'])) {
            foreach ($backup['other_titles'] as $other_post_id => $original_title) {
                $result = wp_update_post([
                    'ID' => $other_post_id,
                    'post_title' => $original_title
                ]);
                if ($result) {
                    $reverted_items[] = 'post_title_' . $other_post_id;
                }
            }
        }
    }

    /**
     * Reverts content of other posts.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_other_post_content(array $backup, array &$reverted_items): void
    {
        if (isset($backup['other_content']) && is_array($backup['other_content'])) {
            foreach ($backup['other_content'] as $other_post_id => $original_content) {
                $result = wp_update_post([
                    'ID' => $other_post_id,
                    'post_content' => $original_content
                ]);
                if ($result) {
                    $reverted_items[] = 'post_content_' . $other_post_id;
                }
            }
        }
    }

    /**
     * Reverts excerpts of other posts.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_other_post_excerpts(array $backup, array &$reverted_items): void
    {
        if (isset($backup['other_excerpts']) && is_array($backup['other_excerpts'])) {
            foreach ($backup['other_excerpts'] as $other_post_id => $original_excerpt) {
                $result = wp_update_post([
                    'ID' => $other_post_id,
                    'post_excerpt' => $original_excerpt
                ]);
                if ($result) {
                    $reverted_items[] = 'post_excerpt_' . $other_post_id;
                }
            }
        }
    }

    /**
     * Reverts theme modifications.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_theme_mods(array $backup, array &$reverted_items): void
    {
        if (isset($backup['theme_mods']) && is_array($backup['theme_mods'])) {
            foreach ($backup['theme_mods'] as $mod_name => $original_value) {
                set_theme_mod($mod_name, $original_value);
                wp_cache_delete('theme_mods_' . get_stylesheet(), 'options');
                $reverted_items[] = 'theme_mod_' . $mod_name;
            }
        }
    }

    /**
     * Reverts WordPress options.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_wp_options(array $backup, array &$reverted_items): void
    {
        if (isset($backup['wp_options']) && is_array($backup['wp_options'])) {
            foreach ($backup['wp_options'] as $option_name => $original_value) {
                update_option($option_name, $original_value);
                wp_cache_delete($option_name, 'options');
                $reverted_items[] = 'wp_option_' . $option_name;
            }
        }
    }

    /**
     * Reverts menu item names (post titles).
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_menu_item_values(array $backup, array &$reverted_items): void
    {
        error_log("Reverting menu item values - type of backup['menu_item_names'] - " . gettype($backup['menu_item_names']));
        if (isset($backup['menu_item_names']) && is_array($backup['menu_item_names'])) {
            error_log("Reverting menu item names");
            foreach ($backup['menu_item_names'] as $menu_item_id => $original_name) {
                error_log("Reverting menu item name for ID $menu_item_id to $original_name");
                $result = wp_update_post(['ID' => $menu_item_id, 'post_title' => $original_name]);
                if ($result) {
                    $reverted_items[] = 'menu_item_name_' . $menu_item_id;
                }
            }
        }


        if (isset($backup['menu_item_orders']) && is_array($backup['menu_item_orders'])) {
            error_log("Reverting menu item orders");
            foreach ($backup['menu_item_orders'] as $menu_item_id => $original_order) {
                $result = wp_update_post(['ID' => $menu_item_id, 'menu_order' => $original_order]);
                $reverted_items[] = 'menu_item_order_' . $menu_item_id;
            }
        }
    }

    /**
     * Reverts other menu item meta fields.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_menu_item_meta(array $backup, array &$reverted_items): void
    {
        $menu_meta_to_revert = [
            'menu_item_parents' => '_menu_item_menu_item_parent',
            'menu_item_urls' => '_menu_item_url',
            'menu_item_object_ids' => '_menu_item_object_id',
            'menu_item_objects' => '_menu_item_object',
        ];

        foreach ($menu_meta_to_revert as $backup_key => $target_meta_key) {
            if (isset($backup[$backup_key]) && is_array($backup[$backup_key])) {
                foreach ($backup[$backup_key] as $menu_item_id => $original_value) {
                    update_post_meta($menu_item_id, $target_meta_key, $original_value);
                    $reverted_items[] = $backup_key . '_' . $menu_item_id;
                }
            }
        }
    }

    /**
     * Reverts block templates.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_block_templates(array $backup, array &$reverted_items): void
    {
        if (isset($backup['block_template']) && is_array($backup['block_template'])) {
            foreach ($backup['block_template'] as $template_slug => $original_content) {
                $current_theme = get_stylesheet();
                $template_type = 'wp_template';

                if (strpos($template_slug, 'template-parts/') === 0) {
                    $template_type = 'wp_template_part';
                    $template_slug = str_replace('template-parts/', '', $template_slug);
                }

                $query_args = [
                    'post_type' => $template_type,
                    'post_status' => 'publish',
                    'name' => $template_slug,
                    'posts_per_page' => 1,
                    'no_found_rows' => true,
                    'tax_query' => [
                        [
                            'taxonomy' => 'wp_theme',
                            'field' => 'name',
                            'terms' => $current_theme
                        ]
                    ],
                ];

                $template_query = new \WP_Query($query_args);

                if ($template_query->have_posts()) {
                    $template_post = $template_query->posts[0];
                    wp_update_post([
                        'ID' => $template_post instanceof WP_Post ? $template_post->ID : $template_post,
                        'post_content' => $original_content,
                    ]);
                    $reverted_items[] = 'block_template_' . $template_slug;
                }
            }
        }
    }

    /**
     * Reverts block widget options (widget_block and widget_custom_html).
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_block_widgets(array $backup, array &$reverted_items): void
    {
        if (isset($backup['block_widgets']) && is_array($backup['block_widgets'])) {
            if (update_option('widget_block', $backup['block_widgets'])) {
                $reverted_items[] = 'widget_block_option';
            }
        }
        if (isset($backup['custom_html_widgets']) && is_array($backup['custom_html_widgets'])) {
            if (update_option('widget_custom_html', $backup['custom_html_widgets'])) {
                $reverted_items[] = 'widget_custom_html_option';
            }
        }
    }

    /**
     * Reverts theme.json data.
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_theme_json(array $backup, array &$reverted_items): void
    {
        if (isset($backup['theme_json']) && is_array($backup['theme_json'])) {
            // The backup contains the original theme.json data for each key that was changed
            foreach ($backup['theme_json'] as $key => $original_data) {
                if (is_array($original_data)) {
                    // Restore the original theme.json data
                    $success = Miruni_Theme_JSON_Manager::restore_theme_json_from_backup($original_data);
                    if ($success) {
                        $reverted_items[] = 'theme_json';
                        // Clear theme-related caches
                        if (function_exists('wp_clean_themes_cache')) {
                            wp_clean_themes_cache();
                        }
                        if (class_exists('WP_Theme_JSON_Resolver')) {
                            wp_cache_delete('theme_json', 'themes');
                        }
                    } else {
                        error_log("Miruni Draft Publisher: Failed to revert theme.json data");
                    }
                    break; // Since there should only be one theme.json entry
                }
            }
        }
    }

    /**
     * Cleans up reversion backup data (meta and transients).
     *
     * @param int $post_id The ID of the post whose reversion data should be cleaned.
     * @return void
     */
    private static function _cleanup_reversion_data(int $post_id): void
    {
        delete_post_meta($post_id, '_miruni_reversion_backup');
        delete_post_meta($post_id, '_miruni_reversion_backup_bd_data'); // Delete the separate BD backup
        delete_transient('miruni_reversion_' . $post_id);
    }

    /**
     * Revert published changes back to original state
     * 
     * @param int $post_id ID of the post to revert
     * @return array{
     * success: bool,
     * message: string,
     * reverted_items: string[]
     * }
     */
    public static function revert_published_changes(int $post_id): array
    {
        try {
            // Clear relevant caches before fetching meta
            wp_cache_delete($post_id, 'post_meta');
            wp_cache_delete('theme_mods_' . get_stylesheet(), 'options');

            // Get backup data
            $backup = get_post_meta(absint($post_id), '_miruni_reversion_backup', true);
            error_log("found backup of type - " . gettype($backup));
            $breakdance_backup_slashed_json = get_post_meta(absint($post_id), '_miruni_reversion_backup_bd_data', true);

            if (!is_array($backup) || empty($backup)) {
                error_log("Miruni Draft Publisher: No backup data found for post ID $post_id.");
                self::_cleanup_reversion_data($post_id); // Clean up potentially orphaned data
                return [
                    'success' => false,
                    'message' => 'No backup found for this post',
                    'reverted_items' => []
                ];
            }

            $reverted_items = [];

            // Revert various data types using helper methods
            self::_revert_main_post($post_id, $backup, $reverted_items);
            self::_revert_elementor_data($post_id, $backup, $reverted_items);
            self::_revert_breakdance_data($post_id, $breakdance_backup_slashed_json, $reverted_items);
            self::_revert_other_post_titles($backup, $reverted_items);
            self::_revert_other_post_content($backup, $reverted_items);
            self::_revert_other_post_excerpts($backup, $reverted_items);
            self::_revert_theme_mods($backup, $reverted_items);
            self::_revert_wp_options($backup, $reverted_items);
            self::_revert_menu_item_values($backup, $reverted_items);
            self::_revert_menu_item_meta($backup, $reverted_items);
            self::_revert_block_templates($backup, $reverted_items);
            self::_revert_other_breakdance_data($backup, $reverted_items);
            self::_revert_block_widgets($backup, $reverted_items); // Added call to revert block widgets
            self::_revert_theme_json($backup, $reverted_items); // Added call to revert theme.json

            // Remove the backups after successful reversion
            self::_cleanup_reversion_data($post_id);

            // Clear general caches after all reversions
            self::_clear_caches();

            return [
                'success' => true,
                'message' => 'Changes reverted successfully',
                'reverted_items' => $reverted_items
            ];
        } catch (Exception $e) {
            do_action('miruni_error', $e, [
                'context' => [
                    'post_id' => $post_id
                ],
                'operation' => 'Failed to revert published changes',
                'user_id' => get_current_user_id()
            ]);
            return [
                'success' => false,
                'message' => 'Error reverting changes: ' . $e->getMessage(),
                'reverted_items' => []
            ];
        }
    }

    /**
     * Updates the main parent post with draft content, title, and excerpt.
     *
     * @param int $parent_id The ID of the parent post.
     * @param WP_Post $draft The draft post object.
     * @throws Exception If the update fails.
     * @return void
     */
    private static function _update_parent_post(int $parent_id, WP_Post $draft): void
    {
        // If the parent ID is a placeholder, there is no actual post to update
        if ($parent_id === Miruni_Post_ID_Placeholder::LATEST_POSTS) {
            return;
        }

        // Run merge syntax processing for publish on string fields
        // @phpstan-ignore function.alreadyNarrowedType
        $post_content = is_string($draft->post_content) ? \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($draft->post_content) : $draft->post_content;
        // @phpstan-ignore function.alreadyNarrowedType
        $post_title = is_string($draft->post_title) ? \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($draft->post_title) : $draft->post_title;
        // @phpstan-ignore function.alreadyNarrowedType
        $post_excerpt = is_string($draft->post_excerpt) ? \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($draft->post_excerpt) : $draft->post_excerpt;

        $update_result = wp_update_post([
            'ID' => $parent_id,
            'post_content' => $post_content,
            'post_title' => $post_title,
            'post_excerpt' => $post_excerpt
        ], true);

        if (is_wp_error($update_result)) {
            do_action('miruni_plugin_error', $update_result, [
                'draft_id' => $draft->ID,
                'parent_id' => $parent_id,
                'update_result' => $update_result,
                'user_id' => get_current_user_id()
            ]);
            throw new Exception(esc_html($update_result->get_error_message()));
        }
    }

    /**
     * Updates Breakdance data for the parent post.
     * Breakdance data for referenced posts (eg nav bar) is updated in _update_other_breakdance_data.
     *
     * @param int $parent_id The ID of the parent post.
     * @param int $draft_id The ID of the draft post.
     * @return void
     */
    private static function _update_breakdance_data(int $parent_id, int $draft_id): void
    {
        // Get Breakdance data from the draft using the service (returns array or false)
        $breakdance_data_array = Miruni_Breakdance_Service::get_page_data($draft_id);
        error_log("Type of breakdance_data_array from draft: " . gettype($breakdance_data_array));

        // @phpstan-ignore function.alreadyNarrowedType
        if ($breakdance_data_array !== false && is_array($breakdance_data_array)) {
            // Update the parent post using the service
            $update_success = Miruni_Breakdance_Service::update_page_data($parent_id, $breakdance_data_array);

            if ($update_success) {
                // Render/regenerate cache after successful update
                Miruni_Breakdance_Service::render_breakdance_page(post_id: $parent_id);
                error_log("Miruni Draft Publisher: Successfully updated and rendered Breakdance data for parent ID $parent_id from draft ID $draft_id.");
            } else {
                error_log("Miruni Draft Publisher: Failed to update Breakdance data via service for parent ID $parent_id from draft ID $draft_id.");
            }
        }
    }

    private static function _apply_other_breakdance_data(int $draft_id): void
    {
        $other_breakdance_data = get_post_meta($draft_id, Miruni_Preview_Meta_Keys::BREAKDANCE_DATA, true);
        if (is_array($other_breakdance_data)) {
            foreach ($other_breakdance_data as $post_id => $data) {
                // Update Breakdance data for each referenced post
                $update_success = Miruni_Breakdance_Service::update_page_data($post_id, $data);
                if ($update_success) {
                    Miruni_Breakdance_Service::render_breakdance_page(post_id: $post_id);
                }
            }
        }
    }

    /**
     * Reverts Breakdance data for other posts (not the main post).
     *
     * @param array<mixed> $backup The backup data array.
     * @param array<string> &$reverted_items Array to track reverted items.
     * @return void
     */
    private static function _revert_other_breakdance_data(array $backup, array &$reverted_items): void
    {
        if (isset($backup['breakdance_data']) && is_array($backup['breakdance_data'])) {
            foreach ($backup['breakdance_data'] as $post_id => $original_data) {
                // Ensure original_data is a string before potentially stripping slashes
                if (!is_string($original_data)) {
                    error_log("Miruni Draft Publisher: Skipping reversion for post ID $post_id - original data is not a string. Type: " . gettype($original_data));
                    continue;
                }

                error_log("Miruni Draft Publisher: Reverting Other Breakdance data for post ID $post_id. - type: " . gettype($original_data));
                error_log("Miruni Draft Publisher: Original data: " . $original_data);
                // Log before stripslashes to see if it looks different
                // error_log("Original data before stripslashes: " . $original_data); // Optional: uncomment for deep debugging

                // Apply stripslashes just in case WP meta handling didn't fully unslash the JSON string within the serialized array
                $unslashed_original_data = $original_data;

                // Log after stripslashes to confirm
                // error_log("Original data after stripslashes: " . $unslashed_original_data); // Optional: uncomment for deep debugging

                // Pass the potentially unslashed data
                $update_success = Miruni_Breakdance_Service::update_page_data($post_id, $unslashed_original_data);

                // Log boolean success/failure correctly
                error_log("Update success for post ID $post_id: " . ($update_success ? 'true' : 'false'));
                if ($update_success) {
                    // Re-enable rendering after successful update
                    Miruni_Breakdance_Service::render_breakdance_page(post_id: $post_id);
                    $reverted_items[] = 'breakdance_data_' . $post_id;
                } else {
                    error_log("Miruni Draft Publisher: Failed to update Breakdance data during reversion for post ID $post_id.");
                }
            }
        }
    }

    /**
     * Updates page builder data (Elementor, Breakdance) for the parent post.
     *
     * @param int $parent_id The ID of the parent post.
     * @param int $draft_id The ID of the draft post.
     * @return void
     */
    private static function _update_builder_data(int $parent_id, int $draft_id): void
    {
        // Update Breakdance data if applicable
        self::_update_breakdance_data($parent_id, $draft_id);

        // Update Elementor data
        $elementor_data = get_post_meta($draft_id, '_elementor_data', true);
        if (!empty($elementor_data)) {
            error_log("Miruni Draft Publisher: Found Elementor data for draft ID $draft_id. Type: " . gettype($elementor_data));
            $str_elementor_data = is_array($elementor_data) ? json_encode($elementor_data) : $elementor_data;
            $merged_elementor_data = \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($str_elementor_data);
            Miruni_Elementor_Service::update_page_data($parent_id, $merged_elementor_data);
            // Clear Elementor cache if data was updated
            Miruni_Elementor_Service::delete_element_cache($parent_id);
        }
    }

    /**
     * Applies changes to other posts (titles, content, excerpts).
     *
     * @param int $draft_id The ID of the draft post.
     * @return array{updated_titles: array<int, string>, updated_content: array<int>, updated_excerpts: array<int>}
     */
    private static function _apply_other_post_changes(int $draft_id): array
    {
        $title_changes = [];
        $content_changes = [];
        $excerpt_changes = [];

        $other_post_titles = get_post_meta($draft_id, Miruni_Preview_Meta_Keys::OTHER_POST_TITLES, true) ?: [];
        $other_post_content = get_post_meta($draft_id, Miruni_Preview_Meta_Keys::OTHER_POST_CONTENT, true) ?: [];
        $other_post_excerpts = get_post_meta($draft_id, Miruni_Preview_Meta_Keys::OTHER_POST_EXCERPTS, true) ?: [];

        // Ensure we're working with arrays
        $other_post_titles = is_array($other_post_titles) ? $other_post_titles : [];
        $other_post_content = is_array($other_post_content) ? $other_post_content : [];
        $other_post_excerpts = is_array($other_post_excerpts) ? $other_post_excerpts : [];

        // Get unique post IDs from all changes
        $other_post_ids = array_unique(
            array_merge(
                array_keys($other_post_titles),
                array_keys($other_post_content),
                array_keys($other_post_excerpts)
            )
        );

        if (!empty($other_post_ids)) {
            foreach ($other_post_ids as $post_id) {
                $update_args = ['ID' => (int) $post_id];
                $post_updated = false;

                if (isset($other_post_titles[$post_id]) && !empty($other_post_titles[$post_id])) {
                    $update_args['post_title'] = \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($other_post_titles[$post_id]);
                }
                if (isset($other_post_content[$post_id]) && !empty($other_post_content[$post_id])) {
                    // Ensure merge syntax is processed for published content strings
                    $update_args['post_content'] = is_string($other_post_content[$post_id]) ? \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($other_post_content[$post_id]) : $other_post_content[$post_id];
                }
                if (isset($other_post_excerpts[$post_id]) && !empty($other_post_excerpts[$post_id])) {
                    $update_args['post_excerpt'] = is_string($other_post_excerpts[$post_id]) ? \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($other_post_excerpts[$post_id]) : $other_post_excerpts[$post_id];
                }

                if (count($update_args) > 1) { // Only update if we have more than just the ID
                    $result = wp_update_post($update_args);
                    // @phpstan-ignore function.impossibleType
                    if ($result && !is_wp_error($result)) {
                        $post_updated = true;
                        if (isset($update_args['post_title'])) {
                            $title_changes[$post_id] = $update_args['post_title'];
                        }
                        if (isset($update_args['post_content'])) {
                            $content_changes[$post_id] = true; // Mark content as updated
                        }
                        if (isset($update_args['post_excerpt'])) {
                            $excerpt_changes[$post_id] = true; // Mark excerpt as updated
                        }
                    }
                }
            }
        }

        // Apply transient title changes, merging them with direct changes
        $title_changes = Miruni_Post_Meta_Utils::apply_transient_title_changes($draft_id, $title_changes);

        return [
            'updated_titles' => $title_changes,
            'updated_content' => array_keys($content_changes),
            'updated_excerpts' => array_keys($excerpt_changes)
        ];
    }

    /**
     * Applies changes to menu items (name and meta).
     *
     * @param int $draft_id The ID of the draft post.
     * @return void
     */
    private static function _apply_menu_item_changes(int $draft_id): void
    {
        // Apply menu item name changes first (uses wp_update_post)
        Miruni_Post_Meta_Utils::apply_menu_item_changes($draft_id);

        // Apply other menu item meta changes
        $menu_meta_to_apply = [
            Miruni_Preview_Meta_Keys::MENU_ITEM_PARENT => '_menu_item_menu_item_parent',
            Miruni_Preview_Meta_Keys::MENU_ITEM_URL => '_menu_item_url',
            Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECT_IDS => '_menu_item_object_id',
            Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECTS => '_menu_item_object',
        ];

        foreach ($menu_meta_to_apply as $draft_key => $target_meta_key) {
            $changes = get_post_meta($draft_id, $draft_key, true);
            if (is_array($changes)) {
                foreach ($changes as $menu_item_id => $new_value) {
                    // Only process merge syntax for string values during publish
                    $value_to_store = is_string($new_value) ? \Miruni\Merge_Syntax\miruni_process_merge_syntax_for_publish($new_value) : $new_value;
                    update_post_meta($menu_item_id, $target_meta_key, $value_to_store);
                }
            }
        }



    }


    /**
     * Clears relevant WordPress and plugin caches.
     *
     * @return void
     */
    private static function _clear_caches(): void
    {
        wp_cache_flush();
        if (function_exists('rocket_clean_domain')) {
            rocket_clean_domain();
        }
        if (function_exists('w3tc_flush_all')) {
            w3tc_flush_all();
        }
    }

    /**
     * Publishes a draft post, applying its changes to the parent and related items.
     *
     * @param int $draft_id The ID of the draft post to publish.
     * @throws Exception If the draft is not found, not a draft, or if backup/update fails.
     * @return array{
     *  post_id: int,
     *  updated_titles: array<int, string>,
     *  updated_content: array<int>,
     *  updated_excerpts: array<int>,
     *  updated_blocks: array<string>,
     *  message: string
     * }
     */
    public static function publish_draft(int $draft_id): array
    {
        $draft = get_post($draft_id);

        if (!$draft instanceof WP_Post) {
            throw new Exception('Draft not found');
        }
        if ($draft->post_status !== 'draft') {
            throw new Exception('Post is not a draft');
        }

        $parent_id = $draft->post_parent ?: Miruni_Post_ID_Placeholder::LATEST_POSTS;

        // 1. Create Reversion Backup (Must happen before changes)
        if (!self::create_reversion_backup($draft_id, $parent_id)) {
            do_action('miruni_error', new Exception("Failed to create reversion backup during publish"), [
                'context' => ['draft_id' => $draft_id, 'parent_id' => $parent_id],
                'operation' => 'Publish Draft - Backup Step',
                'user_id' => get_current_user_id()
            ]);
            throw new Exception('Failed to create reversion backup.');
        }

        try {
            // --- Create a revision of the parent post before applying changes ---
            if ($parent_id !== Miruni_Post_ID_Placeholder::LATEST_POSTS) {
                $parent_post = get_post($parent_id);
                if ($parent_post instanceof WP_Post) {
                    // Create a revision post
                    wp_save_post_revision($parent_id);
                }
            }
            // ---------------------------------------------------------------

            // 2. Update Parent Post
            self::_update_parent_post($parent_id, $draft);

            // 3. Update Builder Data
            self::_update_builder_data($parent_id, $draft_id);

            // 4. Apply Other Post Changes (Titles, Content, Excerpts)
            $other_changes = self::_apply_other_post_changes($draft_id);
            self::_apply_other_breakdance_data($draft_id);

            // 5. Apply Theme Mod Changes
            Miruni_Post_Meta_Utils::apply_theme_mod_changes($draft_id);

            // 6. Apply WordPress Option Changes
            Miruni_Post_Meta_Utils::apply_wp_option_changes($draft_id);

            // 7. Apply Menu Item Changes
            self::_apply_menu_item_changes($draft_id);

            // 8. Apply Block Template Changes
            $updated_blocks = Miruni_Post_Meta_Utils::apply_block_template_changes($draft_id);

            // 9. Apply Theme.json Changes
            $theme_json_result = Miruni_Theme_JSON_Manager::apply_theme_json_changes($draft_id);
            if (!$theme_json_result['success']) {
                do_action('miruni_error', new Exception("Failed to apply theme.json changes: " . $theme_json_result['message']), [
                    'context' => [
                        'draft_id' => $draft_id,
                        'parent_id' => $parent_id,
                        'theme_json_result' => $theme_json_result
                    ],
                    'operation' => 'Apply Theme.json Changes',
                    'user_id' => get_current_user_id()
                ]);
                // Continue with publication despite theme.json failure
            }

            // Apply Block Widget Changes
            $current_widget_block_data = get_option('widget_block', []);
            if (is_array($current_widget_block_data)) {
                $updated_widget_block_data = Miruni_Block_Widget_Manager::update_widget_data($current_widget_block_data, $draft_id, 'block');
                if ($updated_widget_block_data !== null) {
                    update_option('widget_block', $updated_widget_block_data);
                }
            }

            $current_custom_html_data = get_option('widget_custom_html', []);
            if (is_array($current_custom_html_data)) {
                $updated_custom_html_data = Miruni_Block_Widget_Manager::update_widget_data($current_custom_html_data, $draft_id, 'custom_html');
                if ($updated_custom_html_data !== null) {
                    update_option('widget_custom_html', $updated_custom_html_data);
                }
            }

            // 10. Cleanup Draft Data
            self::cleanup_draft_data($draft_id);

            // 11. Clear Caches
            self::_clear_caches();

            // delete the draft
            wp_delete_post($draft_id);

            return [
                'post_id' => $parent_id,
                'updated_titles' => $other_changes['updated_titles'],
                'updated_content' => $other_changes['updated_content'],
                'updated_excerpts' => $other_changes['updated_excerpts'],
                'updated_blocks' => array_keys($updated_blocks), // Assuming apply_block_template_changes returns array<slug, content>
                'theme_json_applied' => $theme_json_result['success'],
                'theme_json_message' => $theme_json_result['message'],
                'message' => 'Changes published successfully'
            ];
        } catch (Exception $e) {
            // Log the specific error during the publishing steps
            do_action('miruni_error', $e, [
                'context' => ['draft_id' => $draft_id, 'parent_id' => $parent_id],
                'operation' => 'Publish Draft - Apply Changes Step',
                'user_id' => get_current_user_id()
            ]);
            // Rethrow the exception to be handled by the caller
            throw $e;
        }
    }
}