<?php

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

/**
 * Admin Autonomy Tools — Facade / Dispatcher.
 *
 * All domain logic lives in traits under includes/admin-autonomy/traits/.
 * This class composes them, owns properties/constants, and dispatches
 * preview / validate / execute / rollback calls.
 */
class Niwa_AI_Admin_Autonomy_Tools {

    use Niwa_AI_Tools_Registry;
    use Niwa_AI_Tools_Product_Ops;
    use Niwa_AI_Tools_Variation_Ops;
    use Niwa_AI_Tools_Product_Search;
    use Niwa_AI_Tools_Bulk_Pricing;
    use Niwa_AI_Tools_Content_Ops;
    use Niwa_AI_Tools_Elementor;
    use Niwa_AI_Tools_File_Ops;
    use Niwa_AI_Tools_Email;
    use Niwa_AI_Tools_Skills;
    use Niwa_AI_Tools_Blog_Posts;
    use Niwa_AI_Tools_Rollback;
    use Niwa_AI_Tools_Individual_Pricing;
    use Niwa_AI_Tools_Users;
    use Niwa_AI_Tools_Taxonomy;
    use Niwa_AI_Tools_Settings;
    use Niwa_AI_Tools_Niwa_Settings;
    use Niwa_AI_Tools_Pages;
    use Niwa_AI_Tools_Media_Management;
    use Niwa_AI_Tools_Elementor_Widgets;
    use Niwa_AI_Tools_Elementor_URL_Builder;
    use Niwa_AI_Tools_Woo_Orders;
    use Niwa_AI_Tools_Cache;
    use Niwa_AI_Tools_Woo_Taxonomy;
    use Niwa_AI_Tools_WP_Extras;
    use Niwa_AI_Tools_Bloging;
    use Niwa_AI_Tools_Blog_Pipeline;
    use Niwa_AI_Tools_Template_Library;

    /* ================================================================
     * Constants
     * ============================================================= */

    const MAX_TRASH_POSTS          = 20;
    const MAX_INVENTORY_LIMIT      = 200;
    const MAX_BULK_CATALOG_TARGETS = 1000;

    /* ================================================================
     * Properties
     * ============================================================= */

    /** @var Niwa_AI_Admin_Autonomy_File_Guard|null */
    private $file_guard;

    /** @var Niwa_AI_Admin_Autonomy_Store|null Store for rollback run access */
    private $autonomy_store;

    /** @var Niwa_AI_Admin_Autonomy_Skills|null */
    private $skills_manager;

    /** @var Niwa_AI_Admin_Autonomy_Email|null */
    private $email_service;

    /** @var Niwa_AI_Admin_Product_Management_Service|null */
    private $product_management_service;

    /** @var Niwa_AI_Admin_Product_Target_Resolver|null */
    private $product_target_resolver;

    private $runtime_context = array();

    /** @var Niwa_AI_AI_Client_Interface|null OpenAI client for eval/benchmark/optimize operations */
    private $eval_openai_client;
    /** @var Niwa_AI_Auth_Provider_Interface|null Auth manager for eval LLM calls */
    private $eval_auth_manager;
    /** @var object|null Model router for eval LLM calls */
    private $eval_model_router;

    /* ================================================================
     * Dependency injection
     * ============================================================= */

    public function set_skills_manager( $skills_manager ) {
        $this->skills_manager = $skills_manager;
    }

    public function set_email_service( $email_service ) {
        $this->email_service = $email_service;
    }

    public function set_product_management_service( $service ) {
        $this->product_management_service = $service;
    }

    public function set_product_target_resolver( $resolver ) {
        $this->product_target_resolver = $resolver;
    }

    public function set_autonomy_store( $store ) {
        $this->autonomy_store = $store;
    }

    public function set_runtime_context( $context ) {
        $this->runtime_context = is_array( $context ) ? $context : array();
    }

    public function clear_runtime_context() {
        $this->runtime_context = array();
    }

    public function set_llm_dependencies( Niwa_AI_AI_Client_Interface $openai_client, Niwa_AI_Auth_Provider_Interface $auth_manager, $model_router ) {
        $this->eval_openai_client = $openai_client;
        $this->eval_auth_manager  = $auth_manager;
        $this->eval_model_router  = $model_router;
    }

    /* ================================================================
     * Preview — maps tool name → trait method
     * ============================================================= */

