<?php
namespace Miruni;

use Miruni\BlockWidget\Miruni_Block_Widget_Manager;
use Miruni_Service;
use Miruni\Miruni_Media_Manager;

if (!defined('ABSPATH'))
    exit; // Exit if accessed directly





use Miruni\ThemePreview\Miruni_Theme_Preview_Manager;
use Miruni\Elementor\Miruni_Elementor_Service;
use Miruni\Breakdance\Miruni_Breakdance_Service;
use Miruni\DraftManager\Miruni_Elementor_Draft_Manager;
use Miruni\DraftManager\Miruni_Breakdance_Draft_Manager;
use Miruni\Miruni_Post_Meta_Utils;
use Miruni\Miruni_Preview_Meta_Keys;
use Miruni\Miruni_Draft_Publisher;
use Miruni\UpdateUtils;
use Miruni\Gutenberg\Miruni_Gutenberg_Manager;
use Miruni\DraftManager\MiruniChangeResult;
use Miruni\Miruni_Theme_JSON_Manager;



use \Exception;
use WP_Post;




class Miruni_Draft_Manager
{
    private Miruni_Theme_Preview_Manager $theme_preview_manager;
    private Miruni_Elementor_Draft_Manager $elementor_draft_manager; // Add this line
    private Miruni_Breakdance_Draft_Manager $breakdance_draft_manager; // Add this line


    public function __construct(Miruni_Theme_Preview_Manager $preview_manager)
    {
        $this->theme_preview_manager = $preview_manager;
        $this->elementor_draft_manager = new Miruni_Elementor_Draft_Manager($this); // Pass $this
        $this->breakdance_draft_manager = new Miruni_Breakdance_Draft_Manager($this); // Pass $this
    }

    /**
     * Creates a special draft for the "Latest Posts" homepage
     * 
     * @return int|null New draft ID if successful, null if failed
     */
    private function create_latest_posts_preview(): ?int
    {
        try {
            // Create a special placeholder post to store our changes
            $draft_args = [
                'post_type' => 'page',
                'post_content' => '',
                'post_title' => 'Latest Posts Preview - ' . gmdate('Y-m-d H:i:s'),
                'post_status' => 'draft',
                'post_author' => get_current_user_id(),
            ];

            $draft_id = wp_insert_post($draft_args);
            // @phpstan-ignore function.impossibleType
            if (!$draft_id || is_wp_error($draft_id)) {
                // @phpstan-ignore function.impossibleType
                $error_msg = is_wp_error($draft_id) ? $draft_id->get_error_message() : 'Failed to create draft post';
                do_action('miruni_plugin_error', new Exception(esc_html($error_msg)), [
                    'context' => 'Draft Manager',
                    'operation' => 'Creating latest posts preview',
                    'user_id' => get_current_user_id()
                ]);
                return null;
            }

            // Store a meta flag to identify this as a latest posts preview
            update_post_meta($draft_id, '_miruni_latest_posts_preview', true);

            // Store current theme mods and WP options that might be relevant
            $theme_mods = get_theme_mods();
            // @phpstan-ignore function.alreadyNarrowedType
            if (is_array($theme_mods)) {
                update_post_meta($draft_id, Miruni_Preview_Meta_Keys::THEME_MODS, $theme_mods);
            }

            // Store relevant options
            $relevant_options = [];
            foreach ($this->get_default_wp_options() as $option_name) {
                $relevant_options[$option_name] = get_option($option_name);
            }
            update_post_meta($draft_id, Miruni_Preview_Meta_Keys::WP_OPTIONS, $relevant_options);

            return $draft_id;
        } catch (Exception $e) {
            do_action('miruni_plugin_error', $e, [
                'context' => 'Draft Manager',
                'operation' => 'Creating latest posts preview',
                'user_id' => get_current_user_id()
            ]);
            return null;
        }
    }

    /**
     * Get default WordPress options to track for previews
     * 
     * @return array<string> Default option names
     */
    private function get_default_wp_options(): array
    {
        return [
            'blogname',
            'blogdescription',
            'posts_per_page',
            'date_format',
            'time_format',
            'page_for_posts',
            'show_on_front',
        ];
    }

