<?php

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

trait Niwa_AI_Tools_Elementor_Widgets {

    /* ==================================================================
     *  READ-ONLY TOOL 1: elementor_list_pages
     * ================================================================== */

    public function execute_elementor_list_pages( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_type   = isset( $args['post_type'] ) && in_array( (string) $args['post_type'], array( 'page', 'post', 'any' ), true )
            ? (string) $args['post_type']
            : 'any';
        $post_status = isset( $args['post_status'] ) && in_array( (string) $args['post_status'], array( 'publish', 'draft', 'pending', 'private', 'any' ), true )
            ? (string) $args['post_status']
            : 'any';
        $limit       = isset( $args['limit'] ) ? max( 1, min( 100, intval( $args['limit'] ) ) ) : 50;
        $offset      = isset( $args['offset'] ) ? max( 0, intval( $args['offset'] ) ) : 0;

        $query_args = array(
            'posts_per_page' => $limit,
            'offset'         => $offset,
            'orderby'        => 'modified',
            'order'          => 'DESC',
            'meta_query'     => array(
                array(
                    'key'   => '_elementor_edit_mode',
                    'value' => 'builder',
                ),
            ),
        );

        if ( 'any' === $post_type ) {
            $query_args['post_type'] = array( 'page', 'post' );
        } else {
            $query_args['post_type'] = $post_type;
        }

        if ( 'any' === $post_status ) {
            $query_args['post_status'] = array( 'publish', 'draft', 'pending', 'private' );
        } else {
            $query_args['post_status'] = $post_status;
        }

        $query = new \WP_Query( $query_args );
        $pages = array();

        foreach ( $query->posts as $post ) {
            $elementor_version = get_post_meta( $post->ID, '_elementor_version', true );
            $pages[] = array(
                'id'                => $post->ID,
                'title'             => $post->post_title,
                'post_type'         => $post->post_type,
                'post_status'       => $post->post_status,
                'modified'          => $post->post_modified,
                'elementor_version' => (string) $elementor_version,
            );
        }

        return array(
            'success'     => true,
            'total_found' => intval( $query->found_posts ),
            'count'       => count( $pages ),
            'offset'      => $offset,
            'pages'       => $pages,
        );
    }

    /* ==================================================================
     *  READ-ONLY TOOL 2: elementor_get_page_structure
     * ================================================================== */

    public function execute_elementor_get_page_structure( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }

        $post = get_post( $post_id );
        if ( ! $post ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') nije pronadjen.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $max_depth        = isset( $args['max_depth'] ) ? max( 1, min( 20, intval( $args['max_depth'] ) ) ) : 10;
        $include_settings = ! empty( $args['include_settings'] );

        $structure = $this->build_elementor_structure( $data, 0, $max_depth, $include_settings );

        return array(
            'success'    => true,
            'post_id'    => $post_id,
            'post_title' => $post->post_title,
            'structure'  => $structure,
        );
    }

    /* ==================================================================
     *  READ-ONLY TOOL 2B: elementor_get_page_data
     * ================================================================== */

    public function execute_elementor_get_page_data( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }

        $post = get_post( $post_id );
        if ( ! $post ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') nije pronadjen.' );
        }
        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $raw = (string) get_post_meta( $post_id, '_elementor_data', true );
        if ( '' === $raw ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan.' );
        }

        $decoded = json_decode( $raw, true );
        if ( ! is_array( $decoded ) || JSON_ERROR_NONE !== json_last_error() ) {
            return array(
                'success'      => false,
                'error'        => '_elementor_data nije validan JSON.',
                'raw_length'   => strlen( $raw ),
                'raw_sha256'   => hash( 'sha256', $raw ),
                'raw_preview'  => mb_substr( $raw, 0, 2000 ),
            );
        }

        $include_raw = ! array_key_exists( 'include_raw', $args ) || ! empty( $args['include_raw'] );
        $max_chars   = isset( $args['max_chars'] ) ? max( 1000, min( 500000, intval( $args['max_chars'] ) ) ) : 120000;
        $truncated   = strlen( $raw ) > $max_chars;

        $result = array(
            'success'       => true,
            'post_id'       => $post_id,
            'post_title'    => (string) $post->post_title,
            'raw_length'    => strlen( $raw ),
            'raw_sha256'    => hash( 'sha256', $raw ),
            'root_count'    => count( $decoded ),
            'raw_truncated' => $include_raw ? $truncated : null,
        );

        if ( $include_raw ) {
            $result['elementor_data_json'] = $truncated ? mb_substr( $raw, 0, $max_chars ) : $raw;
        }

        return $result;
    }

    /* ==================================================================
     *  READ-ONLY TOOL 3: elementor_get_active_kit
     * ================================================================== */

    public function execute_elementor_get_active_kit( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $kit_id = intval( get_option( 'elementor_active_kit', 0 ) );
        if ( $kit_id <= 0 ) {
            return array( 'success' => false, 'error' => 'Aktivan Elementor kit nije pronadjen (elementor_active_kit option je prazna).' );
        }

        $include_colors     = ! array_key_exists( 'include_colors', $args ) || ! empty( $args['include_colors'] );
        $include_typography  = ! array_key_exists( 'include_typography', $args ) || ! empty( $args['include_typography'] );

        return $this->get_elementor_kit_data( $kit_id, $include_colors, $include_typography );
    }

    /* ==================================================================
     *  READ-ONLY TOOL 4: elementor_get_kit_by_id
     * ================================================================== */

    public function execute_elementor_get_kit_by_id( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $kit_id = isset( $args['kit_id'] ) ? intval( $args['kit_id'] ) : 0;
        if ( $kit_id <= 0 ) {
            return array( 'success' => false, 'error' => 'kit_id je obavezan.' );
        }

        $include_colors     = ! array_key_exists( 'include_colors', $args ) || ! empty( $args['include_colors'] );
        $include_typography  = ! array_key_exists( 'include_typography', $args ) || ! empty( $args['include_typography'] );

        return $this->get_elementor_kit_data( $kit_id, $include_colors, $include_typography );
    }

    /* ==================================================================
     *  READ-ONLY TOOL 5: elementor_find_widgets
     * ================================================================== */

    public function execute_elementor_find_widgets( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $widget_types     = isset( $args['widget_types'] ) && is_array( $args['widget_types'] ) ? array_map( 'sanitize_text_field', $args['widget_types'] ) : array();
        if ( empty( $widget_types ) ) {
            return array( 'success' => false, 'error' => 'widget_types niz je obavezan (npr. ["heading", "button"]).' );
        }

        $max_depth        = isset( $args['max_depth'] ) ? max( 1, min( 20, intval( $args['max_depth'] ) ) ) : 10;
        $include_settings = ! array_key_exists( 'include_settings', $args ) || ! empty( $args['include_settings'] );

        $widgets = $this->search_widgets_in_tree( $data, $widget_types, $max_depth, $include_settings, 0 );

        return array(
            'success'       => true,
            'post_id'       => $post_id,
            'widgets_found' => count( $widgets ),
            'widgets'       => $widgets,
        );
    }

    /* ==================================================================
     *  READ-ONLY TOOL 6: elementor_get_widget_by_id
     * ================================================================== */

    public function execute_elementor_get_widget_by_id( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id   = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $widget_id = isset( $args['widget_id'] ) ? sanitize_text_field( trim( (string) $args['widget_id'] ) ) : '';

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $widget_id ) {
            return array( 'success' => false, 'error' => 'widget_id je obavezan.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array(
                'success' => true,
                'found'   => false,
                'post_id' => $post_id,
                'widget'  => null,
            );
        }

        return array(
            'success' => true,
            'found'   => true,
            'post_id' => $post_id,
            'widget'  => array(
                'id'          => isset( $found['element']['id'] ) ? (string) $found['element']['id'] : '',
                'elType'      => isset( $found['element']['elType'] ) ? (string) $found['element']['elType'] : '',
                'widget_type' => isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : '',
                'depth'       => $found['depth'],
                'path'        => $found['path'],
                'settings'    => isset( $found['element']['settings'] ) && is_array( $found['element']['settings'] ) ? $found['element']['settings'] : array(),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 7: elementor_update_widget_content
     * ================================================================== */

    private function preview_elementor_update_widget_content( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id   = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $widget_id = isset( $args['widget_id'] ) ? sanitize_text_field( trim( (string) $args['widget_id'] ) ) : '';
        $content   = isset( $args['content'] ) ? (string) $args['content'] : '';

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $widget_id ) {
            return array( 'success' => false, 'error' => 'widget_id je obavezan.' );
        }
        if ( '' === $content ) {
            return array( 'success' => false, 'error' => 'content je obavezan.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen u Elementor strukturi.' );
        }

        $widget_type = isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : '';
        $supported   = array( 'heading', 'button', 'text-editor' );
        if ( ! in_array( $widget_type, $supported, true ) ) {
            return array( 'success' => false, 'error' => 'Widget tip "' . $widget_type . '" ne podrzava update content. Podrzani: ' . implode( ', ', $supported ) . '.' );
        }

        $post = get_post( $post_id );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_update_widget_content',
                'label'            => 'Update Elementor widget content',
                'summary'          => 'Azuriranje sadrzaja ' . $widget_type . ' widget-a (ID: ' . $widget_id . ') na stranici "' . ( $post ? $post->post_title : $post_id ) . '".',
                'rollback_plan'    => 'Vraca prethodni sadrzaj widget-a.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'post_id'    => $post_id,
                        'post_title' => $post ? $post->post_title : '',
                        'widget_id'  => $widget_id,
                    ),
                ),
                'args'             => array(
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                    'content'   => $content,
                ),
            ),
        );
    }

    private function execute_elementor_update_widget_content_action( $action ) {
        $args      = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $post_id   = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
        $widget_id = sanitize_text_field( isset( $args['widget_id'] ) ? (string) $args['widget_id'] : '' );
        $content   = isset( $args['content'] ) ? (string) $args['content'] : '';

        if ( $post_id <= 0 || '' === $widget_id || '' === $content ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_update_widget_content.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen.' );
        }

        $widget_type      = isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : '';
        $previous_settings = isset( $found['element']['settings'] ) && is_array( $found['element']['settings'] )
            ? $found['element']['settings']
            : array();

        $content_key_map = array(
            'heading'     => 'title',
            'button'      => 'text',
            'text-editor' => 'editor',
        );

        if ( ! isset( $content_key_map[ $widget_type ] ) ) {
            return array( 'success' => false, 'error' => 'Widget tip "' . $widget_type . '" ne podrzava content update.' );
        }

        $setting_key = $content_key_map[ $widget_type ];

        $updated = $this->update_element_in_tree( $data, $widget_id, function ( &$element ) use ( $setting_key, $content ) {
            if ( ! isset( $element['settings'] ) || ! is_array( $element['settings'] ) ) {
                $element['settings'] = array();
            }
            $element['settings'][ $setting_key ] = $content;
        } );

        if ( ! $updated ) {
            return array( 'success' => false, 'error' => 'Widget update u stablu nije uspeo.' );
        }

        $this->save_elementor_data( $post_id, $data );

        return array(
            'success'          => true,
            'summary'          => 'Azuriran sadrzaj ' . $widget_type . ' widget-a (ID: ' . $widget_id . ') na postu ' . $post_id . '.',
            'executed_actions' => array(
                array(
                    'type'      => 'elementor_update_widget_content',
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'              => 'elementor_widget_restore',
                    'post_id'           => $post_id,
                    'widget_id'         => $widget_id,
                    'previous_settings' => $previous_settings,
                ),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 8: elementor_update_widget_image
     * ================================================================== */

    private function preview_elementor_update_widget_image( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id   = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $widget_id = isset( $args['widget_id'] ) ? sanitize_text_field( trim( (string) $args['widget_id'] ) ) : '';
        $image_url = isset( $args['image_url'] ) ? esc_url_raw( (string) $args['image_url'] ) : '';

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $widget_id ) {
            return array( 'success' => false, 'error' => 'widget_id je obavezan.' );
        }
        if ( '' === $image_url ) {
            return array( 'success' => false, 'error' => 'image_url je obavezan.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen u Elementor strukturi.' );
        }

        $widget_type = isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : '';
        if ( 'image' !== $widget_type ) {
            return array( 'success' => false, 'error' => 'Widget tip "' . $widget_type . '" nije image widget. Samo image widgeti podrzavaju update slike.' );
        }

        $post = get_post( $post_id );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_update_widget_image',
                'label'            => 'Update Elementor image widget',
                'summary'          => 'Zamena slike u image widget-u (ID: ' . $widget_id . ') na stranici "' . ( $post ? $post->post_title : $post_id ) . '".',
                'rollback_plan'    => 'Vraca prethodnu sliku widget-a.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'post_id'    => $post_id,
                        'post_title' => $post ? $post->post_title : '',
                        'widget_id'  => $widget_id,
                    ),
                ),
                'args'             => array(
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                    'image_url' => $image_url,
                    'image_id'  => isset( $args['image_id'] ) ? intval( $args['image_id'] ) : 0,
                    'alt_text'  => isset( $args['alt_text'] ) ? sanitize_text_field( (string) $args['alt_text'] ) : '',
                ),
            ),
        );
    }

    private function execute_elementor_update_widget_image_action( $action ) {
        $args      = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $post_id   = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
        $widget_id = sanitize_text_field( isset( $args['widget_id'] ) ? (string) $args['widget_id'] : '' );
        $image_url = isset( $args['image_url'] ) ? esc_url_raw( (string) $args['image_url'] ) : '';
        $image_id  = isset( $args['image_id'] ) ? intval( $args['image_id'] ) : 0;
        $alt_text  = isset( $args['alt_text'] ) ? sanitize_text_field( (string) $args['alt_text'] ) : '';

        if ( $post_id <= 0 || '' === $widget_id || '' === $image_url ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_update_widget_image.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen.' );
        }

        $widget_type = isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : '';
        if ( 'image' !== $widget_type ) {
            return array( 'success' => false, 'error' => 'Widget tip "' . $widget_type . '" nije image widget.' );
        }

        $previous_settings = isset( $found['element']['settings'] ) && is_array( $found['element']['settings'] )
            ? $found['element']['settings']
            : array();

        $updated = $this->update_element_in_tree( $data, $widget_id, function ( &$element ) use ( $image_url, $image_id, $alt_text ) {
            if ( ! isset( $element['settings'] ) || ! is_array( $element['settings'] ) ) {
                $element['settings'] = array();
            }
            if ( ! isset( $element['settings']['image'] ) || ! is_array( $element['settings']['image'] ) ) {
                $element['settings']['image'] = array();
            }
            $element['settings']['image']['url'] = $image_url;
            if ( $image_id > 0 ) {
                $element['settings']['image']['id'] = $image_id;
            }
            if ( '' !== $alt_text ) {
                $element['settings']['image_alt'] = $alt_text;
            }
        } );

        if ( ! $updated ) {
            return array( 'success' => false, 'error' => 'Widget image update u stablu nije uspeo.' );
        }

        $this->save_elementor_data( $post_id, $data );

        return array(
            'success'          => true,
            'summary'          => 'Azurirana slika image widget-a (ID: ' . $widget_id . ') na postu ' . $post_id . '.',
            'executed_actions' => array(
                array(
                    'type'      => 'elementor_update_widget_image',
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'              => 'elementor_widget_restore',
                    'post_id'           => $post_id,
                    'widget_id'         => $widget_id,
                    'previous_settings' => $previous_settings,
                ),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 9: elementor_update_widget_link
     * ================================================================== */

    private function preview_elementor_update_widget_link( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id   = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $widget_id = isset( $args['widget_id'] ) ? sanitize_text_field( trim( (string) $args['widget_id'] ) ) : '';
        $url       = isset( $args['url'] ) ? esc_url_raw( (string) $args['url'] ) : '';

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $widget_id ) {
            return array( 'success' => false, 'error' => 'widget_id je obavezan.' );
        }
        if ( '' === $url ) {
            return array( 'success' => false, 'error' => 'url je obavezan.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen u Elementor strukturi.' );
        }

        $widget_type = isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : '';
        if ( 'button' !== $widget_type ) {
            return array( 'success' => false, 'error' => 'Widget tip "' . $widget_type . '" nije button widget. Samo button widgeti podrzavaju update linka.' );
        }

        $post = get_post( $post_id );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_update_widget_link',
                'label'            => 'Update Elementor button link',
                'summary'          => 'Azuriranje linka button widget-a (ID: ' . $widget_id . ') na stranici "' . ( $post ? $post->post_title : $post_id ) . '" na "' . $url . '".',
                'rollback_plan'    => 'Vraca prethodni link widget-a.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'post_id'    => $post_id,
                        'post_title' => $post ? $post->post_title : '',
                        'widget_id'  => $widget_id,
                    ),
                ),
                'args'             => array(
                    'post_id'     => $post_id,
                    'widget_id'   => $widget_id,
                    'url'         => $url,
                    'is_external' => ! empty( $args['is_external'] ),
                    'nofollow'    => ! empty( $args['nofollow'] ),
                ),
            ),
        );
    }

    private function execute_elementor_update_widget_link_action( $action ) {
        $args        = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $post_id     = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
        $widget_id   = sanitize_text_field( isset( $args['widget_id'] ) ? (string) $args['widget_id'] : '' );
        $url         = isset( $args['url'] ) ? esc_url_raw( (string) $args['url'] ) : '';
        $is_external = ! empty( $args['is_external'] );
        $nofollow    = ! empty( $args['nofollow'] );

        if ( $post_id <= 0 || '' === $widget_id || '' === $url ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_update_widget_link.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen.' );
        }

        $widget_type = isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : '';
        if ( 'button' !== $widget_type ) {
            return array( 'success' => false, 'error' => 'Widget tip "' . $widget_type . '" nije button widget.' );
        }

        $previous_settings = isset( $found['element']['settings'] ) && is_array( $found['element']['settings'] )
            ? $found['element']['settings']
            : array();

        $updated = $this->update_element_in_tree( $data, $widget_id, function ( &$element ) use ( $url, $is_external, $nofollow ) {
            if ( ! isset( $element['settings'] ) || ! is_array( $element['settings'] ) ) {
                $element['settings'] = array();
            }
            $element['settings']['link'] = array(
                'url'         => $url,
                'is_external' => $is_external ? 'on' : '',
                'nofollow'    => $nofollow ? 'on' : '',
            );
        } );

        if ( ! $updated ) {
            return array( 'success' => false, 'error' => 'Widget link update u stablu nije uspeo.' );
        }

        $this->save_elementor_data( $post_id, $data );

        return array(
            'success'          => true,
            'summary'          => 'Azuriran link button widget-a (ID: ' . $widget_id . ') na postu ' . $post_id . ' na "' . $url . '".',
            'executed_actions' => array(
                array(
                    'type'      => 'elementor_update_widget_link',
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'              => 'elementor_widget_restore',
                    'post_id'           => $post_id,
                    'widget_id'         => $widget_id,
                    'previous_settings' => $previous_settings,
                ),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 10: elementor_update_widget_styles
     * ================================================================== */

    private function preview_elementor_update_widget_styles( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id   = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $widget_id = isset( $args['widget_id'] ) ? sanitize_text_field( trim( (string) $args['widget_id'] ) ) : '';
        $styles    = isset( $args['styles'] ) && is_array( $args['styles'] ) ? $args['styles'] : array();

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $widget_id ) {
            return array( 'success' => false, 'error' => 'widget_id je obavezan.' );
        }
        if ( empty( $styles ) ) {
            return array( 'success' => false, 'error' => 'styles objekat je obavezan i ne sme biti prazan.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen u Elementor strukturi.' );
        }

        $style_changes = array();
        if ( ! empty( $styles['colors'] ) && is_array( $styles['colors'] ) ) {
            $style_changes[] = 'boje (' . count( $styles['colors'] ) . ')';
        }
        if ( ! empty( $styles['typography'] ) && is_array( $styles['typography'] ) ) {
            $style_changes[] = 'tipografija (' . count( $styles['typography'] ) . ')';
        }
        if ( ! empty( $styles['padding'] ) && is_array( $styles['padding'] ) ) {
            $style_changes[] = 'padding';
        }
        if ( ! empty( $styles['margin'] ) && is_array( $styles['margin'] ) ) {
            $style_changes[] = 'margin';
        }

        $post = get_post( $post_id );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_update_widget_styles',
                'label'            => 'Update Elementor widget styles',
                'summary'          => 'Azuriranje stilova widget-a (ID: ' . $widget_id . ') na stranici "' . ( $post ? $post->post_title : $post_id ) . '": ' . implode( ', ', $style_changes ) . '.',
                'rollback_plan'    => 'Vraca prethodne stilove widget-a.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'post_id'    => $post_id,
                        'post_title' => $post ? $post->post_title : '',
                        'widget_id'  => $widget_id,
                    ),
                ),
                'args'             => array(
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                    'styles'    => $styles,
                ),
            ),
        );
    }

    private function execute_elementor_update_widget_styles_action( $action ) {
        $args      = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $post_id   = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
        $widget_id = sanitize_text_field( isset( $args['widget_id'] ) ? (string) $args['widget_id'] : '' );
        $styles    = isset( $args['styles'] ) && is_array( $args['styles'] ) ? $args['styles'] : array();

        if ( $post_id <= 0 || '' === $widget_id || empty( $styles ) ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_update_widget_styles.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Widget (ID: ' . $widget_id . ') nije pronadjen.' );
        }

        $previous_settings = isset( $found['element']['settings'] ) && is_array( $found['element']['settings'] )
            ? $found['element']['settings']
            : array();

        $color_map = array(
            'text_color'               => 'color',
            'title_color'              => 'title_color',
            'background_color'         => 'background_color',
            'border_color'             => 'border_color',
            'button_text_color'        => 'button_text_color',
            'button_background_color'  => 'button_background_color',
        );

        $typography_map = array(
            'font_family'    => 'typography_font_family',
            'font_size'      => 'typography_font_size',
            'font_weight'    => 'typography_font_weight',
            'text_transform' => 'typography_text_transform',
        );

        $updated = $this->update_element_in_tree( $data, $widget_id, function ( &$element ) use ( $styles, $color_map, $typography_map ) {
            if ( ! isset( $element['settings'] ) || ! is_array( $element['settings'] ) ) {
                $element['settings'] = array();
            }

            // Apply colors.
            if ( ! empty( $styles['colors'] ) && is_array( $styles['colors'] ) ) {
                foreach ( $styles['colors'] as $color_key => $color_value ) {
                    if ( ! isset( $color_map[ $color_key ] ) ) {
                        continue;
                    }
                    $elementor_key = $color_map[ $color_key ];
                    $element['settings'][ $elementor_key ] = sanitize_text_field( (string) $color_value );

                    // Remove __globals__ reference when direct color is set.
                    if ( isset( $element['settings']['__globals__'] ) && is_array( $element['settings']['__globals__'] ) ) {
                        unset( $element['settings']['__globals__'][ $elementor_key ] );
                    }
                }
            }

            // Apply typography.
            if ( ! empty( $styles['typography'] ) && is_array( $styles['typography'] ) ) {
                // Enable typography override.
                $element['settings']['typography_typography'] = 'custom';

                foreach ( $styles['typography'] as $typo_key => $typo_value ) {
                    if ( ! isset( $typography_map[ $typo_key ] ) ) {
                        continue;
                    }
                    $elementor_key = $typography_map[ $typo_key ];

                    // font_size is an object with size and unit.
                    if ( 'font_size' === $typo_key && is_array( $typo_value ) ) {
                        $element['settings'][ $elementor_key ] = array(
                            'size' => isset( $typo_value['size'] ) ? floatval( $typo_value['size'] ) : 16,
                            'unit' => isset( $typo_value['unit'] ) ? sanitize_text_field( (string) $typo_value['unit'] ) : 'px',
                        );
                    } else {
                        $element['settings'][ $elementor_key ] = sanitize_text_field( (string) $typo_value );
                    }

                    // Remove __globals__ reference for typography.
                    if ( isset( $element['settings']['__globals__'] ) && is_array( $element['settings']['__globals__'] ) ) {
                        unset( $element['settings']['__globals__'][ $elementor_key ] );
                    }
                }
            }

            // Apply padding.
            if ( ! empty( $styles['padding'] ) && is_array( $styles['padding'] ) ) {
                $padding = array(
                    'top'      => isset( $styles['padding']['top'] ) ? (string) $styles['padding']['top'] : '',
                    'right'    => isset( $styles['padding']['right'] ) ? (string) $styles['padding']['right'] : '',
                    'bottom'   => isset( $styles['padding']['bottom'] ) ? (string) $styles['padding']['bottom'] : '',
                    'left'     => isset( $styles['padding']['left'] ) ? (string) $styles['padding']['left'] : '',
                    'unit'     => isset( $styles['padding']['unit'] ) ? sanitize_text_field( (string) $styles['padding']['unit'] ) : 'px',
                    'isLinked' => false,
                );
                $element['settings']['padding'] = $padding;
            }

            // Apply margin.
            if ( ! empty( $styles['margin'] ) && is_array( $styles['margin'] ) ) {
                $margin = array(
                    'top'      => isset( $styles['margin']['top'] ) ? (string) $styles['margin']['top'] : '',
                    'right'    => isset( $styles['margin']['right'] ) ? (string) $styles['margin']['right'] : '',
                    'bottom'   => isset( $styles['margin']['bottom'] ) ? (string) $styles['margin']['bottom'] : '',
                    'left'     => isset( $styles['margin']['left'] ) ? (string) $styles['margin']['left'] : '',
                    'unit'     => isset( $styles['margin']['unit'] ) ? sanitize_text_field( (string) $styles['margin']['unit'] ) : 'px',
                    'isLinked' => false,
                );
                $element['settings']['margin'] = $margin;
            }
        } );

        if ( ! $updated ) {
            return array( 'success' => false, 'error' => 'Widget styles update u stablu nije uspeo.' );
        }

        $this->save_elementor_data( $post_id, $data );

        $changes_summary = array();
        if ( ! empty( $styles['colors'] ) )     { $changes_summary[] = 'boje'; }
        if ( ! empty( $styles['typography'] ) )  { $changes_summary[] = 'tipografija'; }
        if ( ! empty( $styles['padding'] ) )     { $changes_summary[] = 'padding'; }
        if ( ! empty( $styles['margin'] ) )      { $changes_summary[] = 'margin'; }

        return array(
            'success'          => true,
            'summary'          => 'Azurirani stilovi (' . implode( ', ', $changes_summary ) . ') widget-a (ID: ' . $widget_id . ') na postu ' . $post_id . '.',
            'executed_actions' => array(
                array(
                    'type'      => 'elementor_update_widget_styles',
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'              => 'elementor_widget_restore',
                    'post_id'           => $post_id,
                    'widget_id'         => $widget_id,
                    'previous_settings' => $previous_settings,
                ),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 11: elementor_update_widget_settings
     * ================================================================== */

    private function preview_elementor_update_widget_settings( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id    = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $widget_id  = isset( $args['widget_id'] ) ? sanitize_text_field( trim( (string) $args['widget_id'] ) ) : '';
        $settings   = isset( $args['settings'] ) && is_array( $args['settings'] ) ? $args['settings'] : array();
        $unset_keys  = isset( $args['unset_keys'] ) && is_array( $args['unset_keys'] ) ? array_values( array_filter( array_map( 'strval', $args['unset_keys'] ) ) ) : array();
        $mode        = isset( $args['mode'] ) && 'replace' === (string) $args['mode'] ? 'replace' : 'merge';

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $widget_id ) {
            return array( 'success' => false, 'error' => 'widget_id je obavezan.' );
        }
        if ( empty( $settings ) && empty( $unset_keys ) ) {
            return array( 'success' => false, 'error' => 'settings ili unset_keys su obavezni.' );
        }
        if ( strlen( wp_json_encode( $settings ) ) > 200000 ) {
            return array( 'success' => false, 'error' => 'settings payload je prevelik za granularni Elementor update.' );
        }

        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Element (ID: ' . $widget_id . ') nije pronadjen u Elementor strukturi.' );
        }

        $post         = get_post( $post_id );
        $setting_keys = array_keys( $settings );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_update_widget_settings',
                'label'            => 'Update Elementor element settings',
                'summary'          => 'Precizno ' . $mode . ' azuriranje settings-a elementa (ID: ' . $widget_id . ') na stranici "' . ( $post ? $post->post_title : $post_id ) . '": ' . count( $setting_keys ) . ' key(s), unset ' . count( $unset_keys ) . '.',
                'rollback_plan'    => 'Vraca prethodne settings-e tog elementa.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'post_id'      => $post_id,
                        'post_title'   => $post ? $post->post_title : '',
                        'widget_id'    => $widget_id,
                        'setting_keys' => $setting_keys,
                        'unset_keys'   => $unset_keys,
                        'mode'         => $mode,
                    ),
                ),
                'args'             => array(
                    'post_id'    => $post_id,
                    'widget_id'  => $widget_id,
                    'settings'   => $settings,
                    'unset_keys' => $unset_keys,
                    'mode'       => $mode,
                ),
            ),
        );
    }

    private function execute_elementor_update_widget_settings_action( $action ) {
        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $post_id    = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
        $widget_id  = sanitize_text_field( isset( $args['widget_id'] ) ? (string) $args['widget_id'] : '' );
        $settings   = isset( $args['settings'] ) && is_array( $args['settings'] ) ? $args['settings'] : array();
        $unset_keys  = isset( $args['unset_keys'] ) && is_array( $args['unset_keys'] ) ? array_values( array_filter( array_map( 'strval', $args['unset_keys'] ) ) ) : array();
        $mode        = isset( $args['mode'] ) && 'replace' === (string) $args['mode'] ? 'replace' : 'merge';

        if ( $post_id <= 0 || '' === $widget_id || ( empty( $settings ) && empty( $unset_keys ) ) ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_update_widget_settings.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $widget_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Element (ID: ' . $widget_id . ') nije pronadjen.' );
        }

        $previous_settings = isset( $found['element']['settings'] ) && is_array( $found['element']['settings'] )
            ? $found['element']['settings']
            : array();

        $updated = $this->update_element_in_tree( $data, $widget_id, function ( &$element ) use ( $settings, $unset_keys, $mode ) {
            if ( ! isset( $element['settings'] ) || ! is_array( $element['settings'] ) || 'replace' === $mode ) {
                $element['settings'] = array();
            }

            if ( ! empty( $settings ) ) {
                $element['settings'] = $this->merge_elementor_settings( $element['settings'], $settings );
            }

            foreach ( $unset_keys as $unset_key ) {
                $this->unset_elementor_setting_path( $element['settings'], $unset_key );
            }
        } );

        if ( ! $updated ) {
            return array( 'success' => false, 'error' => 'Element settings update u stablu nije uspeo.' );
        }

        $this->save_elementor_data( $post_id, $data );

        return array(
            'success'          => true,
            'summary'          => 'Azurirani Elementor settings-i elementa (ID: ' . $widget_id . ') na postu ' . $post_id . '.',
            'executed_actions' => array(
                array(
                    'type'      => 'elementor_update_widget_settings',
                    'post_id'   => $post_id,
                    'widget_id' => $widget_id,
                    'mode'      => $mode,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'              => 'elementor_widget_restore',
                    'post_id'           => $post_id,
                    'widget_id'         => $widget_id,
                    'previous_settings' => $previous_settings,
                ),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 12: elementor_duplicate_element
     * ================================================================== */

    private function preview_elementor_duplicate_element( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id    = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $element_id = isset( $args['element_id'] ) ? sanitize_text_field( trim( (string) $args['element_id'] ) ) : '';
        $position   = isset( $args['position'] ) && in_array( (string) $args['position'], array( 'before', 'after' ), true ) ? (string) $args['position'] : 'after';

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $element_id ) {
            return array( 'success' => false, 'error' => 'element_id je obavezan.' );
        }
        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $data = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan za post ID ' . $post_id . '.' );
        }

        $found = $this->find_element_by_id_in_tree( $data, $element_id );
        if ( null === $found ) {
            return array( 'success' => false, 'error' => 'Element (ID: ' . $element_id . ') nije pronadjen u Elementor strukturi.' );
        }

        $post = get_post( $post_id );
        $type = isset( $found['element']['widgetType'] ) ? (string) $found['element']['widgetType'] : ( isset( $found['element']['elType'] ) ? (string) $found['element']['elType'] : 'element' );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_duplicate_element',
                'label'            => 'Duplicate Elementor element',
                'summary'          => 'Dupliranje Elementor ' . $type . ' elementa (ID: ' . $element_id . ') ' . $position . ' originala na stranici "' . ( $post ? $post->post_title : $post_id ) . '".',
                'rollback_plan'    => 'Vraca prethodni kompletan _elementor_data za stranicu.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'post_id'    => $post_id,
                        'post_title' => $post ? $post->post_title : '',
                        'element_id' => $element_id,
                        'position'   => $position,
                    ),
                ),
                'args'             => array(
                    'post_id'    => $post_id,
                    'element_id' => $element_id,
                    'position'   => $position,
                ),
            ),
        );
    }

    private function execute_elementor_duplicate_element_action( $action ) {
        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $post_id    = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
        $element_id = sanitize_text_field( isset( $args['element_id'] ) ? (string) $args['element_id'] : '' );
        $position   = isset( $args['position'] ) && in_array( (string) $args['position'], array( 'before', 'after' ), true ) ? (string) $args['position'] : 'after';

        if ( $post_id <= 0 || '' === $element_id ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_duplicate_element.' );
        }

        $previous_raw = (string) get_post_meta( $post_id, '_elementor_data', true );
        $data         = $this->get_elementor_data( $post_id );
        if ( null === $data ) {
            return array( 'success' => false, 'error' => '_elementor_data je prazan ili neispravan.' );
        }

        $new_id     = '';
        $duplicated = $this->duplicate_element_in_tree( $data, $element_id, $position, $new_id );
        if ( ! $duplicated ) {
            return array( 'success' => false, 'error' => 'Element (ID: ' . $element_id . ') nije pronadjen za dupliranje.' );
        }

        $this->save_elementor_data( $post_id, $data );

        return array(
            'success'          => true,
            'summary'          => 'Dupliran Elementor element (ID: ' . $element_id . ') kao novi element (ID: ' . $new_id . ') na postu ' . $post_id . '.',
            'data'             => array(
                'post_id'     => $post_id,
                'element_id'  => $element_id,
                'new_element_id' => $new_id,
                'position'    => $position,
            ),
            'executed_actions' => array(
                array(
                    'type'           => 'elementor_duplicate_element',
                    'post_id'        => $post_id,
                    'element_id'     => $element_id,
                    'new_element_id' => $new_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'                     => 'elementor_page_data_restore',
                    'post_id'                  => $post_id,
                    'previous_elementor_data'  => $previous_raw,
                    'previous_post_content'    => (string) get_post_field( 'post_content', $post_id ),
                    'post_content_was_changed' => false,
                ),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 13: elementor_update_kit_settings
     * ================================================================== */

    private function preview_elementor_update_kit_settings( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $kit_id     = isset( $args['kit_id'] ) ? intval( $args['kit_id'] ) : intval( get_option( 'elementor_active_kit', 0 ) );
        $settings   = isset( $args['settings'] ) && is_array( $args['settings'] ) ? $args['settings'] : array();
        $unset_keys  = isset( $args['unset_keys'] ) && is_array( $args['unset_keys'] ) ? array_values( array_filter( array_map( 'strval', $args['unset_keys'] ) ) ) : array();
        $mode        = isset( $args['mode'] ) && 'replace' === (string) $args['mode'] ? 'replace' : 'merge';

        if ( $kit_id <= 0 ) {
            return array( 'success' => false, 'error' => 'kit_id nije pronadjen. Prosledi kit_id ili proveri elementor_active_kit.' );
        }
        if ( empty( $settings ) && empty( $unset_keys ) ) {
            return array( 'success' => false, 'error' => 'settings ili unset_keys su obavezni.' );
        }
        if ( strlen( wp_json_encode( $settings ) ) > 200000 ) {
            return array( 'success' => false, 'error' => 'settings payload je prevelik za Elementor kit update.' );
        }

        $kit_post = get_post( $kit_id );
        if ( ! $kit_post ) {
            return array( 'success' => false, 'error' => 'Elementor kit (ID: ' . $kit_id . ') nije pronadjen.' );
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_update_kit_settings',
                'label'            => 'Update Elementor kit settings',
                'summary'          => 'Precizno ' . $mode . ' azuriranje Elementor kit settings-a za "' . (string) $kit_post->post_title . '": ' . count( $settings ) . ' key(s), unset ' . count( $unset_keys ) . '.',
                'rollback_plan'    => 'Vraca prethodne _elementor_page_settings aktivnog kit-a.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'kit_id'       => $kit_id,
                        'kit_title'    => (string) $kit_post->post_title,
                        'setting_keys' => array_keys( $settings ),
                        'unset_keys'   => $unset_keys,
                        'mode'         => $mode,
                    ),
                ),
                'args'             => array(
                    'kit_id'     => $kit_id,
                    'settings'   => $settings,
                    'unset_keys' => $unset_keys,
                    'mode'       => $mode,
                ),
            ),
        );
    }

    private function execute_elementor_update_kit_settings_action( $action ) {
        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $kit_id     = intval( isset( $args['kit_id'] ) ? $args['kit_id'] : 0 );
        $settings   = isset( $args['settings'] ) && is_array( $args['settings'] ) ? $args['settings'] : array();
        $unset_keys  = isset( $args['unset_keys'] ) && is_array( $args['unset_keys'] ) ? array_values( array_filter( array_map( 'strval', $args['unset_keys'] ) ) ) : array();
        $mode        = isset( $args['mode'] ) && 'replace' === (string) $args['mode'] ? 'replace' : 'merge';

        if ( $kit_id <= 0 || ( empty( $settings ) && empty( $unset_keys ) ) ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_update_kit_settings.' );
        }

        $previous_settings = get_post_meta( $kit_id, '_elementor_page_settings', true );
        if ( ! is_array( $previous_settings ) ) {
            $previous_settings = array();
        }

        $updated_settings = 'replace' === $mode ? array() : $previous_settings;
        if ( ! empty( $settings ) ) {
            $updated_settings = $this->merge_elementor_settings( $updated_settings, $settings );
        }
        foreach ( $unset_keys as $unset_key ) {
            $this->unset_elementor_setting_path( $updated_settings, $unset_key );
        }

        update_post_meta( $kit_id, '_elementor_page_settings', $updated_settings );
        $this->clear_elementor_cache_for_post( $kit_id );

        return array(
            'success'          => true,
            'summary'          => 'Azurirani Elementor kit settings-i za kit ' . $kit_id . '.',
            'executed_actions' => array(
                array(
                    'type'   => 'elementor_update_kit_settings',
                    'kit_id' => $kit_id,
                    'mode'   => $mode,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'                   => 'elementor_kit_settings_restore',
                    'kit_id'                 => $kit_id,
                    'previous_page_settings' => $previous_settings,
                ),
            ),
        );
    }

    /* ==================================================================
     *  WRITE TOOL 14: elementor_update_page_data
     * ================================================================== */

    private function preview_elementor_update_page_data( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor plugin nije aktivan.' );
        }

        $post_id            = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
        $elementor_data_json = isset( $args['elementor_data_json'] ) ? (string) $args['elementor_data_json'] : '';
        $expected_sha256    = isset( $args['expected_current_sha256'] ) ? strtolower( trim( (string) $args['expected_current_sha256'] ) ) : '';
        $has_post_content   = array_key_exists( 'post_content', $args );

        if ( $post_id <= 0 ) {
            return array( 'success' => false, 'error' => 'post_id je obavezan.' );
        }
        if ( '' === $elementor_data_json ) {
            return array( 'success' => false, 'error' => 'elementor_data_json je obavezan.' );
        }

        $post = get_post( $post_id );
        if ( ! $post ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') nije pronadjen.' );
        }
        if ( ! $this->is_elementor_post( $post_id ) ) {
            return array( 'success' => false, 'error' => 'Post (ID: ' . $post_id . ') ne koristi Elementor builder.' );
        }

        $decoded = json_decode( $elementor_data_json, true );
        if ( ! is_array( $decoded ) || JSON_ERROR_NONE !== json_last_error() ) {
            return array( 'success' => false, 'error' => 'elementor_data_json nije validan Elementor JSON.' );
        }

        $current_raw    = (string) get_post_meta( $post_id, '_elementor_data', true );
        $current_sha256 = hash( 'sha256', $current_raw );
        if ( '' !== $expected_sha256 && $expected_sha256 !== $current_sha256 ) {
            return array(
                'success'        => false,
                'error'          => 'Trenutni _elementor_data SHA-256 se ne poklapa sa expected_current_sha256.',
                'current_sha256' => $current_sha256,
            );
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_update_page_data',
                'label'            => 'Update full Elementor page data',
                'summary'          => 'Zamena celog _elementor_data JSON-a za "' . (string) $post->post_title . '"' . ( $has_post_content ? ' uz update post_content fallback-a.' : '.' ),
                'rollback_plan'    => 'Vraca prethodni _elementor_data i prethodni post_content ako je menjan.',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'post_id'        => $post_id,
                        'post_title'     => (string) $post->post_title,
                        'current_sha256' => $current_sha256,
                    ),
                ),
                'args'             => array(
                    'post_id'                 => $post_id,
                    'elementor_data_json'     => $elementor_data_json,
                    'expected_current_sha256' => $expected_sha256,
                    'post_content'            => $has_post_content ? (string) $args['post_content'] : null,
                    'has_post_content'        => $has_post_content,
                ),
            ),
        );
    }

    private function execute_elementor_update_page_data_action( $action ) {
        $args                = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $post_id             = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
        $elementor_data_json = isset( $args['elementor_data_json'] ) ? (string) $args['elementor_data_json'] : '';
        $expected_sha256     = isset( $args['expected_current_sha256'] ) ? strtolower( trim( (string) $args['expected_current_sha256'] ) ) : '';
        $has_post_content    = ! empty( $args['has_post_content'] );

        if ( $post_id <= 0 || '' === $elementor_data_json ) {
            return array( 'success' => false, 'error' => 'Nedovoljno podataka za elementor_update_page_data.' );
        }

        $post = get_post( $post_id );
        if ( ! $post ) {
            return array( 'success' => false, 'error' => 'Post nije pronadjen.' );
        }

        $decoded = json_decode( $elementor_data_json, true );
        if ( ! is_array( $decoded ) || JSON_ERROR_NONE !== json_last_error() ) {
            return array( 'success' => false, 'error' => 'elementor_data_json nije validan JSON.' );
        }

        $previous_raw      = (string) get_post_meta( $post_id, '_elementor_data', true );
        $previous_sha256   = hash( 'sha256', $previous_raw );
        $previous_content  = (string) $post->post_content;
        if ( '' !== $expected_sha256 && $expected_sha256 !== $previous_sha256 ) {
            return array(
                'success'        => false,
                'error'          => 'Trenutni _elementor_data SHA-256 se promenio pre upisa.',
                'current_sha256' => $previous_sha256,
            );
        }

        update_post_meta( $post_id, '_niwa_elementor_data_backup', wp_slash( $previous_raw ) );
        update_post_meta( $post_id, '_niwa_elementor_content_backup', wp_slash( $previous_content ) );
        $this->save_elementor_data( $post_id, $decoded );

        if ( $has_post_content ) {
            $updated = wp_update_post(
                array(
                    'ID'           => $post_id,
                    'post_content' => isset( $args['post_content'] ) ? (string) $args['post_content'] : '',
                ),
                true
            );
            if ( is_wp_error( $updated ) ) {
                update_post_meta( $post_id, '_elementor_data', wp_slash( $previous_raw ) );
                delete_post_meta( $post_id, '_elementor_css' );
                delete_post_meta( $post_id, '_elementor_element_cache' );
                clean_post_cache( $post_id );
                return array(
                    'success' => false,
                    'error'   => $updated->get_error_message(),
                );
            }
            clean_post_cache( $post_id );
        }

        $stored_raw = (string) get_post_meta( $post_id, '_elementor_data', true );

        return array(
            'success'          => true,
            'summary'          => 'Azuriran je _elementor_data za post ' . $post_id . ( $has_post_content ? ' i post_content fallback.' : '.' ),
            'current_sha256'   => hash( 'sha256', $stored_raw ),
            'executed_actions' => array(
                array(
                    'type'    => 'elementor_update_page_data',
                    'post_id' => $post_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'                     => 'elementor_page_data_restore',
                    'post_id'                  => $post_id,
                    'previous_elementor_data'  => $previous_raw,
                    'previous_post_content'    => $previous_content,
                    'post_content_was_changed' => $has_post_content,
                ),
            ),
        );
    }

    /* ==================================================================
     *  HELPER: Check if post uses Elementor
     * ================================================================== */

    private function is_elementor_post( $post_id ) {
        return 'builder' === get_post_meta( $post_id, '_elementor_edit_mode', true );
    }

    /* ==================================================================
     *  HELPER: Get decoded Elementor data
     * ================================================================== */

    private function get_elementor_data( $post_id ) {
        $raw = get_post_meta( $post_id, '_elementor_data', true );
        if ( empty( $raw ) ) {
            return null;
        }
        $decoded = json_decode( $raw, true );
        if ( ! is_array( $decoded ) ) {
            return null;
        }
        return $decoded;
    }

    /* ==================================================================
     *  HELPER: Save Elementor data back
     * ================================================================== */

    private function save_elementor_data( $post_id, $data ) {
        $json = wp_json_encode( $data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
        update_post_meta( $post_id, '_elementor_data', wp_slash( $json ) );

        $this->clear_elementor_cache_for_post( $post_id );
    }

    /**
     * Clear Elementor-generated caches after direct meta edits.
     *
     * @param int $post_id Post or kit ID.
     */
    private function clear_elementor_cache_for_post( $post_id ) {
        // Clear Elementor CSS caches.
        delete_post_meta( $post_id, '_elementor_css' );
        delete_post_meta( $post_id, '_elementor_element_cache' );

        $css_file = wp_upload_dir()['basedir'] . '/elementor/css/post-' . intval( $post_id ) . '.css';
        if ( file_exists( $css_file ) ) {
            @unlink( $css_file );
        }

        if ( class_exists( '\Elementor\Plugin' ) ) {
            \Elementor\Plugin::$instance->files_manager->clear_cache();
        }

        // Clear WP object cache.
        wp_cache_delete( $post_id, 'posts' );
        clean_post_cache( $post_id );
    }

    /**
     * Merge Elementor settings while replacing numeric arrays as Elementor control lists.
     *
     * @param array $base  Existing settings.
     * @param array $patch Incoming settings patch.
     * @return array
     */
    private function merge_elementor_settings( $base, $patch ) {
        foreach ( $patch as $key => $value ) {
            if ( is_array( $value ) && isset( $base[ $key ] ) && is_array( $base[ $key ] ) && ! $this->is_list_array( $value ) && ! $this->is_list_array( $base[ $key ] ) ) {
                $base[ $key ] = $this->merge_elementor_settings( $base[ $key ], $value );
            } else {
                $base[ $key ] = $value;
            }
        }
        return $base;
    }

    /**
     * Remove a direct setting key or dotted nested path.
     *
     * @param array  &$settings Elementor settings.
     * @param string $path      Setting key or dotted path.
     */
    private function unset_elementor_setting_path( &$settings, $path ) {
        $path = trim( (string) $path );
        if ( '' === $path || ! is_array( $settings ) ) {
            return;
        }
        if ( false === strpos( $path, '.' ) ) {
            unset( $settings[ $path ] );
            return;
        }

        $parts  = array_values( array_filter( explode( '.', $path ), 'strlen' ) );
        $cursor = &$settings;
        $last   = array_pop( $parts );
        foreach ( $parts as $part ) {
            if ( ! isset( $cursor[ $part ] ) || ! is_array( $cursor[ $part ] ) ) {
                return;
            }
            $cursor = &$cursor[ $part ];
        }
        if ( null !== $last ) {
            unset( $cursor[ $last ] );
        }
    }

    /**
     * Determine whether an array is list-like without requiring PHP 8.1 array_is_list.
     *
     * @param array $array Candidate array.
     * @return bool
     */
    private function is_list_array( $array ) {
        if ( ! is_array( $array ) ) {
            return false;
        }
        $expected = 0;
        foreach ( array_keys( $array ) as $key ) {
            if ( $key !== $expected++ ) {
                return false;
            }
        }
        return true;
    }

    /* ==================================================================
     *  HELPER: Recursive find element by ID in tree
     * ================================================================== */

    /**
     * Find an element by its ID in the Elementor element tree.
     *
     * @param array  $elements  Elementor elements array.
     * @param string $widget_id The element ID to find.
     * @param array  $path      Current path (for tracking nesting).
     * @param int    $depth     Current recursion depth.
     * @return array|null       Array with 'element', 'depth', 'path' keys, or null if not found.
     */
    private function find_element_by_id_in_tree( $elements, $widget_id, $path = array(), $depth = 0 ) {
        if ( ! is_array( $elements ) || $depth > 20 ) {
            return null;
        }

        foreach ( $elements as $index => $element ) {
            if ( ! is_array( $element ) ) {
                continue;
            }

            $element_id = isset( $element['id'] ) ? (string) $element['id'] : '';
            $current_path = $path;
            $current_path[] = array(
                'id'    => $element_id,
                'type'  => isset( $element['elType'] ) ? (string) $element['elType'] : 'unknown',
                'index' => $index,
            );

            if ( $element_id === $widget_id ) {
                return array(
                    'element' => $element,
                    'depth'   => $depth,
                    'path'    => $current_path,
                );
            }

            if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
                $found = $this->find_element_by_id_in_tree( $element['elements'], $widget_id, $current_path, $depth + 1 );
                if ( null !== $found ) {
                    return $found;
                }
            }
        }

        return null;
    }

    /* ==================================================================
     *  HELPER: Recursive update element in tree (by reference)
     * ================================================================== */

    /**
     * Update an element in the Elementor tree by ID using a callback.
     *
     * @param array    &$elements  Elementor elements array (modified in place).
     * @param string   $widget_id  The element ID to update.
     * @param callable $callback   Callback receiving &$element to modify it.
     * @param int      $depth      Current recursion depth.
     * @return bool                True if element was found and updated.
     */
    private function update_element_in_tree( &$elements, $widget_id, $callback, $depth = 0 ) {
        if ( ! is_array( $elements ) || $depth > 20 ) {
            return false;
        }

        foreach ( $elements as &$element ) {
            if ( ! is_array( $element ) ) {
                continue;
            }

            $element_id = isset( $element['id'] ) ? (string) $element['id'] : '';
            if ( $element_id === $widget_id ) {
                $callback( $element );
                return true;
            }

            if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
                $found = $this->update_element_in_tree( $element['elements'], $widget_id, $callback, $depth + 1 );
                if ( $found ) {
                    return true;
                }
            }
        }
        unset( $element );

        return false;
    }

    /**
     * Duplicate an element next to its original and regenerate all Elementor IDs in the clone.
     *
     * @param array  &$elements   Elementor element tree.
     * @param string $element_id  Source element ID.
     * @param string $position    before|after.
     * @param string &$new_id     Receives the cloned root ID.
     * @param int    $depth       Recursion guard.
     * @return bool
     */
    private function duplicate_element_in_tree( &$elements, $element_id, $position, &$new_id, $depth = 0 ) {
        if ( ! is_array( $elements ) || $depth > 20 ) {
            return false;
        }

        foreach ( $elements as $index => &$element ) {
            if ( ! is_array( $element ) ) {
                continue;
            }

            if ( isset( $element['id'] ) && (string) $element['id'] === $element_id ) {
                $clone = $element;
                $this->regenerate_elementor_element_ids( $clone );
                $new_id = isset( $clone['id'] ) ? (string) $clone['id'] : '';
                $insert_at = 'before' === $position ? $index : $index + 1;
                array_splice( $elements, $insert_at, 0, array( $clone ) );
                unset( $element );
                return true;
            }

            if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
                $found = $this->duplicate_element_in_tree( $element['elements'], $element_id, $position, $new_id, $depth + 1 );
                if ( $found ) {
                    unset( $element );
                    return true;
                }
            }
        }
        unset( $element );

        return false;
    }

    /**
     * Regenerate IDs in a cloned Elementor element subtree.
     *
     * @param array &$element Elementor element.
     */
    private function regenerate_elementor_element_ids( &$element ) {
        if ( ! is_array( $element ) ) {
            return;
        }
        if ( isset( $element['id'] ) ) {
            $element['id'] = $this->generate_elementor_element_id();
        }
        if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
            foreach ( $element['elements'] as &$child ) {
                $this->regenerate_elementor_element_ids( $child );
            }
            unset( $child );
        }
    }

    /**
     * Generate a compact Elementor-compatible element ID.
     *
     * @return string
     */
    private function generate_elementor_element_id() {
        try {
            return substr( bin2hex( random_bytes( 4 ) ), 0, 7 );
        } catch ( Exception $e ) {
            return substr( md5( uniqid( 'niwa_elementor_', true ) ), 0, 7 );
        }
    }

    /* ==================================================================
     *  HELPER: Build structure tree for display
     * ================================================================== */

    /**
     * Build a human-readable structure tree from Elementor data.
     *
     * @param array $elements         Elementor elements array.
     * @param int   $depth            Current depth.
     * @param int   $max_depth        Maximum depth to traverse.
     * @param bool  $include_settings Whether to include full settings.
     * @return array                  Array of structure nodes.
     */
    private function build_elementor_structure( $elements, $depth = 0, $max_depth = 10, $include_settings = false ) {
        if ( ! is_array( $elements ) || $depth >= $max_depth ) {
            return array();
        }

        $result = array();

        foreach ( $elements as $element ) {
            if ( ! is_array( $element ) ) {
                continue;
            }

            $el_type     = isset( $element['elType'] ) ? (string) $element['elType'] : 'unknown';
            $widget_type = isset( $element['widgetType'] ) ? (string) $element['widgetType'] : '';
            $children    = isset( $element['elements'] ) && is_array( $element['elements'] ) ? $element['elements'] : array();

            $node = array(
                'id'             => isset( $element['id'] ) ? (string) $element['id'] : '',
                'type'           => $el_type,
                'widget_type'    => $widget_type,
                'depth'          => $depth,
                'children_count' => count( $children ),
            );

            // Add content preview for text-bearing widgets.
            if ( 'widget' === $el_type && isset( $element['settings'] ) && is_array( $element['settings'] ) ) {
                $settings = $element['settings'];
                $content_preview = '';

                if ( 'heading' === $widget_type && isset( $settings['title'] ) ) {
                    $content_preview = wp_trim_words( wp_strip_all_tags( (string) $settings['title'] ), 15, '...' );
                } elseif ( 'text-editor' === $widget_type && isset( $settings['editor'] ) ) {
                    $content_preview = wp_trim_words( wp_strip_all_tags( (string) $settings['editor'] ), 15, '...' );
                } elseif ( 'button' === $widget_type && isset( $settings['text'] ) ) {
                    $content_preview = wp_trim_words( wp_strip_all_tags( (string) $settings['text'] ), 15, '...' );
                } elseif ( 'image' === $widget_type && isset( $settings['image'] ) && is_array( $settings['image'] ) ) {
                    $content_preview = isset( $settings['image']['url'] ) ? (string) $settings['image']['url'] : '';
                }

                if ( '' !== $content_preview ) {
                    $node['content_preview'] = $content_preview;
                }
            }

            if ( $include_settings && isset( $element['settings'] ) && is_array( $element['settings'] ) ) {
                $node['settings'] = $element['settings'];
            }

            // Recurse into children.
            if ( ! empty( $children ) ) {
                $node['children'] = $this->build_elementor_structure( $children, $depth + 1, $max_depth, $include_settings );
            }

            $result[] = $node;
        }

        return $result;
    }

    /* ==================================================================
     *  HELPER: Search widgets by type in tree
     * ================================================================== */

    /**
     * Search for widgets of specific types in the Elementor tree.
     *
     * @param array $elements         Elementor elements array.
     * @param array $widget_types     Array of widget type strings to match.
     * @param int   $max_depth        Maximum depth to traverse.
     * @param bool  $include_settings Whether to include full settings.
     * @param int   $depth            Current recursion depth.
     * @param array $path             Current path for tracking nesting.
     * @return array                  Array of found widgets.
     */
    private function search_widgets_in_tree( $elements, $widget_types, $max_depth = 10, $include_settings = true, $depth = 0, $path = array() ) {
        if ( ! is_array( $elements ) || $depth >= $max_depth ) {
            return array();
        }

        $found = array();

        foreach ( $elements as $index => $element ) {
            if ( ! is_array( $element ) ) {
                continue;
            }

            $el_type     = isset( $element['elType'] ) ? (string) $element['elType'] : '';
            $widget_type = isset( $element['widgetType'] ) ? (string) $element['widgetType'] : '';
            $element_id  = isset( $element['id'] ) ? (string) $element['id'] : '';

            $current_path = $path;
            $current_path[] = array(
                'id'    => $element_id,
                'type'  => $el_type,
                'index' => $index,
            );

            if ( 'widget' === $el_type && in_array( $widget_type, $widget_types, true ) ) {
                $widget_entry = array(
                    'id'          => $element_id,
                    'widget_type' => $widget_type,
                    'depth'       => $depth,
                    'path'        => $current_path,
                );

                if ( $include_settings && isset( $element['settings'] ) && is_array( $element['settings'] ) ) {
                    $widget_entry['settings'] = $element['settings'];
                }

                $found[] = $widget_entry;
            }

            // Recurse into children.
            if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
                $child_found = $this->search_widgets_in_tree( $element['elements'], $widget_types, $max_depth, $include_settings, $depth + 1, $current_path );
                $found = array_merge( $found, $child_found );
            }
        }

        return $found;
    }

    /* ==================================================================
     *  HELPER: Get Elementor kit data
     * ================================================================== */

    /**
     * Retrieve and parse Elementor kit data (colors and typography).
     *
     * @param int  $kit_id             The kit post ID.
     * @param bool $include_colors     Include color definitions.
     * @param bool $include_typography  Include typography definitions.
     * @return array                   Structured kit data or error.
     */
    private function get_elementor_kit_data( $kit_id, $include_colors = true, $include_typography = true ) {
        $kit_post = get_post( $kit_id );
        if ( ! $kit_post ) {
            return array( 'success' => false, 'error' => 'Elementor kit (ID: ' . $kit_id . ') nije pronadjen.' );
        }

        $page_settings = get_post_meta( $kit_id, '_elementor_page_settings', true );
        if ( ! is_array( $page_settings ) ) {
            $page_settings = array();
        }

        $result = array(
            'success'   => true,
            'kit_id'    => $kit_id,
            'kit_title' => $kit_post->post_title,
        );

        if ( $include_colors ) {
            $system_colors = array();
            if ( isset( $page_settings['system_colors'] ) && is_array( $page_settings['system_colors'] ) ) {
                foreach ( $page_settings['system_colors'] as $color ) {
                    if ( ! is_array( $color ) ) {
                        continue;
                    }
                    $system_colors[] = array(
                        'id'    => isset( $color['_id'] ) ? (string) $color['_id'] : '',
                        'title' => isset( $color['title'] ) ? (string) $color['title'] : '',
                        'color' => isset( $color['color'] ) ? (string) $color['color'] : '',
                    );
                }
            }

            $custom_colors = array();
            if ( isset( $page_settings['custom_colors'] ) && is_array( $page_settings['custom_colors'] ) ) {
                foreach ( $page_settings['custom_colors'] as $color ) {
                    if ( ! is_array( $color ) ) {
                        continue;
                    }
                    $custom_colors[] = array(
                        'id'    => isset( $color['_id'] ) ? (string) $color['_id'] : '',
                        'title' => isset( $color['title'] ) ? (string) $color['title'] : '',
                        'color' => isset( $color['color'] ) ? (string) $color['color'] : '',
                    );
                }
            }

            $result['colors'] = array(
                'system' => $system_colors,
                'custom' => $custom_colors,
            );
        }

        if ( $include_typography ) {
            $system_typography = array();
            if ( isset( $page_settings['system_typography'] ) && is_array( $page_settings['system_typography'] ) ) {
                foreach ( $page_settings['system_typography'] as $typo ) {
                    if ( ! is_array( $typo ) ) {
                        continue;
                    }
                    $typo_entry = array(
                        'id'    => isset( $typo['_id'] ) ? (string) $typo['_id'] : '',
                        'title' => isset( $typo['title'] ) ? (string) $typo['title'] : '',
                    );
                    if ( isset( $typo['typography_font_family'] ) ) {
                        $typo_entry['font_family'] = (string) $typo['typography_font_family'];
                    }
                    if ( isset( $typo['typography_font_size'] ) ) {
                        $typo_entry['font_size'] = $typo['typography_font_size'];
                    }
                    if ( isset( $typo['typography_font_weight'] ) ) {
                        $typo_entry['font_weight'] = (string) $typo['typography_font_weight'];
                    }
                    if ( isset( $typo['typography_text_transform'] ) ) {
                        $typo_entry['text_transform'] = (string) $typo['typography_text_transform'];
                    }
                    if ( isset( $typo['typography_line_height'] ) ) {
                        $typo_entry['line_height'] = $typo['typography_line_height'];
                    }
                    if ( isset( $typo['typography_letter_spacing'] ) ) {
                        $typo_entry['letter_spacing'] = $typo['typography_letter_spacing'];
                    }
                    $system_typography[] = $typo_entry;
                }
            }

            $custom_typography = array();
            if ( isset( $page_settings['custom_typography'] ) && is_array( $page_settings['custom_typography'] ) ) {
                foreach ( $page_settings['custom_typography'] as $typo ) {
                    if ( ! is_array( $typo ) ) {
                        continue;
                    }
                    $typo_entry = array(
                        'id'    => isset( $typo['_id'] ) ? (string) $typo['_id'] : '',
                        'title' => isset( $typo['title'] ) ? (string) $typo['title'] : '',
                    );
                    if ( isset( $typo['typography_font_family'] ) ) {
                        $typo_entry['font_family'] = (string) $typo['typography_font_family'];
                    }
                    if ( isset( $typo['typography_font_size'] ) ) {
                        $typo_entry['font_size'] = $typo['typography_font_size'];
                    }
                    if ( isset( $typo['typography_font_weight'] ) ) {
                        $typo_entry['font_weight'] = (string) $typo['typography_font_weight'];
                    }
                    if ( isset( $typo['typography_text_transform'] ) ) {
                        $typo_entry['text_transform'] = (string) $typo['typography_text_transform'];
                    }
                    if ( isset( $typo['typography_line_height'] ) ) {
                        $typo_entry['line_height'] = $typo['typography_line_height'];
                    }
                    if ( isset( $typo['typography_letter_spacing'] ) ) {
                        $typo_entry['letter_spacing'] = $typo['typography_letter_spacing'];
                    }
                    $custom_typography[] = $typo_entry;
                }
            }

            $result['typography'] = array(
                'system' => $system_typography,
                'custom' => $custom_typography,
            );
        }

        return $result;
    }

    /* ==================================================================
     *  READ-ONLY TOOL: elementor_search_text
     *  Searches ALL Elementor pages for a text string.
     *  Returns page ID, widget ID, widget type, and text context.
     * ================================================================== */

    private function preview_elementor_search_text( $args ) {
        $search = isset( $args['search_text'] ) ? trim( (string) $args['search_text'] ) : '';
        if ( '' === $search ) {
            return array( 'success' => false, 'error' => 'search_text is required.' );
        }
        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'elementor_search_text',
                'label'            => 'Search Elementor Text',
                'summary'          => 'Search all Elementor pages for "' . mb_substr( $search, 0, 50 ) . '".',
                'rollback_plan'    => 'Read-only, no rollback needed.',
                'rollback_support' => 'none',
                'targets'          => array(),
                'args'             => $args,
            ),
        );
    }

    public function execute_elementor_search_text( $args ) {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'elementor/elementor.php' ) ) {
            return array( 'success' => false, 'error' => 'Elementor is not active.' );
        }

        $search_text = isset( $args['search_text'] ) ? trim( (string) $args['search_text'] ) : '';
        if ( '' === $search_text ) {
            return array( 'success' => false, 'error' => 'search_text is required.' );
        }

        $strip_html = isset( $args['strip_html'] ) ? (bool) $args['strip_html'] : true;

        // Find all Elementor pages.
        $pages = get_posts( array(
            'post_type'      => array( 'page', 'post', 'product' ),
            'post_status'    => array( 'publish', 'draft', 'private', 'pending' ),
            'meta_key'       => '_elementor_edit_mode',
            'meta_value'     => 'builder',
            'posts_per_page' => 200,
            'fields'         => 'ids',
        ) );

        if ( empty( $pages ) ) {
            return array(
                'success' => true,
                'summary' => 'No Elementor pages found on this site.',
                'results' => array(),
            );
        }

        $results    = array();
        $search_lc  = mb_strtolower( $search_text );
        // Normalize whitespace in search term for flexible matching.
        $search_ws  = preg_replace( '/\s+/', ' ', $search_lc );

        foreach ( $pages as $page_id ) {
            $pid = intval( $page_id );

            // --- Search 1: _elementor_data settings (V3 classic + V4 typed values) ---
            $raw = get_post_meta( $pid, '_elementor_data', true );
            if ( ! empty( $raw ) ) {
                $data = json_decode( $raw, true );
                if ( is_array( $data ) ) {
                    $this->walk_elementor_text_search( $data, $search_lc, $pid, $strip_html, $results );
                }
            }

            // --- Search 2: post_content (catches V4 atomic elements, H2, etc.) ---
            $post = get_post( $pid );
            if ( $post && ! empty( $post->post_content ) ) {
                $content = $post->post_content;
                // Strip HTML and normalize whitespace for matching.
                $content_plain = wp_strip_all_tags( $content );
                $content_lc    = mb_strtolower( $content_plain );
                $content_ws    = preg_replace( '/\s+/', ' ', $content_lc );

                if ( false !== mb_strpos( $content_ws, $search_ws ) ) {
                    // Extract the matching line/context from raw HTML.
                    $context_text = '';
                    if ( preg_match( '/>([^<]*' . preg_quote( $search_text, '/' ) . '[^<]*)</i', $content, $m ) ) {
                        $context_text = trim( $m[1] );
                    } elseif ( preg_match( '/>([^<]*' . preg_quote( mb_strtolower( $search_text ), '/' ) . '[^<]*)</i', mb_strtolower( $content ), $m ) ) {
                        $context_text = trim( $m[1] );
                    }

                    // Avoid duplicate if already found in _elementor_data for same page.
                    $already_found = false;
                    foreach ( $results as $r ) {
                        if ( intval( $r['page_id'] ) === $pid ) {
                            $already_found = true;
                            break;
                        }
                    }

                    if ( ! $already_found ) {
                        $results[] = array(
                            'page_id'     => $pid,
                            'widget_id'   => '',
                            'widget_type' => 'post_content_atomic',
                            'field'       => 'post_content',
                            'text_raw'    => mb_substr( $context_text ?: $content_plain, 0, 300 ),
                            'text_plain'  => mb_substr( preg_replace( '/\s+/', ' ', $content_plain ), 0, 300 ),
                            'note'        => 'Found in post_content (V4 atomic element). Replace in BOTH post_content AND meta:_elementor_data.',
                        );
                    }
                }
            }
        }

        $page_titles = array();
        foreach ( $results as &$r ) {
            $pid = intval( $r['page_id'] );
            if ( ! isset( $page_titles[ $pid ] ) ) {
                $page_titles[ $pid ] = get_the_title( $pid );
            }
            $r['page_title'] = $page_titles[ $pid ];
        }
        unset( $r );

        return array(
            'success'       => true,
            'summary'       => count( $results ) . ' match(es) found across ' . count( $pages ) . ' Elementor pages.' . ( count( $results ) > 0 ? ' IMPORTANT: If result has field=post_content, replace in BOTH post_content AND meta:_elementor_data using replace_text_in_post.' : '' ),
            'search_text'   => $search_text,
            'results_count' => count( $results ),
            'results'       => $results,
        );
    }

    /**
     * Recursively walk Elementor elements looking for text that contains the search string.
     * Checks all known text fields, both V3 classic and V4 typed values.
     * Optionally strips HTML before matching so wrapped text is still found.
     */
    private function walk_elementor_text_search( $elements, $search_lc, $page_id, $strip_html, &$results, $depth = 0 ) {
        if ( ! is_array( $elements ) || $depth > 20 ) {
            return;
        }

        $text_fields = array(
            'title', 'editor', 'text', 'description_text', 'description',
            'button_text', 'inner_text', 'prefix', 'suffix',
            'before_text', 'after_text', 'highlight_text', 'rotating_text',
            'alert_title', 'alert_description',
            'testimonial_content', 'testimonial_name', 'testimonial_job',
            'caption', 'link_text', 'badge_text', 'ribbon_title',
            'sub_title', 'header_text', 'footer_text',
        );

        foreach ( $elements as $element ) {
            if ( ! is_array( $element ) ) {
                continue;
            }

            $el_id      = isset( $element['id'] ) ? (string) $element['id'] : '';
            $el_type    = isset( $element['elType'] ) ? (string) $element['elType'] : '';
            $widget_type = isset( $element['widgetType'] ) ? (string) $element['widgetType'] : '';
            $settings   = isset( $element['settings'] ) && is_array( $element['settings'] ) ? $element['settings'] : array();

            // Check V3 classic text fields.
            foreach ( $text_fields as $key ) {
                if ( isset( $settings[ $key ] ) && is_string( $settings[ $key ] ) ) {
                    $raw_text   = $settings[ $key ];
                    $check_text = $strip_html ? wp_strip_all_tags( $raw_text ) : $raw_text;
                    $check_lc   = mb_strtolower( $check_text );

                    if ( false !== mb_strpos( $check_lc, $search_lc ) ) {
                        $results[] = array(
                            'page_id'     => $page_id,
                            'widget_id'   => $el_id,
                            'widget_type' => $widget_type ?: $el_type,
                            'field'       => $key,
                            'text_raw'    => mb_substr( $raw_text, 0, 300 ),
                            'text_plain'  => mb_substr( $check_text, 0, 300 ),
                        );
                    }
                }
            }

            // Check V4 typed values: {$$type: "string", value: "..."}.
            foreach ( $settings as $key => $val ) {
                if ( is_array( $val ) && isset( $val['$$type'] ) && 'string' === $val['$$type'] && isset( $val['value'] ) && is_string( $val['value'] ) ) {
                    $raw_text   = $val['value'];
                    $check_text = $strip_html ? wp_strip_all_tags( $raw_text ) : $raw_text;
                    $check_lc   = mb_strtolower( $check_text );

                    if ( false !== mb_strpos( $check_lc, $search_lc ) ) {
                        $results[] = array(
                            'page_id'     => $page_id,
                            'widget_id'   => $el_id,
                            'widget_type' => $widget_type ?: $el_type,
                            'field'       => $key,
                            'text_raw'    => mb_substr( $raw_text, 0, 300 ),
                            'text_plain'  => mb_substr( $check_text, 0, 300 ),
                        );
                    }
                }
            }

            // Recurse into children.
            if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
                $this->walk_elementor_text_search( $element['elements'], $search_lc, $page_id, $strip_html, $results, $depth + 1 );
            }
        }
    }
}