    public function preview_tool_call( $name, $args, $prompt = '' ) {
        $boundary = $this->guard_untrusted_inbound_email_action( $name );
        if ( empty( $boundary['success'] ) ) {
            return $boundary;
        }

        switch ( (string) $name ) {
            case 'trash_recent_posts':
                return $this->preview_trash_recent_posts( $args );
            case 'trash_posts_by_query':
                return $this->preview_trash_posts_by_query( $args );
            case 'set_product_featured_image_from_url':
                return $this->preview_set_product_featured_image_from_url( $args );
            case 'upload_image_to_media':
                return $this->preview_upload_image_to_media( $args );
            case 'generate_image':
                return $this->preview_generate_image( $args );
            case 'restore_product_from_trash':
                return $this->preview_restore_product_from_trash( $args );
            case 'update_simple_product':
                return $this->preview_update_simple_product( $args );
            case 'create_product':
                return $this->preview_create_product( $args );
            case 'clone_partner_variable_product_from_url':
                return $this->preview_clone_partner_variable_product_from_url( $args );
            case 'manage_product_gallery':
                return $this->preview_manage_product_gallery( $args );
            case 'manage_product_attributes':
                return $this->preview_manage_product_attributes( $args );
            case 'remove_color':
                return $this->preview_remove_color( $args );
            case 'move_to_outlet':
                return $this->preview_move_to_outlet( $args );
            case 'send_email_message':
                return $this->preview_send_email_message( $args );
            case 'email_message_trash':
                return $this->preview_email_message_trash( $args );
            case 'send_email_with_exchange_rate':
                return $this->preview_send_email_with_exchange_rate( $args );
            case 'send_followup_to_captured_leads':
                return $this->preview_send_followup_to_captured_leads( $args );
            case 'send_whatsapp_lead_outreach':
                return $this->preview_send_whatsapp_lead_outreach( $args );
            case 'bulk_update_catalog_prices':
                return $this->preview_bulk_update_catalog_prices( $args, $prompt );
            case 'update_product_variation':
                return $this->preview_update_product_variation( $args );
            case 'set_variation_sale_schedule':
                return $this->preview_set_variation_sale_schedule( $args );
            case 'add_product_variation':
                return $this->preview_add_product_variation( $args );
            case 'bulk_update_variations':
                return $this->preview_bulk_update_variations( $args );
            case 'remove_product_variation':
                return $this->preview_remove_product_variation( $args );
            case 'update_front_page_title':
                return $this->preview_update_front_page_title( $args );
            case 'replace_text_in_post':
                return $this->preview_replace_text_in_post( $args );
            case 'elementor_restore_or_repair':
                return $this->preview_elementor_restore_or_repair( $args );
            case 'swap_page_image':
                return $this->preview_swap_page_image( $args );
            case 'update_wordpress_core':
                return $this->preview_update_wordpress_core( $args );
            case 'update_wordpress_plugins':
                return $this->preview_update_wordpress_plugins( $args );
            case 'manage_wordpress_plugin_activation':
                return $this->preview_manage_wordpress_plugin_activation( $args );
            case 'inventory_cleanup':
                return $this->preview_inventory_cleanup( $args );
            case 'skill_create':
                return $this->preview_skill_create( $args );
            case 'skill_delete':
                return $this->preview_skill_delete( $args );
            case 'skill_import':
                return $this->preview_skill_import( $args );
            case 'skill_import_from_github':
                return $this->preview_skill_import_from_github( $args );
            case 'skill_update':
                return $this->preview_skill_update( $args );
            case 'skill_eval_create':
                return $this->preview_skill_eval_create( $args );
            case 'skill_eval_run':
                return $this->preview_skill_eval_run( $args );
            case 'skill_benchmark':
                return $this->preview_skill_benchmark( $args );
            case 'skill_optimize_description':
                return $this->preview_skill_optimize_description( $args );
            case 'skill_file_write':
                return $this->preview_skill_file_write( $args );
            case 'skill_file_edit_patch':
                return $this->preview_skill_file_edit_patch( $args );
            case 'create_blog_post':
                return $this->preview_create_blog_post( $args );
            case 'update_blog_post':
                return $this->preview_update_blog_post( $args );
            case 'publish_blog_post':
                return $this->preview_publish_blog_post( $args );
            case 'bulk_set_individual_prices':
                return $this->preview_bulk_set_individual_prices( $args );

            /* ---------- Users ---------- */
            case 'search_users':
                return $this->preview_search_users( $args );
            case 'get_user':
                return $this->preview_get_user( $args );
            case 'create_user':
                return $this->preview_create_user( $args );
            case 'update_user':
                return $this->preview_update_user( $args );
            case 'delete_user':
                return $this->preview_delete_user( $args );
            case 'get_current_user':
                return $this->preview_get_current_user( $args );
            case 'update_current_user':
                return $this->preview_update_current_user( $args );

            /* ---------- Taxonomy ---------- */
            case 'list_categories':
                return $this->preview_list_categories( $args );
            case 'get_category':
                return $this->preview_get_category( $args );
            case 'create_category':
                return $this->preview_create_category( $args );
            case 'update_category':
                return $this->preview_update_category( $args );
            case 'delete_category':
                return $this->preview_delete_category( $args );
            case 'list_tags':
                return $this->preview_list_tags( $args );
            case 'get_tag':
                return $this->preview_get_tag( $args );
            case 'create_tag':
                return $this->preview_create_tag( $args );
            case 'update_tag':
                return $this->preview_update_tag( $args );
            case 'delete_tag':
                return $this->preview_delete_tag( $args );

            /* ---------- Settings ---------- */
            case 'update_operative_heartbeat_config':
                return array( 'type' => 'update_operative_heartbeat_config', 'summary' => 'Update operative heartbeat config.' );
            case 'get_wp_settings':
                return $this->preview_get_wp_settings( $args );
            case 'update_wp_settings':
                return $this->preview_update_wp_settings( $args );
            case 'niwa_settings':
                return $this->preview_niwa_settings( $args );
            case 'get_site_info':
                return $this->preview_get_site_info( $args );

            /* ---------- Bloging ---------- */
            case 'search_stock_images':
                return $this->preview_search_stock_images( $args );
            case 'scan_site_for_linking':
                return $this->preview_scan_site_for_linking( $args );
            case 'create_niwa_blog':
                return $this->preview_create_niwa_blog( $args );

            /* ---------- Pages ---------- */
            case 'search_pages':
                return $this->execute_search_pages( $args );
            case 'get_page':
                return $this->execute_get_page( $args );
            case 'create_page':
                return $this->preview_create_page( $args );
            case 'update_page':
                return $this->preview_update_page( $args );
            case 'delete_page':
                return $this->preview_delete_page( $args );

            /* ---------- Media Management ---------- */
            case 'search_media':
                return $this->execute_search_media( $args );
            case 'get_media_details':
                return $this->execute_get_media_details( $args );
            case 'update_media_metadata':
                return $this->preview_update_media_metadata( $args );
            case 'delete_media':
                return $this->preview_delete_media( $args );

            /* ---------- Elementor Widgets (read-only — execute directly) ---------- */
            case 'elementor_list_pages':
                return $this->execute_elementor_list_pages( $args );
            case 'elementor_get_page_structure':
                return $this->execute_elementor_get_page_structure( $args );
            case 'elementor_get_page_data':
                return $this->execute_elementor_get_page_data( $args );
            case 'elementor_get_active_kit':
                return $this->execute_elementor_get_active_kit( $args );
            case 'elementor_get_kit_by_id':
                return $this->execute_elementor_get_kit_by_id( $args );
            case 'elementor_find_widgets':
                return $this->execute_elementor_find_widgets( $args );
            case 'elementor_get_widget_by_id':
                return $this->execute_elementor_get_widget_by_id( $args );
            case 'elementor_search_text':
                return $this->preview_elementor_search_text( $args );
            case 'elementor_update_widget_content':
                return $this->preview_elementor_update_widget_content( $args );
            case 'elementor_update_widget_image':
                return $this->preview_elementor_update_widget_image( $args );
            case 'elementor_update_widget_link':
                return $this->preview_elementor_update_widget_link( $args );
            case 'elementor_update_widget_styles':
                return $this->preview_elementor_update_widget_styles( $args );
            case 'elementor_update_widget_settings':
                return $this->preview_elementor_update_widget_settings( $args );
            case 'elementor_duplicate_element':
                return $this->preview_elementor_duplicate_element( $args );
            case 'elementor_update_kit_settings':
                return $this->preview_elementor_update_kit_settings( $args );
            case 'elementor_update_page_data':
                return $this->preview_elementor_update_page_data( $args );
            case 'elementor_build_page_from_url':
                return $this->preview_elementor_build_page_from_url( $args );
            case 'template_library_list':
                return $this->execute_template_library_list( $args );
            case 'template_library_create_template':
                return $this->preview_template_library_create_template( $args );
            case 'template_library_generate_snippet':
                return $this->preview_template_library_generate_snippet( $args );
            case 'template_library_insert_elementor':
                return $this->preview_template_library_insert_elementor( $args );
            case 'template_library_publish_snippet':
                return $this->preview_template_library_publish_snippet( $args );
            case 'template_library_generate_preview':
                return $this->preview_template_library_generate_preview( $args );

            /* ---------- WooCommerce Orders & Reports ---------- */
            case 'posta_epk_submit_orders':
                return $this->preview_posta_epk_submit_orders( $args );
            case 'search_woo_orders':
                return $this->preview_search_woo_orders( $args );
            case 'inspect_order_attribution':
                return $this->preview_inspect_order_attribution( $args );
            case 'woo_report_sales':
                return $this->preview_woo_report_sales( $args );
            case 'woo_report_orders_totals':
                return $this->preview_woo_report_orders_totals( $args );
            case 'woo_report_products_totals':
                return $this->preview_woo_report_products_totals( $args );
            case 'woo_report_customers_totals':
                return $this->preview_woo_report_customers_totals( $args );
            case 'woo_report_coupons_totals':
                return $this->preview_woo_report_coupons_totals( $args );
            case 'woo_report_reviews_totals':
                return $this->preview_woo_report_reviews_totals( $args );

            /* ---------- Cache ---------- */
            case 'cache_settings_get':
                return $this->preview_cache_settings_get( $args );
            case 'cache_flush':
                return $this->preview_cache_flush( $args );
            case 'accelerator_cache_refresh':
                return $this->preview_accelerator_cache_refresh( $args );
            case 'cache_presets_list':
                return $this->preview_cache_presets_list( $args );
            case 'cache_preset_apply':
                return $this->preview_cache_preset_apply( $args );

            /* ---------- WooCommerce Taxonomy ---------- */
            case 'list_wc_categories':
                return $this->preview_list_wc_categories( $args );
            case 'create_wc_category':
                return $this->preview_create_wc_category( $args );
            case 'update_wc_category':
                return $this->preview_update_wc_category( $args );
            case 'delete_wc_category':
                return $this->preview_delete_wc_category( $args );
            case 'list_wc_tags':
                return $this->preview_list_wc_tags( $args );
            case 'create_wc_tag':
                return $this->preview_create_wc_tag( $args );
            case 'update_wc_tag':
                return $this->preview_update_wc_tag( $args );
            case 'delete_wc_tag':
                return $this->preview_delete_wc_tag( $args );
            case 'list_wc_brands':
                return $this->preview_list_wc_brands( $args );
            case 'create_wc_brand':
                return $this->preview_create_wc_brand( $args );
            case 'update_wc_brand':
                return $this->preview_update_wc_brand( $args );
            case 'delete_wc_brand':
                return $this->preview_delete_wc_brand( $args );

            /* ---------- Blog Posts (read-only) ---------- */
            case 'blog_posts_list':
            case 'blog_post_get':
                return array(
                    'success' => true,
                    'action'  => array(
                        'type'             => $name,
                        'label'            => $name,
                        'summary'          => 'Read-only blog post lookup.',
                        'rollback_plan'    => 'Read-only, no rollback needed.',
                        'rollback_support' => 'none',
                        'targets'          => array(),
                        'args'             => $args,
                    ),
                );

            /* ---------- WP Extras & Backup ---------- */
            case 'list_custom_post_types':
                return $this->execute_list_custom_post_types( $args );
            case 'get_media_file':
                return $this->execute_get_media_file( $args );
            case 'elementor_assign_global_color':
                return $this->preview_elementor_assign_global_color( $args );
            case 'wp_search_posts':
                return $this->execute_wp_search_posts( $args );
            case 'wp_get_post':
                return $this->execute_wp_get_post( $args );

            /* ---------- Rollback ---------- */
            case 'rollback_last_action':
                return $this->preview_rollback_last_action( $args );

            /* ---------- File Read + WP Option ---------- */
            case 'file_read':
                return $this->preview_file_read( $args );
            case 'wp_option_get':
                return $this->preview_wp_option_get( $args );
        }

        return array(
            'success' => false,
            'error'   => 'Nepodrzan autonomy tool: ' . (string) $name,
        );
    }