    /**
     * @param int $page_id
     * @return int|null New draft ID if successful, null if failed
     */
    private function create_preview_post(int $page_id): ?int
    {
        // Check if this is our special placeholder for latest posts
        if ($page_id === Miruni_Post_ID_Placeholder::LATEST_POSTS) {
            return $this->create_latest_posts_preview();
        }

        global $wpdb;
        $original_post = get_post($page_id);
        if (!$original_post instanceof WP_Post) {
            do_action('miruni_plugin_error', new Exception(esc_html("Original post not found: {$page_id}")), [
                'context' => 'Draft Manager',
                'operation' => 'Creating preview post',
                'user_id' => get_current_user_id()
            ]);
            return null;
        }

        $post_content = get_post_field('post_content', $original_post->ID, 'raw');

        try {
            $draft_args = [
                'post_type' => $original_post->post_type,
                'post_content' => wp_slash($post_content),
                'post_title' => $original_post->post_title,
                'post_status' => 'draft',
                'post_author' => get_current_user_id(),
                'post_parent' => $page_id,
                'comment_status' => $original_post->comment_status,
                'comment_count' => $original_post->comment_count,
                'post_date' => $original_post->post_date,
            ];

            $draft_id = wp_insert_post($draft_args);
            // @phpstan-ignore function.impossibleType
            if (!$draft_id || is_wp_error($draft_id)) {
                // @phpstan-ignore function.impossibleType
                $error_msg = is_wp_error($draft_id) ? $draft_id->get_error_message() : 'Failed to create draft post';
                do_action('miruni_plugin_error', new Exception(esc_html($error_msg)), [
                    'context' => 'Draft Manager',
                    'operation' => 'Creating preview post',
                    'user_id' => get_current_user_id()
                ]);
                return null;
            }

            // Copy all post meta, excluding specific keys and handling builders separately
            $exclude_meta_keys = [
                "_elementor_element_cache",
                "_elementor_css",
                "_elementor_page_assets",
                "_elementor_data",
                "_breakdance_data"
            ];
            $meta_keys = get_post_custom_keys($page_id);
            if ($meta_keys) {
                foreach ($meta_keys as $meta_key) {
                    if (in_array($meta_key, $exclude_meta_keys)) {
                        continue;
                    }

                    $meta_values = get_post_meta($page_id, $meta_key);
                    foreach ($meta_values as $meta_value) {
                        add_post_meta($draft_id, $meta_key, $meta_value);
                    }
                }
            }

            // Copy Elementor data from parent to draft without altering JSON encoding
            $elementor_data = Miruni_Elementor_Service::get_page_data($page_id);
            error_log("Miruni Draft Manager: Found Elementor data for original post ID $page_id.");
            if (!empty($elementor_data) || $elementor_data === '0') {
                error_log("Miruni Draft Manager: Copying Elementor data to draft ID $draft_id.");
                // If Elementor data is already a JSON string, copy it raw to avoid double-encoding
                if (is_string($elementor_data)) {
                    // Store exact JSON as-is on the draft and clear Elementor caches
                    Miruni_Elementor_Service::update_page_data($draft_id, $elementor_data);
                } elseif (is_array($elementor_data)) {
                    // If it's an array (rare, but guard), let the service encode and store it
                    $this->elementor_draft_manager->store_elementor_json_change($draft_id, $elementor_data);
                }
            }

            // Copy Breakdance data using the service
            $breakdance_data_array = Miruni_Breakdance_Service::get_page_data($page_id);
            // @phpstan-ignore function.alreadyNarrowedType
            if ($breakdance_data_array !== false && is_array($breakdance_data_array)) {
                $update_success = Miruni_Breakdance_Service::update_page_data($draft_id, $breakdance_data_array);
                if (!$update_success) {
                    error_log("Miruni Draft Manager: Failed to copy Breakdance data to draft ID $draft_id from original post ID $page_id.");
                    // Optionally handle this failure, maybe delete the draft?
                }
            }


            if (!$wpdb) {
                do_action('miruni_plugin_error', new Exception(esc_html("Failed to get WPDB instance")), [
                    'context' => 'Draft Manager',
                    'operation' => "Copying post meta",
                    'user_id' => get_current_user_id()
                ]);
                return null;
            }


            // copy wp_term_relationships
            $term_relationships = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM $wpdb->term_relationships WHERE object_id = %d",
                    $page_id
                )
            );


            if ($term_relationships) {
                foreach ($term_relationships as $term_relationship) {
                    $wpdb->insert(
                        $wpdb->term_relationships,
                        [
                            'object_id' => $draft_id,
                            'term_taxonomy_id' => $term_relationship->term_taxonomy_id,
                            'term_order' => $term_relationship->term_order
                        ]
                    );
                }
            }

            return $draft_id;
        } catch (Exception $e) {
            do_action('miruni_plugin_error', $e, [
                'context' => 'Draft Manager',
                'operation' => 'Creating preview post',
                'user_id' => get_current_user_id()
            ]);
            return null;
        }
    }

    /**
     * @param int $draft_id
     * @param string $content
     * @return bool
     */
    private function update_preview_content(int $draft_id, string $content): bool
    {
        try {
            // More aggressive cleaning of problematic characters

            // Handle all possible representations of non-breaking spaces
            $clean_content = preg_replace('/\x{00A0}/u', ' ', $content); // Unicode non-breaking space
            $clean_content = str_replace(
                ["\xC2\xA0", "\xA0", "&nbsp;", "\u00A0", "&#160;", "&#xA0;"],
                ' ',
                $clean_content ?? $content
            );

            // Convert HTML entities to their actual characters and then back to ensure consistent encoding
            $clean_content = html_entity_decode($clean_content, ENT_QUOTES | ENT_HTML5, 'UTF-8');

            // Normalize newlines
            $clean_content = str_replace(["\r\n", "\r"], "\n", $clean_content);

            // Remove excessive whitespace between tags
            $clean_content = preg_replace('/>\s+</', '><', $clean_content);

            // Completely disable WordPress auto-formatting for this content
            kses_remove_filters(); // Temporarily remove kses filters
            remove_filter('the_content', 'wpautop');
            remove_filter('the_content', 'wptexturize');

            // Force raw content handling
            $update_args = [
                'ID' => $draft_id,
                'post_content' => $clean_content ?? $content,
                'filter_content' => false
            ];

            // Update with kses disabled to prevent WP from modifying the HTML
            $result = wp_update_post($update_args);

            // Restore filters for other content
            kses_init_filters();
            add_filter('the_content', 'wpautop');
            add_filter('the_content', 'wptexturize');

            if (!$result) {
                do_action('miruni_plugin_error', new Exception(esc_html("Failed to update post content")), [
                    'context' => 'Draft Manager',
                    'operation' => 'Updating preview content',
                    'draft_id' => $draft_id,
                    'user_id' => get_current_user_id()
                ]);
            }

            return $result !== 0;
        } catch (Exception $e) {
            do_action('miruni_plugin_error', $e, [
                'context' => 'Draft Manager',
                'operation' => 'Updating preview content',
                'draft_id' => $draft_id,
                'user_id' => get_current_user_id()
            ]);
            return false;
        }
    }

    private function update_preview_title(int $draft_id, string $title): bool
    {
        try {
            $update_args = [
                'ID' => $draft_id,
                'post_title' => $title,
            ];

            $result = wp_update_post($update_args);

            if (!$result) {
                do_action('miruni_plugin_error', new Exception(esc_html("Failed to update post title")), [
                    'context' => 'Draft Manager',
                    'operation' => 'Updating preview title',
                    'draft_id' => $draft_id,
                    'user_id' => get_current_user_id()
                ]);
            }

            return $result !== 0;
        } catch (Exception $e) {
            do_action('miruni_plugin_error', $e, [
                'context' => 'Draft Manager',
                'operation' => 'Updating preview title',
                'draft_id' => $draft_id,
                'user_id' => get_current_user_id()
            ]);
            return false;
        }
    }

    public function store_menu_item_name_change(int $draft_id, int $menu_item_id, string $new_name): bool
    {
        $success = Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::MENU_ITEM_NAME,
            $menu_item_id,
            $new_name,
            "Storing menu item name change: {$menu_item_id} - {$new_name}"
        );

        if (!$success) {
            do_action('miruni_plugin_error', new Exception(esc_html("Failed to store menu item name change")), [
                'context' => 'Draft Manager',
                'operation' => 'Storing menu item name change',
                'draft_id' => $draft_id,
                'menu_item_id' => $menu_item_id,
                'user_id' => get_current_user_id()
            ]);
        }

        return $success;
    }

    public function store_menu_item_parent_change(int $draft_id, int $menu_item_id, int $new_parent_id): bool
    {
        $success = Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::MENU_ITEM_PARENT,
            $menu_item_id,
            $new_parent_id,
            "Storing menu item parent change: {$menu_item_id} - {$new_parent_id}"
        );

        if (!$success) {
            do_action('miruni_plugin_error', new Exception(esc_html("Failed to store menu item parent change")), [
                'context' => 'Draft Manager',
                'operation' => 'Storing menu item parent change',
                'draft_id' => $draft_id,
                'menu_item_id' => $menu_item_id,
                'user_id' => get_current_user_id()
            ]);
        }

        return $success;
    }

    public function store_menu_item_url_change(int $draft_id, int $menu_item_id, string $new_url): bool
    {
        return Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::MENU_ITEM_URL,
            $menu_item_id,
            $new_url,
            "Storing menu item URL change: {$menu_item_id} - {$new_url}"
        );
    }

    public function store_menu_item_object_id_change(int $draft_id, int $menu_item_id, string $new_object_id): bool
    {

        return Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECT_IDS,
            $menu_item_id,
            $new_object_id,
            "Storing menu item object ID change: {$menu_item_id} - {$new_object_id}"
        );
    }

    public function store_menu_item_order_change(int $draft_id, int $menu_item_id, string $new_order): bool
    {
        error_log("Miruni Draft Manager: Processing menu item order change - {$menu_item_id} - {$new_order}");

        return Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::MENU_ITEM_ORDER,
            $menu_item_id,
            $new_order,
            "Storing menu item object ID change: {$menu_item_id} - {$new_order}"
        );
    }

    public function store_menu_item_object_change(int $draft_id, int $menu_item_id, string $new_object): bool
    {

        return Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::MENU_ITEM_OBJECTS,
            $menu_item_id,
            $new_object,
            "Storing menu item object change: {$menu_item_id} - {$new_object}"
        );
    }

    /**
     * Generic helper to store post changes
     * 
     * @param int $draft_id ID of the draft post
     * @param int $post_id ID of the post being changed
     * @param string $new_value The new value to store
     * @param Miruni_Preview_Meta_Keys::* $meta_key The meta key to store changes under
     * @param string $error_context Context for error message
     * @return bool Success status
     */
    public function store_other_post_change(
        int $draft_id,
        int $post_id,
        string $new_value,
        string $meta_key,
        string $error_context
    ): bool {
        return Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            $meta_key,
            $post_id,
            $new_value,
            "Storing {$error_context} changes for post {$post_id}"
        );
    }

    public function store_other_post_title_change(int $draft_id, int $post_id, string $new_title): bool
    {
        return $this->store_other_post_change(
            $draft_id,
            $post_id,
            $new_title,
            Miruni_Preview_Meta_Keys::OTHER_POST_TITLES,
            'title'
        );
    }

    public function store_other_post_content_change(int $draft_id, int $post_id, string $new_content): bool
    {
        return $this->store_other_post_change(
            $draft_id,
            $post_id,
            $new_content,
            Miruni_Preview_Meta_Keys::OTHER_POST_CONTENT,
            'content'
        );
    }

    public function store_theme_mod_change(int $draft_id, string $mod_name, mixed $new_value): bool
    {
        return Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::THEME_MODS,
            $mod_name,
            $new_value,
            "Storing theme mod change: {$mod_name}"
        );
    }

    public function store_wp_option_change(int $draft_id, string $option_name, mixed $new_value): bool
    {
        return Miruni_Post_Meta_Utils::update_meta_array_item(
            $draft_id,
            Miruni_Preview_Meta_Keys::WP_OPTIONS,
            $option_name,
            $new_value,
            "Storing WP option change: {$option_name}"
        );
    }

    private function store_batch_id_to_draft_mapping(int $draft_id, int $batch_id): void
    {
        // store a mapping of batch_id to draft_id in a transient so that we can retrieve it later
        $mapping = get_transient('_miruni_batch_to_draft_mapping_' . $batch_id);
        if (!is_array($mapping)) {
            $mapping = [];
        }
        $mapping["draft_id"] = $draft_id;
        set_transient('_miruni_batch_to_draft_mapping_' . $batch_id, $mapping);
    }

    private function get_draft_id_from_batch_id(int $batch_id): ?int
    {
        $mapping = get_transient('_miruni_batch_to_draft_mapping_' . $batch_id);
        if (is_array($mapping) && isset($mapping["draft_id"])) {
            return $mapping["draft_id"];
        }
        return null;
    }

    private function clear_batch_id_to_draft_mapping(int $batch_id): void
    {
        delete_transient('_miruni_batch_to_draft_mapping_' . $batch_id);
    }




    /**
     * Request to edit a preview with multiple updates
     * 
     * @param int $page_id The ID of the page to create preview for
     * @param int $suggestion_batch_id The ID of the suggestion batch
     * @param list<array{id: int, fileType:  UpdateUtils\MiruniFileType, updatedFileContent: string, fileIdentifier: string, updateSummary: string, updateMeta:array<mixed>}> $updates Array of update operations to perform
     * @return array{
     *   preview_title: string,
     *   preview_page_id: int,
     *   preview_url: string,
     *   changes: array<mixed>
     * }
     * @throws Exception When page not found or preview creation fails
     */
    public function request_edit_preview_v2(int $page_id, int $suggestion_batch_id, array $updates): array
    {
        // For Latest Posts placeholder, we don't need to check if the page exists
        if ($page_id !== Miruni_Post_ID_Placeholder::LATEST_POSTS) {
            $page = get_post($page_id);
            if (!$page instanceof WP_Post) {
                throw new Exception(esc_html('Page not found by ID: ' . $page_id));
            }
        }

        $this->theme_preview_manager->init_preview_system();

        $existing_draft_id = $this->get_draft_id_from_batch_id($suggestion_batch_id);

        if ($existing_draft_id) {
            // delete the existing draft post
            Miruni_Draft_Publisher::cleanup_draft_data($existing_draft_id);
            $this->clear_batch_id_to_draft_mapping($suggestion_batch_id);
        }

        // Create preview post first
        $draft_id = $this->create_preview_post($page_id);
        if (!$draft_id) {
            throw new Exception(esc_html('Failed to create preview post'));
        }
        $this->store_batch_id_to_draft_mapping($draft_id, $suggestion_batch_id);

        /** @var MiruniChangeResult[] */
        $changes = [];
        $elementor_updates = [];
        $breakdance_updates = [];
        foreach ($updates as $update) {
            if ($update['fileType'] === UpdateUtils\MiruniFileType::ELEMENTOR_JSON) {
                $elementor_updates[] = $update;
            } else if ($update['fileType'] === UpdateUtils\MiruniFileType::BREAKDANCE_DATA) {
                $breakdance_updates[] = $update;
            } else {
                $_post = null;
                if ($page_id !== Miruni_Post_ID_Placeholder::LATEST_POSTS) {
                    $__post = get_post($page_id);
                    if ($__post instanceof WP_Post) {
                        $_post = $__post;
                    }
                }
                $result = $this->process_update($update, $draft_id, $_post);
                $changes[] = $result;
            }
        }



        if (!empty($elementor_updates)) {
            // Delegate to Elementor Draft Manager
            $elementor_changes = $this->elementor_draft_manager->process_elementor_json_updates($elementor_updates, $draft_id);
            $changes = array_merge($changes, $elementor_changes);
        }

        if (!empty($breakdance_updates)) {
            error_log("Miruni Draft Manager: Processing Breakdance updates");
            // Delegate to Breakdance Draft Manager
            // Pass $page_id as the parent_id
            $breakdance_changes = $this->breakdance_draft_manager->process_breakdance_updates($breakdance_updates, $draft_id, $page_id);
            $changes = array_merge($changes, $breakdance_changes);
        }

        // Generate the preview URL
        $preview_url = '';
        if ($page_id === Miruni_Post_ID_Placeholder::LATEST_POSTS) {
            // For latest posts homepage, we need to force the homepage view
            $preview_url = $this->theme_preview_manager->get_preview_url(
                home_url('/')
            );
            // Add a query parameter to help identify this is a latest posts preview
            $preview_url = add_query_arg('preview_draft_id', $draft_id, $preview_url);
        } else {
            // Check if this post is the front page
            $posts_page_id = (int) get_option('page_for_posts');

            if ($page_id == $posts_page_id) {
                $path = '/?page_id=' . $draft_id; // Posts page
            } else {
                /** @var WP_Post $page */
                $path = $page->post_type === 'page'
                    ? "?page_id={$draft_id}"
                    : "?p={$draft_id}";
            }

            $preview_url = $this->theme_preview_manager->get_preview_url(
                home_url($path)
            );
        }

        // Get the title for the preview
        $preview_title = $page_id === Miruni_Post_ID_Placeholder::LATEST_POSTS
            ? get_bloginfo('name') . ' - Latest Posts Preview'
            : get_the_title($draft_id);


        return [
            'preview_title' => $preview_title,
            'preview_page_id' => $draft_id,
            'preview_url' => $preview_url,
            'changes' => array_map(function ($c) {
                return $c->to_array();
            }, $changes)
        ];
    }

    /**
     * Process a single update operation
     * 
     * @param array{id: int, fileType:UpdateUtils\MiruniFileType, updatedFileContent: string, fileIdentifier: string, updateSummary: string, updateMeta:array<mixed>} $update The update operation to process
     * @param int $draft_id ID of the draft post
     * @param ?WP_Post $page Original page being edited
     * @return MiruniChangeResult
     */
    private function process_update(array $update, int $draft_id, ?WP_Post $page): MiruniChangeResult
    {
        $file_type = $update['fileType'];
        $updated_content = $update['updatedFileContent'];
        $file_identifier = $update['fileIdentifier'];
        $update_summary = $update['updateSummary'];
        $update_meta = $update['updateMeta'];

        // sometimes the post content/title etc is incorrectly classified as OTHER_POST_CONTENT etc
        // if the update's post ID is the same as the original page ID, we need to reclassify
        if (
            $file_type == UpdateUtils\MiruniFileType::OTHER_POST_CONTENT
            || $file_type == UpdateUtils\MiruniFileType::OTHER_POST_TITLE
        ) {
            // file identifier will look like "other_post_content_1" with the ID at the end. Check to see if the final part equals the post id
            // Check if the file identifier contains the draft ID
            if (preg_match('/\d+$/', $file_identifier, $matches)) {
                $post_id = (int) $matches[0];
                if ($page && $post_id === $page->ID) {
                    // Reclassify to POST_CONTENT or POST_TITLE
                    if ($file_type === UpdateUtils\MiruniFileType::OTHER_POST_CONTENT) {
                        $file_type = UpdateUtils\MiruniFileType::POST_CONTENT;
                    } elseif ($file_type === UpdateUtils\MiruniFileType::OTHER_POST_TITLE) {
                        $file_type = UpdateUtils\MiruniFileType::POST_TITLE;
                    }
                }
            }
        }

        $result = null;
        switch ($file_type) {
            case UpdateUtils\MiruniFileType::POST_TITLE:
                if ($this->update_preview_title($draft_id, $updated_content)) {
                    $original_title = $page instanceof WP_Post ? $page->post_title : "";
                    $post_content = $page instanceof WP_Post ? $page->post_content : '';
                    $result = $this->create_change_entry(
                        "Current post title",
                        $update_summary,
                        $original_title,
                        $file_identifier,
                        $file_type
                    );
                    $result["post"] = [
                        "post_id" => $draft_id,
                        "post_title" => $updated_content,
                        "post_content" => $post_content
                    ];
                }
                break;

            case UpdateUtils\MiruniFileType::GUTENBERG_BLOCK_UPDATE:
                error_log("Miruni Draft Manager: Processing Gutenberg block update - {$file_identifier} - update metadata: " . json_encode($update_meta));

                // Validate required fields in updateMeta
                if (!isset($update_meta['block_name']) || !is_string($update_meta['block_name'])) {
                    error_log("Miruni Draft Manager: Missing or invalid block_name in updateMeta");
                    $result = $this->create_change_entry(
                        'gutenberg block (error)',
                        "Failed to process: {$update_summary} - Missing block_name",
                        '',
                        $file_identifier,
                        $file_type
                    );
                    break;
                }

                if (!isset($update_meta['match_attrs']) || !is_array($update_meta['match_attrs'])) {
                    error_log("Miruni Draft Manager: Missing or invalid match_attrs in updateMeta");
                    $result = $this->create_change_entry(
                        'gutenberg block (error)',
                        "Failed to process: {$update_summary} - Missing match_attrs",
                        '',
                        $file_identifier,
                        $file_type
                    );
                    break;
                }

                if (!isset($update_meta['updates']) || !is_array($update_meta['updates'])) {
                    error_log("Miruni Draft Manager: Missing or invalid updates in updateMeta");
                    $result = $this->create_change_entry(
                        'gutenberg block (error)',
                        "Failed to process: {$update_summary} - Missing updates",
                        '',
                        $file_identifier,
                        $file_type
                    );
                    break;
                }

                $block_name = $update_meta['block_name'];
                $match_attrs = $update_meta['match_attrs'];
                $updates = $update_meta['updates'];

                try {
                    $updated_content = Miruni_Gutenberg_Manager::update_block_surgically(
                        $draft_id,
                        $block_name,
                        $match_attrs,
                        $updates,
                        true
                    );

                    if ($updated_content !== false) {
                        $original_content = $page instanceof WP_Post ? $page->post_content : "";
                        $post_title = $page instanceof WP_Post ? $page->post_title : '';
                        $result = $this->create_change_entry(
                            "gutenberg block ({$block_name})",
                            $update_summary,
                            $original_content,
                            $file_identifier,
                            $file_type,
                            [],
                            $updated_content["updated_post"]
                        );
                        $result["post"] = [
                            "post_id" => $draft_id,
                            "post_title" => $post_title,
                            "post_content" => $updated_content["updated_post"]
                        ];
                    } else {
                        error_log("Miruni Draft Manager: Failed to update Gutenberg block - no changes made");
                        $result = $this->create_change_entry(
                            'gutenberg block (error)',
                            "Failed to update block: {$update_summary} - No changes made",
                            '',
                            $file_identifier,
                            $file_type
                        );
                    }
                } catch (Exception $e) {
                    error_log("Miruni Draft Manager: Exception updating Gutenberg block: " . $e->getMessage());
                    $result = $this->create_change_entry(
                        'gutenberg block (error)',
                        "Failed to update block: {$update_summary} - " . $e->getMessage(),
                        '',
                        $file_identifier,
                        $file_type
                    );
                }
                break;

            case UpdateUtils\MiruniFileType::POST_CONTENT:
            case UpdateUtils\MiruniFileType::POST:
                if ($this->update_preview_content($draft_id, $updated_content)) {
                    $original_content = $page instanceof WP_Post ? $page->post_content : "";
                    $post_title = $page instanceof WP_Post ? $page->post_title : '';
                    $result = $this->create_change_entry(
                        "Current post",
                        $update_summary,
                        $original_content,
                        $file_identifier,
                        $file_type
                    );
                    $result["post"] = [
                        "post_id" => $draft_id,
                        "post_title" => $post_title,
                        "post_content" => $updated_content
                    ];
                }
                break;

            case UpdateUtils\MiruniFileType::WP_OPTION:
                if ($this->store_wp_option_change($draft_id, $file_identifier, $updated_content)) {
                    $result = $this->create_change_entry(
                        "WP Option: {$file_identifier}",
                        $update_summary,
                        get_option($file_identifier),
                        $file_identifier,
                        $file_type
                    );
                }
                break;

            case UpdateUtils\MiruniFileType::OTHER_POST_EXCERPT:
            case UpdateUtils\MiruniFileType::OTHER_POST_TITLE:
            case UpdateUtils\MiruniFileType::OTHER_POST_CONTENT:
                // Print JSON of update
                preg_match('/\d+/', $file_identifier, $matches);
                $post_id = isset($matches[0]) ? (int) $matches[0] : 0;

                if ($post_id > 0) {
                    $reference_post = get_post($post_id);

                    if ($reference_post instanceof WP_Post) {
                        $original_content = $file_type === UpdateUtils\MiruniFileType::OTHER_POST_TITLE
                            ? $reference_post->post_title
                            : $reference_post->post_content;


                        $success = false;
                        if ($file_type === UpdateUtils\MiruniFileType::OTHER_POST_TITLE) {
                            $success = $this->store_other_post_title_change($draft_id, $post_id, $updated_content);
                        } else if ($file_type === UpdateUtils\MiruniFileType::OTHER_POST_CONTENT) {
                            $success = $this->store_other_post_content_change($draft_id, $post_id, $updated_content);
                        } else {
                            $success = $this->store_other_post_change($draft_id, $post_id, $updated_content, Miruni_Preview_Meta_Keys::OTHER_POST_EXCERPTS, 'excerpt');
                        }
                        if ($success) {
                            $file_name = $reference_post->post_title;

                            $result = $this->create_change_entry(
                                $file_name,
                                $update_summary,
                                $original_content,
                                $file_identifier, // Keep original identifier here for context if needed
                                $file_type,
                                ['referenced_post_id' => $post_id]
                            );
                            $result["post"] = [
                                "post_id" => $post_id,
                                "post_title" => $reference_post->post_title,
                                "post_content" => $reference_post->post_content
                            ];
                        }
                    }
                } else {
                    // Handle case where post_id couldn't be extracted or is invalid
                    error_log("Miruni Draft Manager: Invalid post ID extracted from identifier {$file_identifier} for type {$file_type->value}");
                    // Create an error result
                    $result = $this->create_change_entry(
                        'other post (error)',
                        "Failed to process: {$update_summary} - Invalid post ID",
                        '',
                        $file_identifier,
                        $file_type
                    );
                }
                break;

            case UpdateUtils\MiruniFileType::MENU_ITEM_NAME:
            case UpdateUtils\MiruniFileType::MENU_ITEM_PARENT:
            case UpdateUtils\MiruniFileType::MENU_ITEM_URL:
            case UpdateUtils\MiruniFileType::MENU_ITEM_ORDER:
            case UpdateUtils\MiruniFileType::MENU_ITEM_OBJECT_ID:
            case UpdateUtils\MiruniFileType::MENU_ITEM_OBJECT:
                $result = $this->_process_menu_item_update($update, $draft_id);
                break;

            case UpdateUtils\MiruniFileType::BLOCK_TEMPLATE:
                if (
                    Miruni_Post_Meta_Utils::update_meta_array_item(
                        $draft_id,
                        Miruni_Preview_Meta_Keys::BLOCK_TEMPLATE,
                        $file_identifier,
                        $updated_content,
                        "Storing block template change: {$file_identifier}"
                    )
                ) {
                    $result = $this->create_change_entry(
                        'block template',
                        $update_summary,
                        $updated_content,
                        $file_identifier,
                        $file_type
                    );
                }
                break;

            case UpdateUtils\MiruniFileType::BLOCK_WIDGET:
                error_log("Miruni Draft Manager: Processing block widget update - {$file_identifier} - {$updated_content}");
                if (
                    Miruni_Post_Meta_Utils::update_meta_array_item(
                        $draft_id,
                        Miruni_Preview_Meta_Keys::BLOCK_WIDGET,
                        $file_identifier,
                        $updated_content,
                        "Storing block widget change: {$file_identifier}"
                    )
                ) {
                    error_log("Miruni Draft Manager: Block widget update stored successfully - {$file_identifier}");
                    $current_widget = Miruni_Block_Widget_Manager::get_widget_data_by_id($file_identifier);
                    $original_content = $current_widget ? $current_widget->content : '';
                    $result = $this->create_change_entry(
                        'block widget',
                        $update_summary,
                        $original_content,
                        $file_identifier,
                        $file_type
                    );
                }
                break;

            case UpdateUtils\MiruniFileType::BREAKDANCE_GLOBAL_BLOCK:
                error_log("Miruni Draft Manager: Processing Breakdance global block update - {$file_identifier} - {$updated_content}");
                preg_match('/\d+/', $file_identifier, $matches);
                $post_id = isset($matches[0]) ? (int) $matches[0] : 0;
                if (
                    Miruni_Post_Meta_Utils::update_meta_array_item(
                        $draft_id,
                        Miruni_Preview_Meta_Keys::BREAKDANCE_GLOBAL_BLOCK,
                        $post_id,
                        wp_slash($updated_content),
                        "Storing Breakdance global block change: {$file_identifier}"
                    )
                ) {
                    $result = $this->create_change_entry(
                        'breakdance global block',
                        $update_summary,
                        $updated_content,
                        $file_identifier,
                        $file_type
                    );
                }
                break;

            case UpdateUtils\MiruniFileType::THEME_JSON:
                error_log("Miruni Draft Manager: Processing theme.json update - {$file_identifier} - type: " . gettype($updated_content));

                // Decode JSON string to array to prevent quote mangling
                $theme_json_data = json_decode($updated_content, true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log("Miruni Draft Manager: Failed to decode theme.json - " . json_last_error_msg());
                    $result = $this->create_change_entry(
                        'theme.json (error)',
                        "Failed to decode JSON: {$update_summary}",
                        '',
                        'theme.json',
                        $file_type
                    );
                    break;
                }

                if (
                    Miruni_Post_Meta_Utils::update_meta_array_item(
                        $draft_id,
                        Miruni_Preview_Meta_Keys::THEME_JSON,
                        'theme_json', // Using a single key for theme.json
                        $theme_json_data, // Now passing array instead of string
                        "Storing theme.json change"
                    )
                ) {

                    $original_content = Miruni_Theme_JSON_Manager::get_theme_json_data() ?? [];
                    $original_content_str = json_encode($original_content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

                    $result = $this->create_change_entry(
                        'theme.json',
                        $update_summary,
                        (string) $original_content_str,
                        'theme.json',
                        $file_type
                    );
                }
                break;
        }
        if (!$result) {
            do_action('miruni_plugin_error', new Exception(esc_html("Failed to process update: " . $update_summary)), [
                'context' => 'Draft Manager',
                'operation' => 'Processing update',
                'draft_id' => $draft_id,
                'update' => json_encode($update),
                'user_id' => get_current_user_id()
            ]);
            // Consider creating a default error result instead of throwing an exception
            // to maintain consistency with how menu item ID errors are handled.
            $result = $this->create_change_entry(
                'processing error',
                "Failed to process update: {$update_summary}",
                '',
                $file_identifier,
                $file_type
            );
            error_log("Miruni Draft Manager: Failed to process update for type {$file_type->value} with identifier {$file_identifier}");
            // throw new Exception(esc_html("Failed to process update: " . $update['fileType']->value));
        }

        $change_result = new MiruniChangeResult(
            $result['file_name'],
            $result['changes_made'],
            $result['original_content'],
            $result['file_identifier'], // This should be the extracted ID for menu items now
            $result['file_type'],
            $update["id"],
            null,
            $result["post"] ?? null,
            $result["updated_file_content"] ?? null,
        );
        // Add post data back if it exists in the result
        if (isset($result['post'])) {
            $change_result->set_post_data($result['post']);
        }
        if (isset($result['referenced_post_id'])) {
            $change_result->set_referenced_post_id($result['referenced_post_id']);
        }

        error_log("Change result: " . $change_result->suggestionId);

        return $change_result;
    }


    /**
     * Process a menu item update operation
     * 
     * Extracts menu item ID, calls the appropriate storage function, and creates a change entry.
     * 
     * @param array{id: int, fileType:UpdateUtils\MiruniFileType, updatedFileContent: string, fileIdentifier: string, updateSummary: string} $update The update operation to process
     * @param int $draft_id ID of the draft post
     * @return array{
     *  file_name: string,
     *  changes_made: string,
     *  original_content: string,
     *  file_identifier: string,
     *  file_type: string,
     * } The result array for the change entry.
     */
    private function _process_menu_item_update(array $update, int $draft_id): array
    {
        $file_type = $update['fileType'];
        $updated_content = $update['updatedFileContent'];
        $original_identifier = $update['fileIdentifier']; // Keep the original identifier for logging/errors
        $update_summary = $update['updateSummary'];

        $menu_item_id = 0;
        $extracted_identifier = ''; // This will hold the extracted ID string

        // Extract menu item ID from the identifier (assuming format "xxx_123" or just "123")
        if (preg_match('/(?:_|^)(\d+)$/', $original_identifier, $matches)) {
            $menu_item_id = (int) $matches[1];
            $extracted_identifier = $matches[1]; // Use the extracted ID string for the change entry identifier
        } else {
            error_log("Miruni Draft Manager: Failed to extract menu item ID from identifier {$original_identifier} for type {$file_type->value}");
            // Create an error result
            return $this->create_change_entry(
                "menu item {$file_type->name} (error)",
                "Failed to process: {$update_summary} - Invalid identifier",
                '',
                $original_identifier, // Use original identifier in error case
                $file_type
            );
        }

        // If ID extraction failed somehow after regex (e.g., empty match, though regex prevents this)
        if ($menu_item_id <= 0) {
            error_log("Miruni Draft Manager: Invalid menu item ID (0 or less) extracted from identifier {$original_identifier} for type {$file_type->value}");
            return $this->create_change_entry(
                "menu item {$file_type->name} (error)",
                "Failed to process: {$update_summary} - Invalid ID extracted",
                '',
                $original_identifier,
                $file_type
            );
        }


        $success = false;
        $change_label = '';
        $original_value = $updated_content; // Default original value to updated, specific cases might fetch real original

        switch ($file_type) {
            case UpdateUtils\MiruniFileType::MENU_ITEM_NAME:
                $change_label = 'menu item name';
                // TODO: Fetch original name if needed for diffing?
                $success = $this->store_menu_item_name_change($draft_id, $menu_item_id, $updated_content);
                break;
            case UpdateUtils\MiruniFileType::MENU_ITEM_PARENT:
                $change_label = 'menu item parent';
                // TODO: Fetch original parent if needed
                $success = $this->store_menu_item_parent_change($draft_id, $menu_item_id, (int) $updated_content);
                break;
            case UpdateUtils\MiruniFileType::MENU_ITEM_URL:
                $change_label = 'menu item url';
                // TODO: Fetch original URL if needed
                $success = $this->store_menu_item_url_change($draft_id, $menu_item_id, $updated_content);
                break;
            case UpdateUtils\MiruniFileType::MENU_ITEM_ORDER:
                error_log("Miruni Draft Manager: Storing menu item order change - ID {$menu_item_id} - Order {$updated_content}");
                $change_label = 'menu item order';
                // TODO: Fetch original order if needed
                $success = $this->store_menu_item_order_change($draft_id, $menu_item_id, $updated_content);
                break;
            case UpdateUtils\MiruniFileType::MENU_ITEM_OBJECT_ID:
                $change_label = 'menu item object id';
                // TODO: Fetch original object ID if needed
                $success = $this->store_menu_item_object_id_change($draft_id, $menu_item_id, $updated_content);
                break;
            case UpdateUtils\MiruniFileType::MENU_ITEM_OBJECT:
                $change_label = 'menu item object';
                // TODO: Fetch original object if needed
                $success = $this->store_menu_item_object_change($draft_id, $menu_item_id, $updated_content);
                break;
        }

        if ($success) {
            return $this->create_change_entry(
                $change_label,
                $update_summary,
                $original_value, // Placeholder original content
                $extracted_identifier, // Use the extracted ID here
                $file_type
            );
        } else {
            error_log("Miruni Draft Manager: Failed to store change for menu item ID {$menu_item_id}, type {$file_type->value}");
            // Create an error result if storing failed
            return $this->create_change_entry(
                "menu item {$file_type->name} (error)",
                "Failed to store change: {$update_summary}",
                '',
                $extracted_identifier,
                $file_type
            );
        }
    }


    /**
     * Create a standardized change entry
     * 
     * @param string $file_name The name of the file or field being changed
     * @param string $changes_made Summary of changes made
     * @param string $original_content Original content before changes
     * @param string $file_identifier Identifier for the file/content being changed
     * @param UpdateUtils\MiruniFileType $file_type Type of content being changed
     * @param array{
     * referenced_post_id?: int
     * } $additional_fields Additional fields to include
     * @param string $updatedFileContent The updated file content, if applicable
     * @return array{
     *   file_name: string,
     *   changes_made: string,
     *   original_content: string,
     *   file_identifier: string,
     *   file_type: string,
     *   referenced_post_id?: int,
     *   updated_file_content?: string|null
     * }
     */
    public function create_change_entry(
        string $file_name,
        string $changes_made,
        string $original_content,
        string $file_identifier,
        UpdateUtils\MiruniFileType $file_type,
        array $additional_fields = [],
        string $updatedFileContent = ''
    ): array {
        $entry = [
            'file_name' => $file_name,
            'changes_made' => $changes_made,
            'original_content' => $original_content,
            'file_identifier' => $file_identifier,
            'file_type' => $file_type->value,
            'updated_file_content' => $updatedFileContent,
        ];

        return array_merge($entry, $additional_fields);
    }


    /**
     * Summary of handle_ad_hoc_change
     * @param int $draft_id
     * @param array{id: int, fileType: UpdateUtils\MiruniFileType, updatedFileContent: string, fileIdentifier: string, updateSummary: string, updateMeta:array<mixed>} $update
     * @return MiruniChangeResult
     */
    public function handle_ad_hoc_change(int $draft_id, array $update): MiruniChangeResult
    {
        return $this->process_update($update, $draft_id, null);
    }


}