    private function preview_clone_partner_variable_product_from_url( $args ) {
        $args       = is_array( $args ) ? $args : array();
        $source_url = isset( $args['source_url'] ) ? esc_url_raw( trim( (string) $args['source_url'] ) ) : '';
        if ( '' === $source_url && isset( $args['url'] ) ) {
            $source_url = esc_url_raw( trim( (string) $args['url'] ) );
        }

        $mode = isset( $args['mode'] ) ? sanitize_key( (string) $args['mode'] ) : 'replace_existing';
        if ( ! in_array( $mode, array( 'replace_existing', 'create_new', 'update_existing' ), true ) ) {
            $mode = 'replace_existing';
        }

        $target_product_id = isset( $args['target_product_id'] ) ? intval( $args['target_product_id'] ) : 0;
        $target_sku        = isset( $args['target_sku'] ) ? sanitize_text_field( (string) $args['target_sku'] ) : '';

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'clone_partner_variable_product_from_url',
                'label'            => 'Kloniraj partner variable proizvod',
                'summary'          => 'Kloniraj partner proizvod iz URL-a, sacuvaj lokalne kategorije i verifikuj slike/varijacije pre zamene starog proizvoda.',
                'rollback_plan'    => 'Ako verifikacija ne prodje, staged proizvod ostaje draft i stari proizvod se ne dira. Posle uspesne zamene rollback zahteva vracanje arhiviranog proizvoda.',
                'rollback_support' => 'partial',
                'targets'          => array(
                    array(
                        'source_url'        => $source_url,
                        'target_product_id' => $target_product_id,
                        'target_sku'        => $target_sku,
                        'mode'              => $mode,
                    ),
                ),
                'args'             => array_merge(
                    $args,
                    array(
                        'source_url'        => $source_url,
                        'target_product_id' => $target_product_id,
                        'target_sku'        => $target_sku,
                        'mode'              => $mode,
                    )
                ),
            ),
        );
    }

    /* ================================================================
     * Validate — maps action type → trait validator
     * ============================================================= */

    private function normalize_action_for_dispatch( $action ) {
        $action = is_array( $action ) ? $action : array();

        if ( ! isset( $action['args'] ) || ! is_array( $action['args'] ) ) {
            $action['args'] = array();
        }

        return $action;
    }

    public function action_requires_admin_confirmation( $action ) {
        $action = $this->normalize_action_for_dispatch( $action );
        $type   = isset( $action['type'] ) ? (string) $action['type'] : '';
        $args   = $action['args'];
        $fields = array();

        switch ( $type ) {
            case 'update_simple_product':
                $fields = $this->present_action_arg_names( $args, array( 'regular_price', 'sale_price', 'clear_sale_price', 'date_on_sale_from', 'date_on_sale_to', 'clear_sale_dates', 'stock_quantity', 'status' ) );
                break;
            case 'create_product':
                $fields = array( 'product_creation' );
                $fields = array_merge( $fields, $this->present_action_arg_names( $args, array( 'regular_price', 'sale_price', 'stock_quantity', 'manage_stock', 'status' ) ) );
                break;
            case 'update_product_variation':
            case 'bulk_update_variations':
                $fields = $this->present_action_arg_names( $args, array( 'regular_price', 'sale_price', 'clear_sale_price', 'stock_quantity', 'variation_status' ) );
                break;
            case 'set_variation_sale_schedule':
                $fields = $this->present_action_arg_names( $args, array( 'sale_price', 'clear_sale_price', 'date_on_sale_from', 'date_on_sale_to', 'clear_sale_dates' ) );
                break;
            case 'add_product_variation':
                $fields = array( 'variation_creation' );
                $fields = array_merge( $fields, $this->present_action_arg_names( $args, array( 'regular_price', 'sale_price', 'stock_quantity', 'stock_status', 'variation_status', 'image_url', 'attachment_id' ) ) );
                break;
            case 'clone_partner_variable_product_from_url':
                $fields = array( 'partner_product_clone' );
                $fields = array_merge( $fields, $this->present_action_arg_names( $args, array( 'source_url', 'target_product_id', 'target_sku', 'mode' ) ) );
                break;
            case 'bulk_update_catalog_prices':
                $fields = array( 'catalog_prices' );
                break;
            case 'bulk_set_individual_prices':
                $fields = array( 'individual_prices' );
                break;
            case 'inventory_cleanup':
                $fields = array( 'product_status' );
                break;
            case 'move_to_outlet':
                $fields = array( 'product_variations', 'stock_state' );
                break;
        }

        $fields = array_values( array_unique( array_filter( array_map( 'strval', $fields ) ) ) );

        return array(
            'required' => ! empty( $fields ),
            'fields'   => $fields,
            'reason'   => ! empty( $fields ) ? 'WooCommerce product price, stock, product status, or variation state change requires explicit admin confirmation.' : '',
        );
    }

    private function present_action_arg_names( $args, $names ) {
        $args    = is_array( $args ) ? $args : array();
        $present = array();

        foreach ( (array) $names as $name ) {
            $name = (string) $name;
            if ( ! array_key_exists( $name, $args ) ) {
                continue;
            }

            $value = $args[ $name ];
            if ( null === $value ) {
                continue;
            }
            if ( is_string( $value ) && '' === trim( $value ) ) {
                continue;
            }
            if ( false === $value && in_array( $name, array( 'clear_sale_price', 'manage_stock' ), true ) ) {
                continue;
            }

            $present[] = $name;
        }

        return $present;
    }

    private function runtime_requires_admin_confirmation_gate() {
        $context = is_array( $this->runtime_context ) ? $this->runtime_context : array();
        if ( ! empty( $context['requires_admin_confirmation_gate'] ) || ! empty( $context['is_heartbeat'] ) || ! empty( $context['is_proactive_task'] ) ) {
            return true;
        }

        $source = isset( $context['source'] ) ? sanitize_key( (string) $context['source'] ) : '';
        return in_array( $source, array( 'heartbeat', 'proactive_task', 'scheduled_job', 'autonomous_task' ), true );
    }

    private function guard_untrusted_inbound_email_action( $type ) {
        if ( empty( $this->runtime_context['is_inbound_email_event'] ) ) {
            return array( 'success' => true );
        }

        return array(
            'success'             => false,
            'approval_required'   => true,
            'proposal_only'       => true,
            'reason'              => 'untrusted_inbound_email_tool_denied',
            'blocked_action_type' => sanitize_key( (string) $type ),
            'error'               => 'Untrusted inbound email events are read-only. Propose this action to the founder and wait for a new trusted turn.',
        );
    }

    private function guard_admin_confirmation_required_action( $action ) {
        $action = $this->normalize_action_for_dispatch( $action );
        $type   = isset( $action['type'] ) ? (string) $action['type'] : '';
        if ( ! $this->runtime_requires_admin_confirmation_gate() ) {
            return array( 'success' => true );
        }

        $confirmation = $this->action_requires_admin_confirmation( $action );
        if ( empty( $confirmation['required'] ) ) {
            return array( 'success' => true );
        }

        return array(
            'success'             => false,
            'approval_required'   => true,
            'error'               => 'Ova WooCommerce izmena menja cenu, stanje zaliha, status proizvoda ili varijacije i ne sme se izvrsiti iz heartbeat/proaktivnog/zakazanog autonomy konteksta bez eksplicitne admin potvrde. Daj samo predlog sa tacnim vrednostima i sacekaj admina.',
            'reason'              => $confirmation['reason'],
            'sensitive_fields'    => $confirmation['fields'],
            'blocked_action_type' => isset( $action['type'] ) ? (string) $action['type'] : '',
            'action_summary'      => isset( $action['summary'] ) ? (string) $action['summary'] : '',
            'proposal_only'       => true,
        );
    }

    public function validate_action( $action ) {
        $action = $this->normalize_action_for_dispatch( $action );
        $type = isset( $action['type'] ) ? (string) $action['type'] : '';
        $args = $action['args'];

        switch ( $type ) {
            case 'trash_recent_posts':
            case 'trash_posts_by_query':
                return $this->validate_trash_posts_action( $action );
            case 'set_product_featured_image_from_url':
                return $this->validate_required_positive_id( isset( $args['product_id'] ) ? $args['product_id'] : 0, 'Ciljni proizvod nedostaje za glavnu sliku.' );
            case 'upload_image_to_media':
                return array( 'success' => true );
            case 'generate_image':
                return $this->validate_generate_image_action_args( $args );
            case 'send_whatsapp_lead_outreach':
                return $this->validate_send_whatsapp_lead_outreach_action_args( $args );
            case 'restore_product_from_trash':
                return $this->validate_required_positive_id( isset( $args['product_id'] ) ? $args['product_id'] : 0, 'Ciljni proizvod nedostaje za restore iz trash-a.' );
            case 'update_simple_product':
                return $this->validate_simple_product_action_args( $args );
            case 'create_product':
                return $this->validate_create_product_action_args( $args );
            case 'clone_partner_variable_product_from_url':
                $source_url = isset( $args['source_url'] ) ? esc_url_raw( trim( (string) $args['source_url'] ) ) : '';
                if ( '' === $source_url && isset( $args['url'] ) ) {
                    $source_url = esc_url_raw( trim( (string) $args['url'] ) );
                }
                if ( '' === $source_url || ! preg_match( '#^https?://#i', $source_url ) ) {
                    return array( 'success' => false, 'error' => 'source_url mora biti validan http/https URL.' );
                }
                return array( 'success' => true );
            case 'manage_product_gallery':
                return $this->validate_manage_product_gallery_action_args( $args );
            case 'manage_product_attributes':
                return $this->validate_manage_product_attributes_action_args( $args );
            case 'remove_color':
                return $this->validate_remove_color_action_args( $args );
            case 'move_to_outlet':
                return $this->validate_move_to_outlet_action_args( $args );
            case 'send_email_message':
                return $this->validate_send_email_message_action_args( $args );
            case 'email_message_trash':
                return $this->validate_email_message_trash_action_args( $args );
            case 'send_email_with_exchange_rate':
                return $this->validate_send_email_with_exchange_rate_action_args( $args );
            case 'send_followup_to_captured_leads':
                return $this->validate_send_followup_action_args( $args );
            case 'bulk_update_catalog_prices':
                return $this->validate_bulk_catalog_price_action_args( $args );
            case 'update_product_variation':
                return $this->validate_variation_update_action_args( $args );
            case 'set_variation_sale_schedule':
                return $this->validate_variation_sale_schedule_action_args( $args );
            case 'add_product_variation':
                return $this->validate_add_variation_action_args( $args );
            case 'bulk_update_variations':
                return $this->validate_bulk_update_variations_action_args( $args );
            case 'remove_product_variation':
                return $this->validate_remove_variation_action_args( $args );
            case 'update_front_page_title':
                if ( empty( $args['new_title'] ) ) {
                    return array(
                        'success' => false,
                        'error'   => 'Novi naslov stranice nedostaje.',
                    );
                }
                return $this->validate_required_positive_id( isset( $args['post_id'] ) ? $args['post_id'] : 0, 'Ciljna stranica nedostaje za promenu naslova.' );
            case 'replace_text_in_post':
                return $this->validate_replace_text_in_post_action_args( $args );
            case 'elementor_restore_or_repair':
                return array( 'success' => true );
            case 'swap_page_image':
                return $this->validate_swap_page_image_args( $args );
            case 'update_wordpress_core':
                return array( 'success' => true );
            case 'update_wordpress_plugins':
                return $this->validate_update_wordpress_plugins_action_args( $args );
            case 'inventory_cleanup':
                $product_ids = array_values( array_filter( array_map( 'intval', isset( $args['product_ids'] ) ? (array) $args['product_ids'] : array() ) ) );
                if ( empty( $product_ids ) ) {
                    return array(
                        'success' => false,
                        'error'   => 'Inventory cleanup nema nijedan cilj za obradu.',
                    );
                }
                return array( 'success' => true );
            case 'skill_create':
                return $this->validate_skill_create_action( $args );
            case 'skill_delete':
                return $this->validate_skill_delete_action( $args );
            case 'skill_import':
                return $this->validate_skill_import_action( $args );
            case 'skill_import_from_github':
                return $this->validate_skill_import_from_github_action( $args );
            case 'skill_update':
                return $this->validate_skill_update_action( $args );
            case 'skill_eval_create':
                return $this->validate_skill_eval_create_action( $args );
            case 'skill_eval_run':
                return $this->validate_skill_eval_run_action( $args );
            case 'skill_benchmark':
                return $this->validate_skill_benchmark_action( $args );
            case 'skill_optimize_description':
                return $this->validate_skill_optimize_description_action( $args );
            case 'skill_file_write':
                return $this->validate_skill_file_write_action( $args );
            case 'skill_file_edit_patch':
                return $this->validate_skill_file_edit_patch_action( $args );
            case 'create_blog_post':
                return $this->validate_create_blog_post_action_args( $args );
            case 'update_blog_post':
                return $this->validate_update_blog_post_action_args( $args );
            case 'publish_blog_post':
                return $this->validate_publish_blog_post_action_args( $args );
            case 'bulk_set_individual_prices':
                return $this->validate_bulk_individual_price_action_args( $args );

            /* ---------- Users ---------- */
            case 'create_user':
                return $this->validate_create_user_action_args( $args );
            case 'update_user':
            case 'update_current_user':
                return $this->validate_update_user_action_args( $args );
            case 'delete_user':
                return $this->validate_delete_user_action_args( $args );
            case 'search_users':
            case 'get_user':
            case 'get_current_user':
                return array( 'success' => true );

            /* ---------- Taxonomy ---------- */
            case 'create_category':
                return $this->validate_create_category_action_args( $args );
            case 'update_category':
                return $this->validate_update_category_action_args( $args );
            case 'delete_category':
                return $this->validate_delete_category_action_args( $args );
            case 'create_tag':
                return $this->validate_create_tag_action_args( $args );
            case 'update_tag':
                return $this->validate_update_tag_action_args( $args );
            case 'delete_tag':
                return $this->validate_delete_tag_action_args( $args );
            case 'list_categories':
            case 'get_category':
            case 'list_tags':
            case 'get_tag':
                return array( 'success' => true );

            /* ---------- Settings ---------- */
            case 'update_operative_heartbeat_config':
                return array( 'valid' => true );
            case 'update_wp_settings':
                return $this->validate_update_wp_settings_action_args( $args );
            case 'niwa_settings_update':
                return $this->validate_niwa_settings_action( $args );
            case 'get_wp_settings':
            case 'get_site_info':
                return array( 'success' => true );
            case 'manage_wordpress_plugin_activation':
                return $this->validate_manage_wordpress_plugin_activation_action_args( $args );

            /* ---------- Bloging ---------- */
            case 'search_stock_images':
            case 'scan_site_for_linking':
                return array( 'success' => true );
            case 'create_niwa_blog':
                return $this->validate_create_niwa_blog_action_args( $args );

            /* ---------- Pages ---------- */
            case 'create_page':
                return $this->validate_create_page_action_args( $args );
            case 'update_page':
                return $this->validate_update_page_action_args( $args );
            case 'delete_page':
                return $this->validate_delete_page_action_args( $args );
            case 'search_pages':
            case 'get_page':
                return array( 'success' => true );

            /* ---------- Media Management ---------- */
            case 'update_media_metadata':
                return $this->validate_update_media_metadata_action_args( $args );
            case 'delete_media':
                return $this->validate_delete_media_action_args( $args );
            case 'search_media':
            case 'get_media_details':
                return array( 'success' => true );

            /* ---------- Elementor Widgets ---------- */
            case 'elementor_update_widget_content':
            case 'elementor_update_widget_image':
            case 'elementor_update_widget_link':
            case 'elementor_update_widget_styles':
            case 'elementor_update_widget_settings':
            case 'elementor_duplicate_element':
            case 'elementor_update_page_data':
                return $this->validate_required_positive_id( isset( $args['post_id'] ) ? $args['post_id'] : 0, 'Post ID nedostaje za Elementor update.' );
            case 'elementor_build_page_from_url':
                return $this->validate_elementor_build_page_from_url_args( $args );
            case 'elementor_update_kit_settings':
                return array( 'success' => true );
            case 'elementor_list_pages':
            case 'elementor_get_page_structure':
            case 'elementor_get_page_data':
            case 'elementor_get_active_kit':
            case 'elementor_get_kit_by_id':
            case 'elementor_find_widgets':
            case 'elementor_get_widget_by_id':
            case 'elementor_search_text':
                return array( 'success' => true );
            case 'template_library_list':
            case 'template_library_create_template':
            case 'template_library_generate_snippet':
            case 'template_library_insert_elementor':
            case 'template_library_publish_snippet':
            case 'template_library_generate_preview':
                return $this->validate_template_library_action_args( $args );

            /* ---------- WooCommerce Orders & Reports ---------- */
            case 'posta_epk_submit_orders':
                return $this->validate_posta_epk_submit_orders_action_args( $args );
            case 'search_woo_orders':
            case 'inspect_order_attribution':
            case 'woo_report_sales':
            case 'woo_report_orders_totals':
            case 'woo_report_products_totals':
            case 'woo_report_customers_totals':
            case 'woo_report_coupons_totals':
            case 'woo_report_reviews_totals':
                return array( 'success' => true );

            /* ---------- Cache ---------- */
            case 'cache_preset_apply':
                return $this->validate_cache_preset_apply_action_args( $args );
            case 'cache_settings_get':
            case 'cache_flush':
            case 'accelerator_cache_refresh':
            case 'cache_presets_list':
                return array( 'success' => true );

            /* ---------- WooCommerce Taxonomy ---------- */
            case 'create_wc_category':
                return $this->validate_create_wc_term_action_args( $args, 'product_cat' );
            case 'update_wc_category':
                return $this->validate_update_wc_term_action_args( $args, 'category_id' );
            case 'delete_wc_category':
                return $this->validate_delete_wc_term_action_args( $args, 'category_id' );
            case 'create_wc_tag':
                return $this->validate_create_wc_term_action_args( $args, 'product_tag' );
            case 'update_wc_tag':
                return $this->validate_update_wc_term_action_args( $args, 'tag_id' );
            case 'delete_wc_tag':
                return $this->validate_delete_wc_term_action_args( $args, 'tag_id' );
            case 'create_wc_brand':
                return $this->validate_create_wc_term_action_args( $args, 'product_brand' );
            case 'update_wc_brand':
                return $this->validate_update_wc_term_action_args( $args, 'brand_id' );
            case 'delete_wc_brand':
                return $this->validate_delete_wc_term_action_args( $args, 'brand_id' );
            case 'list_wc_categories':
            case 'list_wc_tags':
            case 'list_wc_brands':
                return array( 'success' => true );

            /* ---------- WP Extras & Backup ---------- */
            case 'elementor_assign_global_color':
                return $this->validate_required_positive_id( isset( $args['post_id'] ) ? $args['post_id'] : 0, 'Post ID nedostaje za Elementor global color.' );
            case 'blog_posts_list':
            case 'blog_post_get':
            case 'list_custom_post_types':
            case 'get_media_file':
            case 'wp_search_posts':
            case 'wp_get_post':
                return array( 'success' => true );

            /* ---------- Rollback ---------- */
            case 'rollback_last_action':
                return array( 'success' => true );

            /* ---------- File Read + WP Option ---------- */
            case 'file_read':
            case 'wp_option_get':
                return array( 'success' => true );
        }

        return array(
            'success' => false,
            'error'   => 'Nepodrzan action type za validaciju: ' . $type,
        );
    }

    /* ================================================================
     * Execute — maps action type → trait executor
     * ============================================================= */

    public function execute_action( $action ) {
        // Extract args for read-only tools that accept $args instead of $action.
        $action           = is_array( $action ) ? $action : array();
        $args_from_action = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : $action;
        $action           = $this->normalize_action_for_dispatch( $action );
        $boundary         = $this->guard_untrusted_inbound_email_action( isset( $action['type'] ) ? (string) $action['type'] : '' );
        if ( empty( $boundary['success'] ) ) {
            return $boundary;
        }
        $approval_guard   = $this->guard_admin_confirmation_required_action( $action );
        if ( empty( $approval_guard['success'] ) ) {
            return $approval_guard;
        }
		$type = isset( $action['type'] ) ? (string) $action['type'] : '';
		if ( in_array( $type, array( 'create_niwa_blog', 'create_blog_post', 'update_blog_post', 'publish_blog_post', 'replace_text_in_post', 'swap_page_image' ), true ) ) {
			$validation = $this->validate_action( $action );
			if ( empty( $validation['success'] ) ) {
				return $validation;
			}
		}

        switch ( $type ) {
            case 'trash_recent_posts':
            case 'trash_posts_by_query':
                return $this->execute_trash_posts_action( $action );
            case 'set_product_featured_image_from_url':
                return $this->execute_set_product_featured_image_action( $action );
            case 'upload_image_to_media':
                return $this->execute_upload_image_to_media_action( $action );
            case 'generate_image':
                return $this->execute_generate_image_action( $action );
            case 'restore_product_from_trash':
                return $this->execute_restore_product_from_trash_action( $action );
            case 'update_simple_product':
                return $this->execute_update_simple_product_action( $action );
            case 'create_product':
                return $this->execute_create_product_action( $action );
            case 'clone_partner_variable_product_from_url':
                return $this->execute_clone_partner_variable_product_from_url_action( $action );
            case 'manage_product_gallery':
                return $this->execute_manage_product_gallery_action( $action );
            case 'manage_product_attributes':
                return $this->execute_manage_product_attributes_action( $action );
            case 'remove_color':
                return $this->execute_remove_color_action( $action );
            case 'move_to_outlet':
                return $this->execute_move_to_outlet_action( $action );
            case 'send_email_message':
                return $this->execute_send_email_message_action( $action );
            case 'email_message_trash':
                return $this->execute_email_message_trash_action( $action );
            case 'send_email_with_exchange_rate':
                return $this->execute_send_email_with_exchange_rate_action( $action );
            case 'send_followup_to_captured_leads':
                return $this->execute_send_followup_to_captured_leads_action( $action );
            case 'send_whatsapp_lead_outreach':
                return $this->execute_send_whatsapp_lead_outreach_action( $action );
            case 'bulk_update_catalog_prices':
                return $this->execute_bulk_update_catalog_prices_action( $action );
            case 'update_product_variation':
                return $this->execute_update_product_variation_action( $action );
            case 'set_variation_sale_schedule':
                return $this->execute_set_variation_sale_schedule_action( $action );
            case 'add_product_variation':
                return $this->execute_add_product_variation_action( $action );
            case 'bulk_update_variations':
                return $this->execute_bulk_update_variations_action( $action );
            case 'remove_product_variation':
                return $this->execute_remove_product_variation_action( $action );
            case 'update_front_page_title':
                return $this->execute_update_front_page_title_action( $action );
            case 'replace_text_in_post':
                return $this->execute_replace_text_in_post_action( $action );
            case 'elementor_restore_or_repair':
                return $this->execute_elementor_restore_or_repair_action( $action );
            case 'swap_page_image':
                return $this->execute_swap_page_image_action( $action );
            case 'update_wordpress_core':
                return $this->execute_update_wordpress_core_action( $action );
            case 'update_wordpress_plugins':
                return $this->execute_update_wordpress_plugins_action( $action );
            case 'manage_wordpress_plugin_activation':
                return $this->execute_manage_wordpress_plugin_activation_action( $action );
            case 'inventory_cleanup':
                return $this->execute_inventory_cleanup_action( $action );
            case 'skill_create':
                return $this->execute_skill_create_action( $action );
            case 'skill_delete':
                return $this->execute_skill_delete_action( $action );
            case 'skill_import':
                return $this->execute_skill_import_action( $action );
            case 'skill_import_from_github':
                return $this->execute_skill_import_from_github_action( $action );
            case 'skill_update':
                return $this->execute_skill_update_action( $action );
            case 'skill_eval_create':
                return $this->execute_skill_eval_create_action( $action );
            case 'skill_eval_run':
                return $this->execute_skill_eval_run_action( $action );
            case 'skill_benchmark':
                return $this->execute_skill_benchmark_action( $action );
            case 'skill_optimize_description':
                return $this->execute_skill_optimize_description_action( $action );
            case 'skill_file_write':
                return $this->execute_skill_file_write_action( $action );
            case 'skill_file_edit_patch':
                return $this->execute_skill_file_edit_patch_action( $action );
            case 'create_blog_post':
                return $this->execute_create_blog_post_action( $action );
            case 'update_blog_post':
                return $this->execute_update_blog_post_action( $action );
            case 'publish_blog_post':
                return $this->execute_publish_blog_post_action( $action );
            case 'bulk_set_individual_prices':
                return $this->execute_bulk_set_individual_prices_action( $action );

            /* ---------- Users ---------- */
            case 'search_users':
                return $this->execute_search_users_action( $action );
            case 'get_user':
                return $this->execute_get_user_action( $action );
            case 'create_user':
                return $this->execute_create_user_action( $action );
            case 'update_user':
                return $this->execute_update_user_action( $action );
            case 'delete_user':
                return $this->execute_delete_user_action( $action );
            case 'get_current_user':
                return $this->execute_get_current_user_action( $action );
            case 'update_current_user':
                return $this->execute_update_current_user_action( $action );

            /* ---------- Taxonomy ---------- */
            case 'list_categories':
                return $this->execute_list_categories( $args_from_action );
            case 'get_category':
                return $this->execute_get_category( $args_from_action );
            case 'create_category':
                return $this->execute_create_category_action( $action );
            case 'update_category':
                return $this->execute_update_category_action( $action );
            case 'delete_category':
                return $this->execute_delete_category_action( $action );
            case 'list_tags':
                return $this->execute_list_tags( $args_from_action );
            case 'get_tag':
                return $this->execute_get_tag( $args_from_action );
            case 'create_tag':
                return $this->execute_create_tag_action( $action );
            case 'update_tag':
                return $this->execute_update_tag_action( $action );
            case 'delete_tag':
                return $this->execute_delete_tag_action( $action );

            /* ---------- Settings ---------- */
            case 'update_operative_heartbeat_config':
                return $this->execute_update_operative_heartbeat_config( $action );
            case 'get_wp_settings':
                return $this->execute_get_wp_settings_action( $action );
            case 'update_wp_settings':
                return $this->execute_update_wp_settings_action( $action );
            case 'niwa_settings_update':
                return $this->execute_niwa_settings_action( $action );
            case 'get_site_info':
                return $this->execute_get_site_info_action( $action );

            /* ---------- Bloging ---------- */
            case 'search_stock_images':
                return $this->execute_search_stock_images_action( $action );
            case 'scan_site_for_linking':
                return $this->execute_scan_site_for_linking_action( $action );
            case 'create_niwa_blog':
                return $this->execute_create_niwa_blog_action( $action );

            /* ---------- Pages ---------- */
            case 'search_pages':
                return $this->execute_search_pages( $args_from_action );
            case 'get_page':
                return $this->execute_get_page( $args_from_action );
            case 'create_page':
                return $this->execute_create_page_action( $action );
            case 'update_page':
                return $this->execute_update_page_action( $action );
            case 'delete_page':
                return $this->execute_delete_page_action( $action );

            /* ---------- Media Management ---------- */
            case 'search_media':
                return $this->execute_search_media( $args_from_action );
            case 'get_media_details':
                return $this->execute_get_media_details( $args_from_action );
            case 'update_media_metadata':
                return $this->execute_update_media_metadata_action( $action );
            case 'delete_media':
                return $this->execute_delete_media_action( $action );

            /* ---------- Elementor Widgets ---------- */
            case 'elementor_list_pages':
                return $this->execute_elementor_list_pages( $args_from_action );
            case 'elementor_get_page_structure':
                return $this->execute_elementor_get_page_structure( $args_from_action );
            case 'elementor_get_page_data':
                return $this->execute_elementor_get_page_data( $args_from_action );
            case 'elementor_get_active_kit':
                return $this->execute_elementor_get_active_kit( $args_from_action );
            case 'elementor_get_kit_by_id':
                return $this->execute_elementor_get_kit_by_id( $args_from_action );
            case 'elementor_find_widgets':
                return $this->execute_elementor_find_widgets( $args_from_action );
            case 'elementor_get_widget_by_id':
                return $this->execute_elementor_get_widget_by_id( $args_from_action );
            case 'elementor_search_text':
                return $this->execute_elementor_search_text( $args_from_action );
            case 'elementor_update_widget_content':
                return $this->execute_elementor_update_widget_content_action( $action );
            case 'elementor_update_widget_image':
                return $this->execute_elementor_update_widget_image_action( $action );
            case 'elementor_update_widget_link':
                return $this->execute_elementor_update_widget_link_action( $action );
            case 'elementor_update_widget_styles':
                return $this->execute_elementor_update_widget_styles_action( $action );
            case 'elementor_update_widget_settings':
                return $this->execute_elementor_update_widget_settings_action( $action );
            case 'elementor_duplicate_element':
                return $this->execute_elementor_duplicate_element_action( $action );
            case 'elementor_update_kit_settings':
                return $this->execute_elementor_update_kit_settings_action( $action );
            case 'elementor_update_page_data':
                return $this->execute_elementor_update_page_data_action( $action );
            case 'elementor_build_page_from_url':
                return $this->execute_elementor_build_page_from_url_action( $action );
            case 'template_library_list':
                return $this->execute_template_library_list( $args_from_action );
            case 'template_library_create_template':
                return $this->execute_template_library_create_template_action( $action );
            case 'template_library_generate_snippet':
                return $this->execute_template_library_generate_snippet_action( $action );
            case 'template_library_insert_elementor':
                return $this->execute_template_library_insert_elementor_action( $action );
            case 'template_library_publish_snippet':
                return $this->execute_template_library_publish_snippet_action( $action );
            case 'template_library_generate_preview':
                return $this->execute_template_library_generate_preview_action( $action );

            /* ---------- WooCommerce Orders & Reports ---------- */
            case 'posta_epk_submit_orders':
                return $this->execute_posta_epk_submit_orders_action( $action );
            case 'search_woo_orders':
                return $this->execute_search_woo_orders_action( $action );
            case 'inspect_order_attribution':
                return $this->execute_inspect_order_attribution_action( $action );
            case 'woo_report_sales':
                return $this->execute_woo_report_sales_action( $action );
            case 'woo_report_orders_totals':
                return $this->execute_woo_report_orders_totals_action( $action );
            case 'woo_report_products_totals':
                return $this->execute_woo_report_products_totals_action( $action );
            case 'woo_report_customers_totals':
                return $this->execute_woo_report_customers_totals_action( $action );
            case 'woo_report_coupons_totals':
                return $this->execute_woo_report_coupons_totals_action( $action );
            case 'woo_report_reviews_totals':
                return $this->execute_woo_report_reviews_totals_action( $action );

            /* ---------- Cache ---------- */
            case 'cache_settings_get':
                return $this->execute_cache_settings_get_action( $action );
            case 'cache_flush':
                return $this->execute_cache_flush_action( $action );
            case 'accelerator_cache_refresh':
                return $this->execute_accelerator_cache_refresh_action( $action );
            case 'cache_presets_list':
                return $this->execute_cache_presets_list_action( $action );
            case 'cache_preset_apply':
                return $this->execute_cache_preset_apply_action( $action );

            /* ---------- WooCommerce Taxonomy ---------- */
            case 'list_wc_categories':
                return $this->execute_list_wc_categories( $args_from_action );
            case 'create_wc_category':
                return $this->execute_create_wc_category_action( $action );
            case 'update_wc_category':
                return $this->execute_update_wc_category_action( $action );
            case 'delete_wc_category':
                return $this->execute_delete_wc_category_action( $action );
            case 'list_wc_tags':
                return $this->execute_list_wc_tags( $args_from_action );
            case 'create_wc_tag':
                return $this->execute_create_wc_tag_action( $action );
            case 'update_wc_tag':
                return $this->execute_update_wc_tag_action( $action );
            case 'delete_wc_tag':
                return $this->execute_delete_wc_tag_action( $action );
            case 'list_wc_brands':
                return $this->execute_list_wc_brands( $args_from_action );
            case 'create_wc_brand':
                return $this->execute_create_wc_brand_action( $action );
            case 'update_wc_brand':
                return $this->execute_update_wc_brand_action( $action );
            case 'delete_wc_brand':
                return $this->execute_delete_wc_brand_action( $action );

            /* ---------- WP Extras & Backup ---------- */
            case 'blog_posts_list':
                return $this->execute_blog_posts_list( $args_from_action );
            case 'blog_post_get':
                return $this->execute_blog_post_get( $args_from_action );
            case 'list_custom_post_types':
                return $this->execute_list_custom_post_types( $args_from_action );
            case 'get_media_file':
                return $this->execute_get_media_file( $args_from_action );
            case 'elementor_assign_global_color':
                return $this->execute_elementor_assign_global_color_action( $action );
            case 'wp_search_posts':
                return $this->execute_wp_search_posts( $args_from_action );
            case 'wp_get_post':
                return $this->execute_wp_get_post( $args_from_action );

            /* ---------- Rollback ---------- */
            case 'rollback_last_action':
                return $this->execute_rollback_last_action( $action );

            /* ---------- File Read + WP Option ---------- */
            case 'file_read':
                return $this->execute_file_read( $args_from_action );
            case 'wp_option_get':
                return $this->execute_wp_option_get( $args_from_action );
        }

        return array(
            'success' => false,
            'error'   => 'Executor nema handler za action type ' . ( isset( $action['type'] ) ? (string) $action['type'] : '' ),
        );
    }

    private function execute_clone_partner_variable_product_from_url_action( $action ) {
        $args = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        if ( empty( $args['source_url'] ) && ! empty( $args['url'] ) ) {
            $args['source_url'] = $args['url'];
        }

        $cloner = new Niwa_AI_Admin_Partner_Product_Cloner();
        return $cloner->clone_from_url( $args );
    }

    /* ================================================================
     * Rollback
     * ============================================================= */

    public function rollback_entries( $entries ) {
        foreach ( array_reverse( (array) $entries ) as $entry ) {
            $result = $this->restore_entry( $entry );
            if ( empty( $result['success'] ) ) {
                return $result;
            }
        }

        return array(
            'success' => true,
        );
    }

    /* ================================================================
     * Shared validation helpers (used by dispatch switch above)
     * ============================================================= */

    private function validate_trash_posts_action( $action ) {
        $targets = isset( $action['targets'] ) && is_array( $action['targets'] ) ? $action['targets'] : array();
        if ( empty( $targets ) ) {
            return array(
                'success' => false,
                'error'   => 'Trash akcija nema nijedan cilj.',
            );
        }

        foreach ( $targets as $target ) {
            if ( empty( $target['post_id'] ) || intval( $target['post_id'] ) <= 0 ) {
                return array(
                    'success' => false,
                    'error'   => 'Trash akcija sadrzi nevalidan post cilj.',
                );
            }
        }

        return array(
            'success' => true,
        );
    }

    private function validate_required_positive_id( $value, $error_message ) {
        if ( intval( $value ) <= 0 ) {
            return array(
                'success' => false,
                'error'   => (string) $error_message,
            );
        }

        return array(
            'success' => true,
        );
    }
}
