<?php

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

trait Niwa_AI_Tools_Product_Ops {

    private function validate_simple_product_action_args( $args ) {
        $id_check = $this->validate_required_positive_id( isset( $args['product_id'] ) ? $args['product_id'] : 0, 'Ciljni proizvod nedostaje.' );
        if ( empty( $id_check['success'] ) ) {
            return $id_check;
        }

        $has_change = false;

        /* Check all string/value fields that indicate a change. */
        $value_fields = array(
            'regular_price', 'sale_price', 'stock_quantity', 'title', 'description', 'short_description',
            'sku', 'status', 'weight', 'length', 'width', 'height', 'catalog_visibility', 'tax_status',
            'tax_class', 'backorders', 'low_stock_amount', 'purchase_note', 'menu_order',
        );
        foreach ( $value_fields as $field ) {
            if ( array_key_exists( $field, $args ) && null !== $args[ $field ] && '' !== trim( (string) $args[ $field ] ) ) {
                $has_change = true;
                break;
            }
        }

        /* Boolean fields. */
        $bool_fields = array( 'featured', 'virtual', 'downloadable', 'sold_individually', 'reviews_allowed' );
        if ( ! $has_change ) {
            foreach ( $bool_fields as $bf ) {
                if ( array_key_exists( $bf, $args ) && null !== $args[ $bf ] ) {
                    $has_change = true;
                    break;
                }
            }
        }

        if ( ! $has_change && ! empty( $args['clear_sale_price'] ) ) {
            $has_change = true;
        }

        /* Array fields: upsells, cross-sells. */
        if ( ! $has_change && ! empty( $args['upsell_ids'] ) && is_array( $args['upsell_ids'] ) ) {
            $has_change = true;
        }
        if ( ! $has_change && ! empty( $args['cross_sell_ids'] ) && is_array( $args['cross_sell_ids'] ) ) {
            $has_change = true;
        }

        /* Categories or tags resolved. */
        if ( ! $has_change && ! empty( $args['resolved_category_ids'] ) && is_array( $args['resolved_category_ids'] ) ) {
            $has_change = true;
        }
        if ( ! $has_change && ! empty( $args['resolved_tag_ids'] ) && is_array( $args['resolved_tag_ids'] ) ) {
            $has_change = true;
        }

        if ( ! $has_change ) {
            return array(
                'success' => false,
                'error'   => 'Nijedna Woo promena nije upisana u product akciji.',
            );
        }

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

    private function validate_create_product_action_args( $args ) {
        if ( empty( $args['title'] ) || '' === trim( (string) $args['title'] ) ) {
            return array( 'success' => false, 'error' => 'Naziv proizvoda (title) nedostaje.' );
        }
        $ptype = isset( $args['product_type'] ) ? (string) $args['product_type'] : 'simple';
        if ( 'simple' === $ptype && ( empty( $args['regular_price'] ) || '' === trim( (string) $args['regular_price'] ) ) ) {
            return array( 'success' => false, 'error' => 'Regular price je obavezan za simple proizvod.' );
        }
        return array( 'success' => true );
    }

    private function validate_manage_product_gallery_action_args( $args ) {
        $id_check = $this->validate_required_positive_id( isset( $args['product_id'] ) ? $args['product_id'] : 0, 'Ciljni proizvod nedostaje za gallery.' );
        if ( empty( $id_check['success'] ) ) {
            return $id_check;
        }
        $has_urls = ! empty( $args['image_urls'] ) && is_array( $args['image_urls'] );
        $has_ids  = ! empty( $args['attachment_ids'] ) && is_array( $args['attachment_ids'] );
        if ( ! $has_urls && ! $has_ids ) {
            return array( 'success' => false, 'error' => 'Nema image URL-ova ili attachment ID-jeva za galeriju.' );
        }
        return array( 'success' => true );
    }

    private function validate_manage_product_attributes_action_args( $args ) {
        $id_check = $this->validate_required_positive_id( isset( $args['product_id'] ) ? $args['product_id'] : 0, 'Ciljni proizvod nedostaje za upravljanje atributima.' );
        if ( empty( $id_check['success'] ) ) {
            return $id_check;
        }
        if ( empty( $args['attribute_name'] ) ) {
            return array( 'success' => false, 'error' => 'Naziv atributa nedostaje.' );
        }
        $attr_action = isset( $args['action'] ) ? (string) $args['action'] : 'add';
        if ( 'add' === $attr_action && ( empty( $args['attribute_values'] ) || ! is_array( $args['attribute_values'] ) ) ) {
            return array( 'success' => false, 'error' => 'attribute_values je obavezan za add akciju.' );
        }
        return array( 'success' => true );
    }

    private function validate_remove_color_action_args( $args ) {
        $id_check = $this->validate_required_positive_id( isset( $args['product_id'] ) ? $args['product_id'] : 0, 'Ciljni proizvod nedostaje za remove_color.' );
        if ( empty( $id_check['success'] ) ) {
            return $id_check;
        }

        $color = isset( $args['color'] ) ? trim( (string) $args['color'] ) : '';
        if ( '' === $color ) {
            return array( 'success' => false, 'error' => 'Boja je obavezna za remove_color.' );
        }

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

    private function validate_move_to_outlet_action_args( $args ) {
        $id_check = $this->validate_required_positive_id( isset( $args['product_id'] ) ? $args['product_id'] : 0, 'Ciljni proizvod nedostaje za move_to_outlet.' );
        if ( empty( $id_check['success'] ) ) {
            return $id_check;
        }

        $specs = isset( $args['specs'] ) && is_array( $args['specs'] ) ? array_values( $args['specs'] ) : array();
        if ( empty( $specs ) ) {
            return array( 'success' => false, 'error' => 'Outlet specifikacija je prazna.' );
        }

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

    private function preview_set_product_featured_image_from_url( $args ) {
        if ( ! class_exists( 'WooCommerce' ) ) {
            return array(
                'success' => false,
                'error'   => 'WooCommerce nije aktivan.',
            );
        }

        $product_query = trim( (string) ( isset( $args['product_query'] ) ? $args['product_query'] : '' ) );
        $product_id    = isset( $args['product_id'] ) ? intval( $args['product_id'] ) : 0;
        $image_url     = esc_url_raw( isset( $args['image_url'] ) ? (string) $args['image_url'] : '' );
        $attachment_id = isset( $args['attachment_id'] ) ? intval( $args['attachment_id'] ) : 0;

        // Allow product_id as direct lookup (MCP agents often pass ID instead of query)
        if ( '' === $product_query && $product_id > 0 ) {
            $product_query = (string) $product_id;
        }

        if ( '' === $product_query || ( '' === $image_url && $attachment_id <= 0 ) ) {
            return array(
                'success' => false,
                'error'   => 'Product query i image source su obavezni.',
            );
        }

        $resolved = $this->resolve_product_target_for_write( $product_id, $product_query, array( 'publish', 'private', 'draft', 'pending', 'future' ), 'featured image' );
        if ( empty( $resolved['success'] ) ) {
            return array(
                'success' => false,
                'error'   => isset( $resolved['error'] ) ? (string) $resolved['error'] : 'Nisam nasao proizvod za query: ' . $product_query,
            );
        }
        $product = $resolved['target'];

        $summary = 'Set featured image za proizvod "' . $product['post_title'] . '".';
        if ( ! empty( $product['match_warning'] ) ) {
            $summary .= ' [UPOZORENJE: ' . (string) $product['match_warning'] . ']';
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'set_product_featured_image_from_url',
                'label'            => 'Postavi glavnu sliku za proizvod',
                'summary'          => $summary,
                'rollback_plan'    => 'Vraca prethodni featured image i brise novouvezeni attachment ako ga je run dodao.',
                'rollback_support' => 'full',
                'targets'          => array( $product ),
                'args'             => array(
                    'product_id'    => $product['post_id'],
                    'image_url'     => $image_url,
                    'attachment_id' => $attachment_id,
                ),
            ),
        );
    }

    private function preview_upload_image_to_media( $args ) {
        $title         = sanitize_text_field( isset( $args['title'] ) ? (string) $args['title'] : '' );
        $image_url     = esc_url_raw( isset( $args['image_url'] ) ? (string) $args['image_url'] : '' );
        $alt_text      = sanitize_text_field( isset( $args['alt_text'] ) ? (string) $args['alt_text'] : '' );
        $parent_id     = isset( $args['parent_post_id'] ) ? intval( $args['parent_post_id'] ) : 0;
        $attachment_id = isset( $args['attachment_id'] ) ? intval( $args['attachment_id'] ) : 0;

        if ( '' === $title ) {
            return array(
                'success' => false,
                'error'   => 'Title je obavezan za upload slike.',
            );
        }

        if ( '' === $image_url && $attachment_id <= 0 ) {
            return array(
                'success' => false,
                'error'   => 'Nema slike za upload — ni image_url ni attachment_id.',
            );
        }

        $summary = 'Upload slike "' . $title . '" u Media Library.';
        if ( $attachment_id > 0 ) {
            $summary .= ' (iz vec ingestovane Telegram slike, att_id=' . $attachment_id . ')';
        }
        if ( '' !== $alt_text ) {
            $summary .= ' Alt: "' . $alt_text . '".';
        }
        if ( $parent_id > 0 ) {
            $summary .= ' Attach to post #' . $parent_id . '.';
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'upload_image_to_media',
                'label'            => 'Upload slike u Media Library',
                'summary'          => $summary,
                'rollback_plan'    => 'Brise novi attachment iz Media Library.',
                'rollback_support' => 'full',
                'targets'          => array(),
                'args'             => array(
                    'image_url'      => $image_url,
                    'title'          => $title,
                    'alt_text'       => $alt_text,
                    'parent_post_id' => $parent_id,
                    'attachment_id'  => $attachment_id,
                ),
            ),
        );
    }

    private function preview_generate_image( $args ) {
        $prompt    = isset( $args['prompt'] ) ? sanitize_textarea_field( (string) $args['prompt'] ) : '';
        $title     = isset( $args['title'] ) ? sanitize_text_field( (string) $args['title'] ) : '';
        $alt_text  = isset( $args['alt_text'] ) ? sanitize_text_field( (string) $args['alt_text'] ) : '';
        $quality   = $this->normalize_generate_image_quality( isset( $args['quality'] ) ? $args['quality'] : $this->get_default_generate_image_quality() );
        $size      = $this->normalize_generate_image_size( isset( $args['size'] ) ? $args['size'] : '1024x1024' );
        $reference_image_urls = $this->normalize_generate_image_reference_urls( isset( $args['reference_image_urls'] ) ? $args['reference_image_urls'] : array() );
        $parent_id = isset( $args['parent_post_id'] ) ? intval( $args['parent_post_id'] ) : 0;
        $force_regenerate = ! empty( $args['force_regenerate'] );
        if ( '' === $prompt ) { return array( 'success' => false, 'error' => 'Prompt je obavezan za generate_image.' ); }
        if ( '' === $title ) { return array( 'success' => false, 'error' => 'Title je obavezan za generate_image.' ); }
        if ( empty( $reference_image_urls ) && $parent_id > 0 ) {
            $reference_image_urls = $this->collect_parent_product_reference_urls( $parent_id );
        }
        return array( 'success' => true, 'action' => array( 'type' => 'generate_image', 'label' => 'Generisi sliku', 'summary' => 'Generisi sliku "' . $title . '" preko Codex OAuth image_generation alata (quality=' . $quality . ', size=' . $size . ', references=' . count( $reference_image_urls ) . ') i sacuvaj je u Media Library.', 'rollback_plan' => 'Brise novi generated-image attachment iz Media Library.', 'rollback_support' => 'full', 'targets' => array(), 'args' => array( 'prompt' => $prompt, 'title' => $title, 'alt_text' => $alt_text, 'quality' => $quality, 'size' => $size, 'parent_post_id' => $parent_id, 'reference_image_urls' => $reference_image_urls, 'force_regenerate' => $force_regenerate ) ) );
    }

    private function validate_generate_image_action_args( $args ) {
        $prompt = isset( $args['prompt'] ) ? trim( (string) $args['prompt'] ) : '';
        $title  = isset( $args['title'] ) ? trim( (string) $args['title'] ) : '';
        if ( '' === $prompt ) { return array( 'success' => false, 'error' => 'Prompt je obavezan za generate_image.' ); }
        if ( '' === $title ) { return array( 'success' => false, 'error' => 'Title je obavezan za generate_image.' ); }
        return array( 'success' => true );
    }

    private function get_default_generate_image_quality() {
        $settings = get_option( Niwa_AI_Admin_Autonomy_Telegram::OPTION_KEY, array() );
        $settings = is_array( $settings ) ? $settings : array();
        return $this->normalize_generate_image_quality( isset( $settings['founder_image_quality'] ) ? $settings['founder_image_quality'] : 'medium' );
    }

    private function normalize_generate_image_quality( $quality ) {
        $quality = sanitize_key( (string) $quality );
        return in_array( $quality, array( 'low', 'medium', 'high' ), true ) ? $quality : 'medium';
    }

    private function normalize_generate_image_size( $size ) {
        $size = strtolower( trim( (string) $size ) );
        return in_array( $size, array( '1024x1024', '1536x1024', '1024x1536' ), true ) ? $size : '1024x1024';
    }

    private function normalize_generate_image_reference_urls( $urls ) {
        $clean = array();
        foreach ( (array) $urls as $url ) {
            $url = esc_url_raw( trim( (string) $url ) );
            if ( '' === $url || ! preg_match( '#^https?://#i', $url ) ) {
                continue;
            }
            $clean[] = $url;
            if ( count( $clean ) >= 3 ) {
                break;
            }
        }

        return array_values( array_unique( $clean ) );
    }

    /**
     * Collect reference image URLs from a parent product/post so generated
     * lifestyle images preserve the real product identity. Uses ONLY the
     * featured image — gallery entries may already contain previously
     * generated lifestyle images, which must not feed back as references.
     *
     * @param int $parent_id Parent product/post ID.
     * @return array Reference URLs (0 or 1 entries).
     */
    private function collect_parent_product_reference_urls( $parent_id ) {
        $parent_id = intval( $parent_id );
        if ( $parent_id <= 0 || ! function_exists( 'get_post_thumbnail_id' ) || ! function_exists( 'wp_get_attachment_url' ) ) {
            return array();
        }

        $thumb_id = intval( get_post_thumbnail_id( $parent_id ) );
        if ( $thumb_id <= 0 ) {
            return array();
        }

        $url = wp_get_attachment_url( $thumb_id );
        if ( ! is_string( $url ) || '' === $url ) {
            return array();
        }

        return $this->normalize_generate_image_reference_urls( array( $url ) );
    }

    /**
     * Find a generated-image attachment with the exact same title from the
     * last 24 hours, so retried/cancelled turns reuse it instead of paying
     * another multi-minute generation.
     *
     * @param string $title Attachment title used at generation time.
     * @return int Attachment ID, or 0 when none found.
     */
    private function find_recent_generated_image_attachment( $title ) {
        if ( ! function_exists( 'get_posts' ) ) {
            return 0;
        }

        $found = get_posts(
            array(
                'post_type'      => 'attachment',
                'post_status'    => 'inherit',
                'post_mime_type' => 'image/png',
                'title'          => (string) $title,
                'posts_per_page' => 1,
                'orderby'        => 'date',
                'order'          => 'DESC',
                'fields'         => 'ids',
                'no_found_rows'  => true,
                'date_query'     => array(
                    array(
                        'after'  => gmdate( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS ),
                        'column' => 'post_date_gmt',
                    ),
                ),
            )
        );

        return ! empty( $found[0] ) ? intval( $found[0] ) : 0;
    }

    private function preview_restore_product_from_trash( $args ) {
        if ( ! class_exists( 'WooCommerce' ) ) {
            return array(
                'success' => false,
                'error'   => 'WooCommerce nije aktivan.',
            );
        }

        $product_query = trim( (string) ( isset( $args['product_query'] ) ? $args['product_query'] : '' ) );
        if ( '' === $product_query ) {
            return array(
                'success' => false,
                'error'   => 'product_query je obavezan za restore_product_from_trash.',
            );
        }

        $resolved = $this->resolve_product_target_for_write( 0, $product_query, array( 'trash' ), 'restore iz trash-a' );
        if ( empty( $resolved['success'] ) ) {
            return array(
                'success' => false,
                'error'   => isset( $resolved['error'] ) ? (string) $resolved['error'] : 'Nisam nasao trash proizvod za query: ' . $product_query,
            );
        }
        $product = $resolved['target'];

        $summary = 'Vrati proizvod "' . $product['post_title'] . '" iz trash-a.';
        if ( ! empty( $product['match_warning'] ) ) {
            $summary .= ' [UPOZORENJE: ' . (string) $product['match_warning'] . ']';
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'restore_product_from_trash',
                'label'            => 'Vrati proizvod iz trash-a',
                'summary'          => $summary,
                'rollback_plan'    => 'Vraca proizvod nazad u trash ako treba.',
                'rollback_support' => 'full',
                'targets'          => array( $product ),
                'args'             => array(
                    'product_id' => $product['post_id'],
                ),
            ),
        );
    }

    private function preview_update_simple_product( $args ) {
        if ( ! class_exists( 'WooCommerce' ) || ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije aktivan.' );
        }

        $product_id       = isset( $args['product_id'] ) ? intval( $args['product_id'] ) : 0;
        $product_query    = trim( (string) ( isset( $args['product_query'] ) ? $args['product_query'] : '' ) );

        // --- Parse all change fields ---
        $title             = isset( $args['title'] ) ? trim( (string) $args['title'] ) : null;
        $description       = isset( $args['description'] ) ? (string) $args['description'] : null;
        $short_description = isset( $args['short_description'] ) ? (string) $args['short_description'] : null;
        $sku               = isset( $args['sku'] ) ? trim( (string) $args['sku'] ) : null;
        $status            = isset( $args['status'] ) ? sanitize_key( (string) $args['status'] ) : null;
        $regular_price_in  = isset( $args['regular_price'] ) ? (string) $args['regular_price'] : null;
        $sale_price_in     = isset( $args['sale_price'] ) ? (string) $args['sale_price'] : null;
        $clear_sale_price  = ! empty( $args['clear_sale_price'] );
        $clear_sale_dates  = ! empty( $args['clear_sale_dates'] );
        $has_sale_from     = array_key_exists( 'date_on_sale_from', $args ) && null !== $args['date_on_sale_from'] && '' !== trim( (string) $args['date_on_sale_from'] );
        $has_sale_to       = array_key_exists( 'date_on_sale_to', $args ) && null !== $args['date_on_sale_to'] && '' !== trim( (string) $args['date_on_sale_to'] );
        $has_stock_qty     = array_key_exists( 'stock_quantity', $args ) && '' !== (string) $args['stock_quantity'] && null !== $args['stock_quantity'];
        $stock_quantity    = $has_stock_qty ? max( 0, intval( $args['stock_quantity'] ) ) : null;
        $weight            = isset( $args['weight'] ) ? trim( (string) $args['weight'] ) : null;
        $length            = isset( $args['length'] ) ? trim( (string) $args['length'] ) : null;
        $width             = isset( $args['width'] ) ? trim( (string) $args['width'] ) : null;
        $height            = isset( $args['height'] ) ? trim( (string) $args['height'] ) : null;
        $categories_in     = isset( $args['categories'] ) && is_array( $args['categories'] ) ? array_filter( array_map( 'trim', $args['categories'] ) ) : array();
        $category_action   = isset( $args['category_action'] ) ? sanitize_key( (string) $args['category_action'] ) : 'set';
        $tags_in           = isset( $args['tags'] ) && is_array( $args['tags'] ) ? array_filter( array_map( 'trim', $args['tags'] ) ) : array();
        $tag_action        = isset( $args['tag_action'] ) ? sanitize_key( (string) $args['tag_action'] ) : 'set';
        $has_categories    = ! empty( $categories_in );
        $has_tags          = ! empty( $tags_in );

        if ( $product_id <= 0 && '' === $product_query ) {
            return array( 'success' => false, 'error' => 'Treba mi product_id ili product_query za update_simple_product.' );
        }

        // Check at least one change is present.
        $has_any_change = ( null !== $title && '' !== $title )
            || null !== $description
            || null !== $short_description
            || ( null !== $sku && '' !== $sku )
            || ( null !== $status && '' !== $status )
            || null !== $regular_price_in
            || null !== $sale_price_in
            || $clear_sale_price
            || $clear_sale_dates
            || $has_sale_from
            || $has_sale_to
            || $has_stock_qty
            || ( null !== $weight && '' !== $weight )
            || ( null !== $length && '' !== $length )
            || ( null !== $width && '' !== $width )
            || ( null !== $height && '' !== $height )
            || $has_categories
            || $has_tags;

        if ( ! $has_any_change ) {
            return array( 'success' => false, 'error' => 'Nema nijedne Woo promene. Posalji bar jedno polje za izmenu.' );
        }

        // --- Resolve product ---
        $resolved_product = $this->resolve_product_target_for_write( $product_id, $product_query, array( 'publish', 'private', 'draft', 'pending', 'future', 'trash' ), 'update_simple_product' );
        if ( empty( $resolved_product['success'] ) ) {
            return array( 'success' => false, 'error' => isset( $resolved_product['error'] ) ? (string) $resolved_product['error'] : ( $product_id > 0 ? 'Nisam nasao Woo proizvod za product_id: ' . $product_id : 'Nisam nasao proizvod za query: ' . $product_query ) );
        }
        $product_target = $resolved_product['target'];
        if ( 'trash' === (string) $product_target['post_status'] && ( null === $status || 'trash' === $status ) ) {
            return array( 'success' => false, 'error' => 'Proizvod je trenutno u trash-u. Prvo ga vrati iz trash-a.' );
        }

        $product = wc_get_product( intval( $product_target['post_id'] ) );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Woo proizvod vise nije dostupan.' );
        }

        // Allow both simple and variable (and other types) — price/stock fields only apply when meaningful.
        $is_simple = $product->is_type( 'simple' );
        $has_price_or_stock_change = null !== $regular_price_in
            || null !== $sale_price_in
            || $clear_sale_price
            || $clear_sale_dates
            || $has_sale_from
            || $has_sale_to
            || $has_stock_qty;

        if ( $has_price_or_stock_change && ! $is_simple ) {
            return array(
                'success' => false,
                'error'   => 'Ciljni proizvod nije simple. Za variable parent koristi update_product_variation, bulk_update_variations ili bulk_set_individual_prices sa exact variation ID-jevima za cenu, sale cenu ili stock.',
            );
        }

        // --- Validate prices ---
        $regular_price = null;
        if ( null !== $regular_price_in && '' !== trim( $regular_price_in ) ) {
            $regular_price = $this->normalize_decimal_price( $regular_price_in );
            if ( null === $regular_price ) {
                return array( 'success' => false, 'error' => 'regular_price nije validan decimalni broj.' );
            }
        }
        $sale_price = null;
        if ( null !== $sale_price_in && '' !== trim( $sale_price_in ) ) {
            $sale_price = $this->normalize_decimal_price( $sale_price_in );
            if ( null === $sale_price ) {
                return array( 'success' => false, 'error' => 'sale_price nije validan decimalni broj.' );
            }
        }
        if ( $is_simple && null !== $sale_price ) {
            $effective_regular = null !== $regular_price ? floatval( $regular_price ) : floatval( $product->get_regular_price() );
            if ( $effective_regular > 0 && floatval( $sale_price ) >= $effective_regular ) {
                return array( 'success' => false, 'error' => 'Sale price mora biti manji od regular price.' );
            }
        }

        $date_on_sale_from = null;
        if ( $has_sale_from ) {
            $from_result = $this->normalize_variation_sale_schedule_date( (string) $args['date_on_sale_from'], 'from' );
            if ( empty( $from_result['success'] ) ) {
                return $from_result;
            }
            $date_on_sale_from = $from_result['value'];
        }

        $date_on_sale_to = null;
        if ( $has_sale_to ) {
            $to_result = $this->normalize_variation_sale_schedule_date( (string) $args['date_on_sale_to'], 'to' );
            if ( empty( $to_result['success'] ) ) {
                return $to_result;
            }
            $date_on_sale_to = $to_result['value'];
        }

        if ( null !== $date_on_sale_from && null !== $date_on_sale_to && strtotime( $date_on_sale_from ) > strtotime( $date_on_sale_to ) ) {
            return array( 'success' => false, 'error' => 'date_on_sale_from mora biti pre date_on_sale_to.' );
        }

        // --- Validate status ---
        if ( null !== $status && '' !== $status ) {
            $allowed_statuses = array( 'publish', 'draft', 'private', 'pending' );
            if ( ! in_array( $status, $allowed_statuses, true ) ) {
                return array( 'success' => false, 'error' => 'Nevalidan status. Dozvoljeni: publish, draft, private, pending.' );
            }
        }

        // --- Resolve categories ---
        $resolved_categories = array();
        if ( $has_categories ) {
            foreach ( $categories_in as $cat_name ) {
                $term = get_term_by( 'name', $cat_name, 'product_cat' );
                if ( ! $term ) {
                    $term = get_term_by( 'slug', sanitize_title( $cat_name ), 'product_cat' );
                }
                if ( $term && ! is_wp_error( $term ) ) {
                    $resolved_categories[] = array( 'term_id' => intval( $term->term_id ), 'name' => $term->name );
                } else {
                    // For set/add: create the category. For remove: skip non-existent.
                    if ( 'remove' !== $category_action ) {
                        $inserted = wp_insert_term( $cat_name, 'product_cat' );
                        if ( ! is_wp_error( $inserted ) ) {
                            $resolved_categories[] = array( 'term_id' => intval( $inserted['term_id'] ), 'name' => $cat_name );
                        }
                    }
                }
            }
        }

        // --- Resolve tags ---
        $resolved_tags = array();
        if ( $has_tags ) {
            foreach ( $tags_in as $tag_name ) {
                $term = get_term_by( 'name', $tag_name, 'product_tag' );
                if ( ! $term ) {
                    $term = get_term_by( 'slug', sanitize_title( $tag_name ), 'product_tag' );
                }
                if ( $term && ! is_wp_error( $term ) ) {
                    $resolved_tags[] = array( 'term_id' => intval( $term->term_id ), 'name' => $term->name );
                } else {
                    if ( 'remove' !== $tag_action ) {
                        $inserted = wp_insert_term( $tag_name, 'product_tag' );
                        if ( ! is_wp_error( $inserted ) ) {
                            $resolved_tags[] = array( 'term_id' => intval( $inserted['term_id'] ), 'name' => $tag_name );
                        }
                    }
                }
            }
        }

        // --- Build summary ---
        $summary_parts = array();
        if ( null !== $title && '' !== $title ) {
            $summary_parts[] = 'naziv -> "' . $title . '"';
        }
        if ( null !== $description ) {
            $summary_parts[] = 'opis (azuriran)';
        }
        if ( null !== $short_description ) {
            $summary_parts[] = 'kratki opis (azuriran)';
        }
        if ( null !== $sku && '' !== $sku ) {
            $summary_parts[] = 'SKU -> ' . $sku;
        }
        if ( null !== $status && '' !== $status ) {
            $summary_parts[] = 'status -> ' . $status;
        }
        if ( null !== $regular_price ) {
            $summary_parts[] = 'regular price ' . $this->format_price_display_value( '' !== (string) $product->get_regular_price() ? (string) $product->get_regular_price() : '-' ) . ' -> ' . $this->format_price_display_value( $regular_price );
        }
        if ( $clear_sale_price ) {
            $summary_parts[] = 'ukloni sale price';
        } elseif ( null !== $sale_price ) {
            $summary_parts[] = 'sale price ' . $this->format_price_display_value( '' !== (string) $product->get_sale_price() ? (string) $product->get_sale_price() : '-' ) . ' -> ' . $this->format_price_display_value( $sale_price );
        }
        if ( $clear_sale_dates ) {
            $summary_parts[] = 'ukloni sale schedule datume';
        } else {
            if ( null !== $date_on_sale_from ) {
                $summary_parts[] = 'sale start -> ' . $date_on_sale_from;
            }
            if ( null !== $date_on_sale_to ) {
                $summary_parts[] = 'sale end -> ' . $date_on_sale_to;
            }
        }
        if ( null !== $stock_quantity ) {
            $current_qty     = $product->get_stock_quantity();
            $summary_parts[] = 'stock qty ' . ( null !== $current_qty ? intval( $current_qty ) : '-' ) . ' -> ' . intval( $stock_quantity );
        }
        if ( null !== $weight && '' !== $weight ) {
            $summary_parts[] = 'tezina -> ' . $weight;
        }
        if ( ( null !== $length && '' !== $length ) || ( null !== $width && '' !== $width ) || ( null !== $height && '' !== $height ) ) {
            $summary_parts[] = 'dimenzije (azurirane)';
        }
        if ( $has_categories && ! empty( $resolved_categories ) ) {
            $cat_names = array_column( $resolved_categories, 'name' );
            $summary_parts[] = 'kategorije ' . $category_action . ': ' . implode( ', ', $cat_names );
        }
        if ( $has_tags && ! empty( $resolved_tags ) ) {
            $tag_names = array_column( $resolved_tags, 'name' );
            $summary_parts[] = 'tagovi ' . $tag_action . ': ' . implode( ', ', $tag_names );
        }

        $action_data = array(
            'type'             => 'update_simple_product',
            'label'            => 'Azuriraj Woo proizvod',
            'summary'          => 'Azuriraj "' . $product_target['post_title'] . '": ' . implode( '; ', $summary_parts ) . '.',
            'rollback_plan'    => 'Vraca prethodne vrednosti proizvoda.',
            'rollback_support' => 'full',
            'targets'          => array( $product_target ),
            'args'             => array(
                'product_id'        => intval( $product_target['post_id'] ),
                'title'             => ( null !== $title && '' !== $title ) ? $title : null,
                'description'       => $description,
                'short_description' => $short_description,
                'sku'               => ( null !== $sku && '' !== $sku ) ? $sku : null,
                'status'            => ( null !== $status && '' !== $status ) ? $status : null,
                'regular_price'     => null !== $regular_price ? (string) $regular_price : null,
                'sale_price'        => null !== $sale_price ? (string) $sale_price : null,
                'clear_sale_price'  => $clear_sale_price ? 1 : 0,
                'date_on_sale_from' => null !== $date_on_sale_from ? (string) $date_on_sale_from : null,
                'date_on_sale_to'   => null !== $date_on_sale_to ? (string) $date_on_sale_to : null,
                'clear_sale_dates'  => $clear_sale_dates ? 1 : 0,
                'stock_quantity'    => null !== $stock_quantity ? intval( $stock_quantity ) : null,
                'weight'            => ( null !== $weight && '' !== $weight ) ? $weight : null,
                'length'            => ( null !== $length && '' !== $length ) ? $length : null,
                'width'             => ( null !== $width && '' !== $width ) ? $width : null,
                'height'            => ( null !== $height && '' !== $height ) ? $height : null,
                'resolved_category_ids' => ! empty( $resolved_categories ) ? array_column( $resolved_categories, 'term_id' ) : null,
                'category_action'   => $has_categories ? $category_action : null,
                'resolved_tag_ids'  => ! empty( $resolved_tags ) ? array_column( $resolved_tags, 'term_id' ) : null,
                'tag_action'        => $has_tags ? $tag_action : null,
            ),
        );

        if ( ! empty( $product_target['match_warning'] ) ) {
            $action_data['match_warning'] = (string) $product_target['match_warning'];
            $action_data['summary']      .= ' [UPOZORENJE: ' . (string) $product_target['match_warning'] . ']';
        }

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

    private function preview_create_product( $args ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $title = isset( $args['title'] ) ? sanitize_text_field( trim( (string) $args['title'] ) ) : '';
        if ( '' === $title ) {
            return array( 'success' => false, 'error' => 'Naziv proizvoda (title) je obavezan.' );
        }

        $product_type = isset( $args['product_type'] ) && 'variable' === (string) $args['product_type'] ? 'variable' : 'simple';

        $regular_price = isset( $args['regular_price'] ) ? trim( (string) $args['regular_price'] ) : '';
        if ( 'simple' === $product_type && '' === $regular_price ) {
            return array( 'success' => false, 'error' => 'Regular price je obavezan za simple proizvod.' );
        }

        $sale_price = isset( $args['sale_price'] ) ? trim( (string) $args['sale_price'] ) : '';
        if ( '' !== $sale_price && '' !== $regular_price && floatval( $sale_price ) >= floatval( $regular_price ) ) {
            return array( 'success' => false, 'error' => 'Sale price mora biti manji od regular price.' );
        }

        /* Resolve categories. */
        $resolved_cat_ids = array();
        if ( ! empty( $args['categories'] ) && is_array( $args['categories'] ) ) {
            foreach ( $args['categories'] as $cat_input ) {
                $cat_name = sanitize_text_field( trim( (string) $cat_input ) );
                if ( '' === $cat_name ) {
                    continue;
                }
                $term = get_term_by( 'name', $cat_name, 'product_cat' );
                if ( ! $term ) {
                    $term = get_term_by( 'slug', sanitize_title( $cat_name ), 'product_cat' );
                }
                if ( $term && ! is_wp_error( $term ) ) {
                    $resolved_cat_ids[] = intval( $term->term_id );
                } else {
                    $inserted = wp_insert_term( $cat_name, 'product_cat' );
                    if ( ! is_wp_error( $inserted ) ) {
                        $resolved_cat_ids[] = intval( $inserted['term_id'] );
                    }
                }
            }
        }

        /* Resolve tags. */
        $resolved_tag_ids = array();
        if ( ! empty( $args['tags'] ) && is_array( $args['tags'] ) ) {
            foreach ( $args['tags'] as $tag_input ) {
                $tag_name = sanitize_text_field( trim( (string) $tag_input ) );
                if ( '' === $tag_name ) {
                    continue;
                }
                $term = get_term_by( 'name', $tag_name, 'product_tag' );
                if ( ! $term ) {
                    $term = get_term_by( 'slug', sanitize_title( $tag_name ), 'product_tag' );
                }
                if ( $term && ! is_wp_error( $term ) ) {
                    $resolved_tag_ids[] = intval( $term->term_id );
                } else {
                    $inserted = wp_insert_term( $tag_name, 'product_tag' );
                    if ( ! is_wp_error( $inserted ) ) {
                        $resolved_tag_ids[] = intval( $inserted['term_id'] );
                    }
                }
            }
        }

        $summary_parts = array();
        $summary_parts[] = 'Kreiranje ' . $product_type . ' proizvoda: "' . $title . '"';
        if ( '' !== $regular_price ) {
            $summary_parts[] = 'Cena: ' . $regular_price;
        }
        if ( '' !== $sale_price ) {
            $summary_parts[] = 'Akcijska cena: ' . $sale_price;
        }
        $sku = isset( $args['sku'] ) ? trim( (string) $args['sku'] ) : '';
        if ( '' !== $sku ) {
            $summary_parts[] = 'SKU: ' . $sku;
        }
        $status = isset( $args['status'] ) && in_array( (string) $args['status'], array( 'publish', 'draft' ), true ) ? (string) $args['status'] : 'publish';
        $summary_parts[] = 'Status: ' . $status;
        if ( ! empty( $resolved_cat_ids ) ) {
            $summary_parts[] = 'Kategorije: ' . count( $resolved_cat_ids );
        }
        if ( ! empty( $resolved_tag_ids ) ) {
            $summary_parts[] = 'Tagovi: ' . count( $resolved_tag_ids );
        }
        if ( array_key_exists( 'stock_quantity', $args ) && null !== $args['stock_quantity'] ) {
            $summary_parts[] = 'Stock: ' . intval( $args['stock_quantity'] );
        }

        $action_data = array(
            'type'                  => 'create_product',
            'args'                  => array(
                'product_type'        => $product_type,
                'title'               => $title,
                'description'         => isset( $args['description'] ) ? (string) $args['description'] : '',
                'short_description'   => isset( $args['short_description'] ) ? (string) $args['short_description'] : '',
                'regular_price'       => $regular_price,
                'sale_price'          => $sale_price,
                'sku'                 => $sku,
                'status'              => $status,
                'weight'              => isset( $args['weight'] ) ? trim( (string) $args['weight'] ) : '',
                'length'              => isset( $args['length'] ) ? trim( (string) $args['length'] ) : '',
                'width'               => isset( $args['width'] ) ? trim( (string) $args['width'] ) : '',
                'height'              => isset( $args['height'] ) ? trim( (string) $args['height'] ) : '',
                'manage_stock'        => ! empty( $args['manage_stock'] ) || ( array_key_exists( 'stock_quantity', $args ) && null !== $args['stock_quantity'] ),
                'stock_quantity'      => array_key_exists( 'stock_quantity', $args ) && null !== $args['stock_quantity'] ? intval( $args['stock_quantity'] ) : null,
                'resolved_category_ids' => $resolved_cat_ids,
                'resolved_tag_ids'      => $resolved_tag_ids,
            ),
            'summary'               => implode( ' | ', $summary_parts ),
        );

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

    private function preview_manage_product_gallery( $args ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $product_id = isset( $args['product_id'] ) ? intval( $args['product_id'] ) : 0;
        $product    = null;
        $product_target = array();

        if ( $product_id > 0 ) {
            $product = wc_get_product( $product_id );
            if ( $product ) {
                $post = get_post( $product_id );
                if ( $post && 'product' === (string) $post->post_type ) {
                    $product_target = $this->build_product_target( $post, $product );
                }
            }
        }

        if ( ! $product && ! empty( $args['product_query'] ) ) {
            $resolved = $this->resolve_product_target_for_write( 0, (string) $args['product_query'], array( 'publish', 'private', 'draft', 'pending', 'future' ), 'gallery upravljanje' );
            if ( ! empty( $resolved['success'] ) ) {
                $product_id     = intval( $resolved['product_id'] );
                $product        = wc_get_product( $product_id );
                $product_target = $resolved['target'];
            }
        }

        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen za gallery upravljanje.' );
        }

        $image_urls = array();
        if ( ! empty( $args['image_urls'] ) && is_array( $args['image_urls'] ) ) {
            foreach ( $args['image_urls'] as $url ) {
                $url = esc_url_raw( trim( (string) $url ) );
                if ( '' !== $url ) {
                    $image_urls[] = $url;
                }
            }
        }

        $attachment_ids = array();
        if ( ! empty( $args['attachment_ids'] ) && is_array( $args['attachment_ids'] ) ) {
            foreach ( $args['attachment_ids'] as $attachment_id ) {
                $attachment_id = intval( $attachment_id );
                if ( $attachment_id > 0 ) {
                    $attachment_ids[] = $attachment_id;
                }
            }
            $attachment_ids = array_values( array_unique( $attachment_ids ) );
        }

        if ( empty( $image_urls ) && empty( $attachment_ids ) ) {
            return array( 'success' => false, 'error' => 'Nema image URL-ova ili attachment ID-jeva za galeriju.' );
        }

        $gallery_action = isset( $args['action'] ) && in_array( (string) $args['action'], array( 'set', 'add', 'remove' ), true ) ? (string) $args['action'] : 'set';
        $current_ids    = $product->get_gallery_image_ids();

        $incoming_count = count( $image_urls ) + count( $attachment_ids );
        $summary = ucfirst( $gallery_action ) . ' ' . $incoming_count . ' slika u galeriji za "' . $product->get_name() . '" (trenutno: ' . count( $current_ids ) . ' slika)';

        return array(
            'success' => true,
            'action'  => array(
                'type'       => 'manage_product_gallery',
                'targets'    => ! empty( $product_target ) ? array( $product_target ) : array(),
                'args'       => array(
                    'product_id'      => $product_id,
                    'image_urls'      => $image_urls,
                    'attachment_ids'  => $attachment_ids,
                    'action'          => $gallery_action,
                ),
                'summary'    => $summary,
            ),
        );
    }

    private function preview_manage_product_attributes( $args ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $product_id = isset( $args['product_id'] ) ? intval( $args['product_id'] ) : 0;
        $product    = null;
        $product_target = array();

        if ( $product_id > 0 ) {
            $product = wc_get_product( $product_id );
            if ( $product ) {
                $post = get_post( $product_id );
                if ( $post && 'product' === (string) $post->post_type ) {
                    $product_target = $this->build_product_target( $post, $product );
                }
            }
        }

        if ( ! $product && ! empty( $args['product_query'] ) ) {
            $resolved = $this->resolve_product_target_for_write( 0, (string) $args['product_query'], array( 'publish', 'private', 'draft', 'pending', 'future' ), 'upravljanje atributima' );
            if ( ! empty( $resolved['success'] ) ) {
                $product_target = $resolved['target'];
                $product_id     = intval( $resolved['product_id'] );
                $product        = wc_get_product( $product_id );
            }
        }

        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen za upravljanje atributima.' );
        }

        $attr_name = isset( $args['attribute_name'] ) ? sanitize_text_field( trim( (string) $args['attribute_name'] ) ) : '';
        if ( '' === $attr_name ) {
            return array( 'success' => false, 'error' => 'Naziv atributa (attribute_name) je obavezan.' );
        }

        $attr_action = isset( $args['action'] ) && 'remove' === (string) $args['action'] ? 'remove' : 'add';
        $values      = array();
        if ( ! empty( $args['attribute_values'] ) && is_array( $args['attribute_values'] ) ) {
            foreach ( $args['attribute_values'] as $v ) {
                $v = sanitize_text_field( trim( (string) $v ) );
                if ( '' !== $v ) {
                    $values[] = $v;
                }
            }
        }

        if ( 'add' === $attr_action && empty( $values ) ) {
            return array( 'success' => false, 'error' => 'attribute_values je obavezan za add akciju.' );
        }

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

        $existing_attrs = $product->get_attributes();
        $attr_slug      = $this->resolve_existing_attribute_taxonomy( $attr_name );
        if ( '' === $attr_slug ) {
            $attr_slug = 'pa_' . sanitize_title( $attr_name );
        }

        $summary = '';
        if ( 'add' === $attr_action ) {
            $summary = 'Dodajem atribut "' . $attr_name . '" sa vrednostima [' . implode( ', ', $values ) . '] na "' . $product->get_name() . '"';
            if ( $used_for_variations ) {
                $summary .= ' (za varijacije)';
            }
        } else {
            $summary = 'Uklanjam atribut "' . $attr_name . '" sa "' . $product->get_name() . '"';
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'    => 'manage_product_attributes',
                'targets' => ! empty( $product_target ) ? array( $product_target ) : array(),
                'args'    => array(
                    'product_id'          => $product_id,
                    'action'              => $attr_action,
                    'attribute_name'      => $attr_name,
                    'attribute_slug'      => $attr_slug,
                    'attribute_values'    => $values,
                    'visible'             => $visible,
                    'used_for_variations' => $used_for_variations,
                ),
                'summary' => $summary,
            ),
        );
    }

    private function preview_remove_color( $args ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $product_id     = isset( $args['product_id'] ) ? intval( $args['product_id'] ) : 0;
        $product_target = array();
        if ( $product_id > 0 ) {
            $post    = get_post( $product_id );
            $product = wc_get_product( $product_id );
            if ( $post && 'product' === (string) $post->post_type && $product ) {
                $product_target = $this->build_product_target( $post, $product );
            }
        } elseif ( ! empty( $args['product_query'] ) ) {
            $resolved = $this->resolve_product_target_for_write( 0, (string) $args['product_query'], array( 'publish', 'private', 'draft', 'pending', 'future' ), 'remove_color' );
            if ( ! empty( $resolved['success'] ) ) {
                $product_target = $resolved['target'];
                $product_id     = intval( $resolved['product_id'] );
            }
        }

        $product = $product_id > 0 ? wc_get_product( $product_id ) : false;
        if ( ! $product || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'remove_color trazi variable proizvod.' );
        }

        $color = sanitize_text_field( trim( (string) ( isset( $args['color'] ) ? $args['color'] : '' ) ) );
        if ( '' === $color ) {
            return array( 'success' => false, 'error' => 'Boja je obavezna za remove_color.' );
        }

        $summary = 'Ukloni boju "' . $color . '" sa proizvoda "' . $product->get_name() . '" i obrisi sve varijacije te boje.';
        if ( ! empty( $product_target['match_warning'] ) ) {
            $summary .= ' [UPOZORENJE: ' . (string) $product_target['match_warning'] . ']';
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'remove_color',
                'label'            => 'Ukloni boju sa variable proizvoda',
                'summary'          => $summary,
                'rollback_plan'    => 'Vraca prethodne atribute proizvoda i ponovo kreira obrisane varijacije te boje iz snapshot-a.',
                'rollback_support' => 'full',
                'targets'          => ! empty( $product_target ) ? array( $product_target ) : array(),
                'args'             => array(
                    'product_id' => $product_id,
                    'color'      => $color,
                ),
            ),
        );
    }

    private function preview_move_to_outlet( $args ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $product_id     = isset( $args['product_id'] ) ? intval( $args['product_id'] ) : 0;
        $product_target = array();
        if ( $product_id > 0 ) {
            $post    = get_post( $product_id );
            $product = wc_get_product( $product_id );
            if ( $post && 'product' === (string) $post->post_type && $product ) {
                $product_target = $this->build_product_target( $post, $product );
            }
        } elseif ( ! empty( $args['product_query'] ) ) {
            $resolved = $this->resolve_product_target_for_write( 0, (string) $args['product_query'], array( 'publish', 'private', 'draft', 'pending', 'future' ), 'move_to_outlet' );
            if ( ! empty( $resolved['success'] ) ) {
                $product_target = $resolved['target'];
                $product_id     = intval( $resolved['product_id'] );
            }
        }

        $product = $product_id > 0 ? wc_get_product( $product_id ) : false;
        if ( ! $product || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'move_to_outlet trazi variable proizvod.' );
        }

        $specs            = isset( $args['specs'] ) && is_array( $args['specs'] ) ? array_values( $args['specs'] ) : array();
        $normalized_specs = array();
        foreach ( $specs as $spec ) {
            if ( ! is_array( $spec ) ) {
                continue;
            }

            $color = sanitize_text_field( trim( (string) ( isset( $spec['color'] ) ? $spec['color'] : '' ) ) );
            $size  = sanitize_text_field( trim( (string) ( isset( $spec['size'] ) ? $spec['size'] : '' ) ) );
            if ( '' === $color || '' === $size ) {
                continue;
            }

            $normalized_specs[] = array(
                'color' => $color,
                'size'  => $size,
                'qty'   => max( 0, intval( isset( $spec['qty'] ) ? $spec['qty'] : 1 ) ),
            );
        }

        if ( empty( $normalized_specs ) ) {
            return array( 'success' => false, 'error' => 'move_to_outlet zahteva specs sa color, size i qty.' );
        }

        $summary = 'Premesti "' . $product->get_name() . '" u outlet i rekonstruise outlet varijacije prema dostavljenom spisku (' . count( $normalized_specs ) . ' stavki).';
        if ( ! empty( $product_target['match_warning'] ) ) {
            $summary .= ' [UPOZORENJE: ' . (string) $product_target['match_warning'] . ']';
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'move_to_outlet',
                'label'            => 'Premesti proizvod u outlet',
                'summary'          => $summary,
                'rollback_plan'    => 'Vraca prethodne kategorije, atribute i sve prethodne varijacije proizvoda, a brise novokreirane outlet varijacije.',
                'rollback_support' => 'full',
                'targets'          => ! empty( $product_target ) ? array( $product_target ) : array(),
                'args'             => array(
                    'product_id' => $product_id,
                    'specs'      => $normalized_specs,
                ),
            ),
        );
    }

    private function execute_create_product_action( $action ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $args = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $product_type = isset( $args['product_type'] ) ? (string) $args['product_type'] : 'simple';

        if ( 'variable' === $product_type ) {
            $product = new \WC_Product_Variable();
        } else {
            $product = new \WC_Product_Simple();
        }

        $product->set_name( sanitize_text_field( (string) $args['title'] ) );

        if ( ! empty( $args['description'] ) ) {
            $product->set_description( wp_kses_post( (string) $args['description'] ) );
        }
        if ( ! empty( $args['short_description'] ) ) {
            $product->set_short_description( wp_kses_post( (string) $args['short_description'] ) );
        }

        /* Prices — only for simple products. */
        if ( 'simple' === $product_type ) {
            if ( ! empty( $args['regular_price'] ) ) {
                $product->set_regular_price( (string) $args['regular_price'] );
            }
            if ( ! empty( $args['sale_price'] ) ) {
                $product->set_sale_price( (string) $args['sale_price'] );
            }
        }

        if ( ! empty( $args['sku'] ) ) {
            try {
                $product->set_sku( wc_clean( (string) $args['sku'] ) );
            } catch ( \Exception $e ) {
                return array( 'success' => false, 'error' => 'SKU greska: ' . $e->getMessage() );
            }
        }

        $status = isset( $args['status'] ) && in_array( (string) $args['status'], array( 'publish', 'draft' ), true ) ? (string) $args['status'] : 'publish';
        $product->set_status( $status );

        if ( ! empty( $args['weight'] ) ) {
            $product->set_weight( (string) $args['weight'] );
        }
        if ( ! empty( $args['length'] ) ) {
            $product->set_length( (string) $args['length'] );
        }
        if ( ! empty( $args['width'] ) ) {
            $product->set_width( (string) $args['width'] );
        }
        if ( ! empty( $args['height'] ) ) {
            $product->set_height( (string) $args['height'] );
        }

        /* Stock management. */
        if ( ! empty( $args['manage_stock'] ) ) {
            $product->set_manage_stock( true );
            if ( null !== $args['stock_quantity'] ) {
                $qty = max( 0, intval( $args['stock_quantity'] ) );
                $product->set_stock_quantity( $qty );
                $product->set_stock_status( $qty > 0 ? 'instock' : 'outofstock' );
            }
        }

        /* Visibility & display. */
        if ( ! empty( $args['catalog_visibility'] ) && in_array( (string) $args['catalog_visibility'], array( 'visible', 'catalog', 'search', 'hidden' ), true ) ) {
            $product->set_catalog_visibility( (string) $args['catalog_visibility'] );
        }
        if ( array_key_exists( 'featured', $args ) && null !== $args['featured'] ) {
            $product->set_featured( (bool) $args['featured'] );
        }
        if ( array_key_exists( 'virtual', $args ) && null !== $args['virtual'] ) {
            $product->set_virtual( (bool) $args['virtual'] );
        }
        if ( array_key_exists( 'downloadable', $args ) && null !== $args['downloadable'] ) {
            $product->set_downloadable( (bool) $args['downloadable'] );
        }

        /* Tax. */
        if ( ! empty( $args['tax_status'] ) && in_array( (string) $args['tax_status'], array( 'taxable', 'shipping', 'none' ), true ) ) {
            $product->set_tax_status( (string) $args['tax_status'] );
        }
        if ( array_key_exists( 'tax_class', $args ) && null !== $args['tax_class'] ) {
            $product->set_tax_class( (string) $args['tax_class'] );
        }

        /* Purchase rules. */
        if ( array_key_exists( 'sold_individually', $args ) && null !== $args['sold_individually'] ) {
            $product->set_sold_individually( (bool) $args['sold_individually'] );
        }
        if ( ! empty( $args['backorders'] ) && in_array( (string) $args['backorders'], array( 'no', 'notify', 'yes' ), true ) ) {
            $product->set_backorders( (string) $args['backorders'] );
        }
        if ( array_key_exists( 'low_stock_amount', $args ) && null !== $args['low_stock_amount'] ) {
            $product->set_low_stock_amount( intval( $args['low_stock_amount'] ) );
        }
        if ( ! empty( $args['purchase_note'] ) ) {
            $product->set_purchase_note( wp_kses_post( (string) $args['purchase_note'] ) );
        }
        if ( array_key_exists( 'reviews_allowed', $args ) && null !== $args['reviews_allowed'] ) {
            $product->set_reviews_allowed( (bool) $args['reviews_allowed'] );
        }
        if ( array_key_exists( 'menu_order', $args ) && null !== $args['menu_order'] ) {
            $product->set_menu_order( intval( $args['menu_order'] ) );
        }

        $product_id = $product->save();
        if ( ! $product_id ) {
            return array( 'success' => false, 'error' => 'WC proizvod nije kreiran.' );
        }

        /* Categories. */
        if ( ! empty( $args['resolved_category_ids'] ) && is_array( $args['resolved_category_ids'] ) ) {
            wp_set_object_terms( $product_id, array_map( 'intval', $args['resolved_category_ids'] ), 'product_cat', false );
        }

        /* Tags. */
        if ( ! empty( $args['resolved_tag_ids'] ) && is_array( $args['resolved_tag_ids'] ) ) {
            wp_set_object_terms( $product_id, array_map( 'intval', $args['resolved_tag_ids'] ), 'product_tag', false );
        }

        $this->sync_product_ops_write( $product_id );

        return array(
            'success'          => true,
            'summary'          => 'Kreiran novi ' . $product_type . ' proizvod "' . get_the_title( $product_id ) . '" (ID: ' . $product_id . ').',
            'executed_actions' => array(
                array(
                    'type'       => 'create_product',
                    'product_id' => $product_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'       => 'product_delete',
                    'product_id' => $product_id,
                ),
            ),
        );
    }

    private function execute_manage_product_gallery_action( $action ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $product_id = intval( isset( $args['product_id'] ) ? $args['product_id'] : 0 );
        $product    = wc_get_product( $product_id );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'WC proizvod nije pronadjen.' );
        }

        $gallery_action = isset( $args['action'] ) ? (string) $args['action'] : 'set';
        $image_urls     = isset( $args['image_urls'] ) && is_array( $args['image_urls'] ) ? $args['image_urls'] : array();
        $attachment_ids = isset( $args['attachment_ids'] ) && is_array( $args['attachment_ids'] ) ? $args['attachment_ids'] : array();
        $current_ids    = $product->get_gallery_image_ids();

        /* Rollback entry — save current gallery state. */
        $rollback_entry = array(
            'type'                  => 'gallery_restore',
            'product_id'            => $product_id,
            'previous_gallery_ids'  => $current_ids,
        );

        $has_ingest = class_exists( 'Niwa_AI_Admin_Autonomy_Image_Utils' ) && method_exists( 'Niwa_AI_Admin_Autonomy_Image_Utils', 'ingest_from_url' );

        /* Keep Telegram-ingested attachment IDs in order, then ingest each image URL. */
        $new_ids  = array();
        $failures = array();
        foreach ( $attachment_ids as $attachment_id ) {
            $attachment_id = intval( $attachment_id );
            if ( $attachment_id <= 0 ) {
                continue;
            }
            $post = get_post( $attachment_id );
            if ( ! $post || 'attachment' !== (string) $post->post_type || 0 !== strpos( (string) $post->post_mime_type, 'image/' ) ) {
                $failures[] = 'attachment_id:' . $attachment_id;
                continue;
            }
            $new_ids[] = $attachment_id;
        }

        foreach ( $image_urls as $url ) {
            $url = esc_url_raw( trim( (string) $url ) );
            if ( '' === $url ) {
                continue;
            }

            if ( $has_ingest ) {
                $ingested = \Niwa_AI_Admin_Autonomy_Image_Utils::ingest_from_url( $url );
                if ( ! empty( $ingested['attachment_id'] ) ) {
                    $new_ids[] = intval( $ingested['attachment_id'] );
                } else {
                    $failures[] = $url;
                }
            } else {
                $failures[] = $url . ' (ingest nedostupan)';
            }
        }
        $new_ids = array_values( array_unique( array_filter( array_map( 'intval', $new_ids ) ) ) );

        if ( empty( $new_ids ) && ! empty( $failures ) ) {
            return array( 'success' => false, 'error' => 'Nijedna slika nije uspesno ucitana. Greske: ' . implode( ', ', $failures ) );
        }

        /* Apply gallery action. */
        if ( 'remove' === $gallery_action ) {
            $updated_ids = array_values( array_diff( $current_ids, $new_ids ) );
        } elseif ( 'add' === $gallery_action ) {
            $merged = array_unique( array_merge( $current_ids, $new_ids ) );
            $updated_ids = array_values( $merged );
        } else {
            /* set — replace */
            $updated_ids = $new_ids;
        }

        $current_compare = array_values( array_map( 'intval', $current_ids ) );
        $updated_compare = array_values( array_map( 'intval', $updated_ids ) );
        if ( $updated_compare === $current_compare ) {
            return array(
                'success'    => false,
                'error'      => 'Galerija proizvoda je vec u trazenom stanju; nema slike za dodavanje, uklanjanje ili zamenu.',
                'product_id' => $product_id,
                'action'     => $gallery_action,
            );
        }

        $product->set_gallery_image_ids( $updated_ids );
        $product->save();

        $this->sync_product_ops_write( $product_id );

        $summary = ucfirst( $gallery_action ) . ' ' . count( $new_ids ) . ' slika u galeriji za "' . $product->get_name() . '".';
        if ( ! empty( $failures ) ) {
            $summary .= ' Neuspesno: ' . count( $failures ) . '.';
        }

        return array(
            'success'          => true,
            'summary'          => $summary,
            'executed_actions' => array(
                array(
                    'type'       => 'manage_product_gallery',
                    'product_id' => $product_id,
                    'action'     => $gallery_action,
                    'added'      => count( $new_ids ),
                    'final_count' => count( $updated_ids ),
                    'failed'     => count( $failures ),
                ),
            ),
            'rollback_entries' => array( $rollback_entry ),
        );
    }

    private function execute_manage_product_attributes_action( $action ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $product_id = intval( isset( $args['product_id'] ) ? $args['product_id'] : 0 );
        $product    = wc_get_product( $product_id );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'WC proizvod nije pronadjen.' );
        }

        $attr_action = isset( $args['action'] ) ? (string) $args['action'] : 'add';
        $attr_name   = isset( $args['attribute_name'] ) ? (string) $args['attribute_name'] : '';
        $attr_slug   = isset( $args['attribute_slug'] ) ? (string) $args['attribute_slug'] : '';
        if ( '' === $attr_slug ) {
            /* Try to match an existing WC attribute by normalised name comparison
             * so that "Tezina" (without diacritics) resolves to "pa_težina". */
            $attr_slug = $this->resolve_existing_attribute_taxonomy( $attr_name );
            if ( '' === $attr_slug ) {
                $attr_slug = 'pa_' . sanitize_title( $attr_name );
            }
        }
        $values      = isset( $args['attribute_values'] ) && is_array( $args['attribute_values'] ) ? $args['attribute_values'] : array();
        $visible     = isset( $args['visible'] ) ? (bool) $args['visible'] : true;
        $for_variations = isset( $args['used_for_variations'] ) ? (bool) $args['used_for_variations'] : true;

        /* Save current attributes for rollback. */
        $prev_attributes_raw = array();
        foreach ( $product->get_attributes() as $key => $attr_obj ) {
            if ( is_a( $attr_obj, 'WC_Product_Attribute' ) ) {
                $prev_attributes_raw[ $key ] = array(
                    'name'      => $attr_obj->get_name(),
                    'options'   => $attr_obj->get_options(),
                    'visible'   => $attr_obj->get_visible(),
                    'variation' => $attr_obj->get_variation(),
                );
            }
        }

        $rollback_entry = array(
            'type'                 => 'attribute_restore',
            'product_id'           => $product_id,
            'previous_attributes'  => $prev_attributes_raw,
        );

        if ( $this->product_management_service && method_exists( $this->product_management_service, 'update_named_attribute' ) ) {
            $service_result = $this->product_management_service->update_named_attribute(
                $product_id,
                array(
                    'action'              => $attr_action,
                    'attribute_name'      => $attr_name,
                    'attribute_slug'      => $attr_slug,
                    'attribute_values'    => $values,
                    'visible'             => $visible,
                    'used_for_variations' => $for_variations,
                )
            );

            if ( empty( $service_result['success'] ) ) {
                return array(
                    'success' => false,
                    'error'   => isset( $service_result['error'] ) ? (string) $service_result['error'] : 'Product management attribute update nije uspela.',
                );
            }

            $summary = 'remove' === $attr_action
                ? 'Uklonjen atribut "' . $attr_name . '" sa "' . $product->get_name() . '".'
                : 'Azuriran atribut "' . $attr_name . '" na "' . $product->get_name() . '".';
            $unchanged = ! empty( $service_result['unchanged'] );
            if ( $unchanged ) {
                $summary = 'Atribut "' . $attr_name . '" na "' . $product->get_name() . '" vec odgovara trazenom stanju.';
            }

            return array(
                'success'          => true,
                'unchanged'        => $unchanged,
                'summary'          => $summary,
                'executed_actions' => array(
                    array(
                        'type'       => 'manage_product_attributes',
                        'product_id' => $product_id,
                        'action'     => $attr_action,
                        'attribute'  => $attr_name,
                        'unchanged'  => $unchanged,
                    ),
                ),
                'rollback_entries' => $unchanged ? array() : array( $rollback_entry ),
            );
        }

        $attributes = $product->get_attributes();

        if ( 'remove' === $attr_action ) {
            /* Remove attribute by slug. */
            $removed = false;
            foreach ( $attributes as $key => $attr_obj ) {
                if ( is_a( $attr_obj, 'WC_Product_Attribute' ) && $attr_obj->get_name() === $attr_slug ) {
                    unset( $attributes[ $key ] );
                    $removed = true;
                    break;
                }
            }
            if ( ! $removed ) {
                return array( 'success' => false, 'error' => 'Atribut "' . $attr_name . '" ne postoji na ovom proizvodu.' );
            }
            $product->set_attributes( array_values( $attributes ) );
            $product->save();
            $this->sync_product_ops_write( $product_id );

            return array(
                'success'          => true,
                'summary'          => 'Uklonjen atribut "' . $attr_name . '" sa "' . $product->get_name() . '".',
                'executed_actions' => array( array( 'type' => 'manage_product_attributes', 'product_id' => $product_id, 'action' => 'remove', 'attribute' => $attr_name ) ),
                'rollback_entries' => array( $rollback_entry ),
            );
        }

        /* ── Add attribute ── */

        /* Ensure global taxonomy exists. */
        $taxonomy = $attr_slug;
        if ( ! taxonomy_exists( $taxonomy ) ) {
            $wc_attr_id = wc_create_attribute( array(
                'name'         => $attr_name,
                'slug'         => sanitize_title( $attr_name ),
                'type'         => 'select',
                'order_by'     => 'menu_order',
                'has_archives' => false,
            ) );

            if ( is_wp_error( $wc_attr_id ) ) {
                /* Might already exist — try to get the existing attribute. */
                $existing = wc_attribute_taxonomy_id_by_name( $attr_name );
                if ( ! $existing ) {
                    return array( 'success' => false, 'error' => 'Kreiranje atributa neuspesno: ' . $wc_attr_id->get_error_message() );
                }
            }

            /* Register the taxonomy immediately so wp_insert_term works. */
            register_taxonomy( $taxonomy, array( 'product' ), array(
                'hierarchical' => false,
                'show_ui'      => false,
                'query_var'    => true,
                'rewrite'      => false,
            ) );
        }

        /* Insert terms. */
        $term_ids = array();
        foreach ( $values as $value ) {
            $value = sanitize_text_field( trim( (string) $value ) );
            if ( '' === $value ) {
                continue;
            }
            $existing_term = get_term_by( 'name', $value, $taxonomy );
            if ( $existing_term && ! is_wp_error( $existing_term ) ) {
                $term_ids[] = intval( $existing_term->term_id );
            } else {
                $inserted = wp_insert_term( $value, $taxonomy );
                if ( ! is_wp_error( $inserted ) ) {
                    $term_ids[] = intval( $inserted['term_id'] );
                }
            }
        }

        /* Check if attribute already exists on product — merge if so. */
        $attr_exists       = false;
        $assigned_term_ids = array();
        if ( taxonomy_exists( $taxonomy ) ) {
            $current_terms = wp_get_object_terms( $product_id, $taxonomy, array( 'fields' => 'ids' ) );
            if ( ! is_wp_error( $current_terms ) ) {
                $assigned_term_ids = array_values( array_filter( array_map( 'intval', (array) $current_terms ) ) );
            }
        }
        foreach ( $attributes as $key => $attr_obj ) {
            if ( is_a( $attr_obj, 'WC_Product_Attribute' ) && $attr_obj->get_name() === $taxonomy ) {
                $existing_options = array_map( 'intval', (array) $attr_obj->get_options() );
                $merged           = array_values( array_unique( array_merge( $existing_options, $term_ids ) ) );
                $existing_compare = array_values( array_unique( array_filter( $existing_options ) ) );
                $merged_compare   = array_values( array_unique( array_filter( $merged ) ) );
                sort( $existing_compare );
                sort( $merged_compare );

                $assigned_compare = array_values( array_unique( array_filter( $assigned_term_ids ) ) );
                $term_compare     = array_values( array_unique( array_filter( $term_ids ) ) );
                sort( $assigned_compare );
                sort( $term_compare );
                $terms_already_assigned = empty( array_diff( $term_compare, $assigned_compare ) );

                if (
                    $existing_compare === $merged_compare
                    && (bool) $attr_obj->get_visible() === $visible
                    && (bool) $attr_obj->get_variation() === $for_variations
                    && $terms_already_assigned
                ) {
                    return array( 'success' => false, 'error' => 'Atribut "' . $attr_name . '" je vec u trazenom stanju.' );
                }

                $attributes[ $key ] = $this->clone_product_ops_attribute_with_options( $attr_obj, $merged, $visible, $for_variations );
                $attr_exists = true;
                break;
            }
        }

        if ( ! $attr_exists ) {
            $new_attr = new \WC_Product_Attribute();
            $new_attr->set_id( wc_attribute_taxonomy_id_by_name( $attr_name ) );
            $new_attr->set_name( $taxonomy );
            $new_attr->set_options( $term_ids );
            $new_attr->set_visible( $visible );
            $new_attr->set_variation( $for_variations );
            $new_attr->set_position( count( $attributes ) );
            $attributes[] = $new_attr;
        }

        $product->set_attributes( $attributes );
        $product->save();

        /* Assign terms to product post. */
        if ( ! empty( $term_ids ) && ( ! $attr_exists || ! empty( array_diff( $term_ids, $assigned_term_ids ) ) ) ) {
            wp_set_object_terms( $product_id, $term_ids, $taxonomy, true );
        }

        $this->sync_product_ops_write( $product_id );

        return array(
            'success'          => true,
            'summary'          => 'Dodat atribut "' . $attr_name . '" sa ' . count( $values ) . ' vrednosti na "' . $product->get_name() . '".',
            'executed_actions' => array(
                array(
                    'type'       => 'manage_product_attributes',
                    'product_id' => $product_id,
                    'action'     => 'add',
                    'attribute'  => $attr_name,
                    'values'     => $values,
                ),
            ),
            'rollback_entries' => array( $rollback_entry ),
        );
    }

    private function clone_product_ops_attribute_with_options( $attribute, $options, $visible, $variation ) {
        $updated_attribute = new \WC_Product_Attribute();
        $updated_attribute->set_id( $attribute->get_id() );
        $updated_attribute->set_name( $attribute->get_name() );
        $updated_attribute->set_options( $options );
        $updated_attribute->set_position( $attribute->get_position() );
        $updated_attribute->set_visible( (bool) $visible );
        $updated_attribute->set_variation( (bool) $variation );

        return $updated_attribute;
    }

    /**
     * Resolve an attribute name (possibly without diacritics) to an existing
     * WooCommerce attribute taxonomy slug (e.g. "pa_težina").
     *
     * WooCommerce stores attribute slugs via wc_sanitize_taxonomy_name() which
     * preserves diacritical characters in the slug.  The agent, however, may
     * only see the ASCII-normalised version ("Tezina" instead of "Težina").
     * A plain sanitize_title() strips accents and produces a different slug,
     * causing duplicate attributes to be created.
     *
     * This method iterates over all registered WC attributes and compares
     * accent-stripped, lowercased versions of label and slug against the
     * provided name so that "Tezina" correctly resolves to "pa_težina".
     *
     * @param string $name Attribute name as provided by the agent.
     * @return string Full taxonomy slug (e.g. "pa_težina") or empty string.
     */
    private function resolve_existing_attribute_taxonomy( $name ) {
        if ( ! function_exists( 'wc_get_attribute_taxonomies' ) ) {
            return '';
        }

        $needle = $this->normalize_attribute_name_for_comparison( $name );
        if ( '' === $needle ) {
            return '';
        }

        foreach ( wc_get_attribute_taxonomies() as $attr ) {
            /* Compare against the human-readable label. */
            $label_norm = $this->normalize_attribute_name_for_comparison(
                isset( $attr->attribute_label ) ? $attr->attribute_label : ''
            );
            if ( '' !== $label_norm && $needle === $label_norm ) {
                return 'pa_' . $attr->attribute_name;
            }

            /* Compare against the stored slug (attribute_name column). */
            $slug_norm = $this->normalize_attribute_name_for_comparison(
                isset( $attr->attribute_name ) ? $attr->attribute_name : ''
            );
            if ( '' !== $slug_norm && $needle === $slug_norm ) {
                return 'pa_' . $attr->attribute_name;
            }
        }

        return '';
    }

    /**
     * Strip accents, lowercase and remove non-alphanumeric characters so that
     * "Težina", "težina", "Tezina" and "tezina" all produce the same key.
     */
    private function normalize_attribute_name_for_comparison( $value ) {
        $value = trim( (string) $value );
        if ( '' === $value ) {
            return '';
        }

        // Apply Serbian diacritics FIRST — WordPress remove_accents() maps đ→d
        // (not dj) which breaks Serbian attribute matching.
        $serbian_map = array(
            'š' => 's', 'đ' => 'dj', 'č' => 'c', 'ć' => 'c', 'ž' => 'z',
            'Š' => 'S', 'Đ' => 'Dj', 'Č' => 'C', 'Ć' => 'C', 'Ž' => 'Z',
        );
        $value = strtr( $value, $serbian_map );

        // Then strip remaining accents (French, German, etc.) via WordPress.
        if ( function_exists( 'remove_accents' ) ) {
            $value = remove_accents( $value );
        }

        $value = mb_strtolower( $value );
        $value = preg_replace( '/[^a-z0-9]+/', '', $value );

        return $value;
    }

    private function execute_set_product_featured_image_action( $action ) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/media.php';
        require_once ABSPATH . 'wp-admin/includes/image.php';

        $product_id = intval( isset( $action['args']['product_id'] ) ? $action['args']['product_id'] : 0 );
        $image_url  = esc_url_raw( isset( $action['args']['image_url'] ) ? (string) $action['args']['image_url'] : '' );
        $post       = get_post( $product_id );

        if ( ! $post || 'product' !== $post->post_type ) {
            return array( 'success' => false, 'error' => 'Ciljni proizvod ne postoji.' );
        }

        $previous_thumbnail_id = intval( get_post_thumbnail_id( $product_id ) );

        // Shortcut: if the image was already ingested into Media Library (e.g. from Telegram),
        // use the existing attachment directly instead of re-downloading.
        $context_attachment_id = isset( $action['args']['attachment_id'] ) ? intval( $action['args']['attachment_id'] ) : 0;
        if ( $context_attachment_id > 0 && get_post( $context_attachment_id ) ) {
            if ( $previous_thumbnail_id === $context_attachment_id ) {
                return array(
                    'success'       => false,
                    'error'         => 'Featured image je vec postavljen na taj attachment; nema promene za snimanje.',
                    'product_id'    => $product_id,
                    'attachment_id' => $context_attachment_id,
                );
            }

            $thumb_set = $this->force_set_product_thumbnail( $product_id, $context_attachment_id );
            if ( ! $thumb_set ) {
                return array( 'success' => false, 'error' => 'WordPress nije uspeo da postavi featured image (attachment ID ' . $context_attachment_id . ').' );
            }

            return array(
                'success'          => true,
                'summary'          => 'Featured image postavljen za proizvod "' . get_the_title( $product_id ) . '" (iz Media Library).',
                'executed_actions' => array(
                    array(
                        'type'          => 'set_product_featured_image_from_url',
                        'product_id'    => $product_id,
                        'attachment_id' => $context_attachment_id,
                    ),
                ),
                'rollback_entries' => array(
                    array(
                        'type'                  => 'product_thumbnail_restore',
                        'product_id'            => $product_id,
                        'previous_thumbnail_id' => $previous_thumbnail_id,
                        'new_attachment_id'     => $context_attachment_id,
                    ),
                ),
            );
        }

        // Use the full Image Utils pipeline: download → detect → WebP convert → sideload.
        $ingested = Niwa_AI_Admin_Autonomy_Image_Utils::ingest_from_url(
            $image_url,
            get_the_title( $product_id ) . '-featured',
            $product_id
        );
        if ( ! empty( $ingested['error'] ) ) {
            return array( 'success' => false, 'error' => 'Download/import slike: ' . $ingested['error'] );
        }

        $attachment_id = intval( $ingested['attachment_id'] );
        $thumb_set     = $this->force_set_product_thumbnail( $product_id, $attachment_id );
        if ( ! $thumb_set ) {
            return array( 'success' => false, 'error' => 'WordPress nije uspeo da postavi featured image (attachment ID ' . $attachment_id . ').' );
        }

        return array(
            'success'          => true,
            'summary'          => 'Featured image postavljen za proizvod "' . get_the_title( $product_id ) . '".',
            'executed_actions' => array(
                array(
                    'type'          => 'set_product_featured_image_from_url',
                    'product_id'    => $product_id,
                    'attachment_id' => $attachment_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'                  => 'product_thumbnail_restore',
                    'product_id'            => $product_id,
                    'previous_thumbnail_id' => $previous_thumbnail_id,
                    'new_attachment_id'     => $attachment_id,
                ),
            ),
        );
    }

    /**
     * Reliably set a product's featured image, bypassing set_post_thumbnail()'s
     * wp_get_attachment_image() check that silently DELETES the thumbnail when the
     * attachment lacks complete image metadata (common with WebP on hosts without
     * full WebP support in GD/Imagick).
     *
     * Also flushes WooCommerce product caches so the change is visible immediately.
     *
     * @param int $product_id    WooCommerce product post ID.
     * @param int $attachment_id WordPress attachment post ID.
     * @return bool True on success.
     */
    private function force_set_product_thumbnail( $product_id, $attachment_id ) {
        $product_id    = intval( $product_id );
        $attachment_id = intval( $attachment_id );

        if ( $product_id <= 0 || $attachment_id <= 0 ) {
            return false;
        }

        // Write _thumbnail_id directly — avoids the wp_get_attachment_image() guard
        // inside set_post_thumbnail() that deletes the meta when the image editor
        // cannot produce a 'thumbnail' size (e.g. WebP without server support).
        $result = update_post_meta( $product_id, '_thumbnail_id', $attachment_id );

        // update_post_meta returns false when the value is unchanged OR on DB failure.
        // Verify by re-reading the meta.
        $stored = intval( get_post_meta( $product_id, '_thumbnail_id', true ) );
        if ( $stored !== $attachment_id ) {
            return false;
        }

        // Flush WooCommerce product caches so the frontend shows the new image.
        clean_post_cache( $product_id );
        $this->sync_product_ops_write( $product_id );

        return true;
    }

    private function execute_generate_image_action( $action ) {
        $args = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $prompt = isset( $args['prompt'] ) ? sanitize_textarea_field( (string) $args['prompt'] ) : '';
        $title = isset( $args['title'] ) ? sanitize_text_field( (string) $args['title'] ) : '';
        $alt_text = isset( $args['alt_text'] ) ? sanitize_text_field( (string) $args['alt_text'] ) : '';
        $quality = $this->normalize_generate_image_quality( isset( $args['quality'] ) ? $args['quality'] : $this->get_default_generate_image_quality() );
        $size = $this->normalize_generate_image_size( isset( $args['size'] ) ? $args['size'] : '1024x1024' );
        $reference_image_urls = $this->normalize_generate_image_reference_urls( isset( $args['reference_image_urls'] ) ? $args['reference_image_urls'] : array() );
        $parent_id = isset( $args['parent_post_id'] ) ? intval( $args['parent_post_id'] ) : 0;
        $force_regenerate = ! empty( $args['force_regenerate'] );
        if ( '' === $prompt || '' === $title ) { return array( 'success' => false, 'error' => 'generate_image trazi prompt i title.' ); }
        if ( ! $this->eval_openai_client || ! method_exists( $this->eval_openai_client, 'generate_image' ) || ! $this->eval_auth_manager ) { return array( 'success' => false, 'error' => 'Codex image generation runtime nije ucitan.' ); }

        // Without a visual reference the generator invents a generic product,
        // so fall back to the parent product's featured image automatically.
        if ( empty( $reference_image_urls ) && $parent_id > 0 ) {
            $reference_image_urls = $this->collect_parent_product_reference_urls( $parent_id );
        }
        if ( ! empty( $reference_image_urls ) ) {
            $prompt .= ' The product in the generated image must look IDENTICAL to the product in the attached reference image(s): same shape, colors, materials, label and branding placement. Do not invent a different product.';
        }

        // Idempotency: a cancelled turn loses its reply but the generated
        // attachment survives in the Media Library — the retry must reuse it
        // instead of paying another multi-minute generation for the same image.
        if ( ! $force_regenerate ) {
            $existing_id = $this->find_recent_generated_image_attachment( $title );
            if ( $existing_id > 0 ) {
                $existing_url = function_exists( 'wp_get_attachment_url' ) ? (string) wp_get_attachment_url( $existing_id ) : '';
                return array(
                    'success'          => true,
                    'reused_existing'  => true,
                    'summary'          => 'Slika "' . $title . '" je vec generisana u poslednja 24h (att_id=' . $existing_id . ', url=' . $existing_url . ') — iskoriscen postojeci attachment, nova slika NIJE generisana. Zakaci ovaj attachment u galeriju ako vec nije. Za namerno novu verziju pozovi generate_image sa force_regenerate=true i drugacijim title.',
                    'attachment_id'    => $existing_id,
                    'url'              => $existing_url,
                    'quality'          => $quality,
                    'size'             => $size,
                    'executed_actions' => array( array( 'type' => 'generate_image', 'attachment_id' => $existing_id, 'url' => $existing_url, 'reused_existing' => true ) ),
                    'rollback_entries' => array(),
                );
            }
        }

        // Time-budget guard: generation takes up to 3 minutes; starting a call
        // the turn's soft deadline will kill mid-flight loses the work AND the
        // reply. Fail fast with guidance instead, and clamp the timeout.
        $timeout = 180;
        $soft_deadline_ts = isset( $args['_soft_deadline_ts'] ) ? intval( $args['_soft_deadline_ts'] ) : 0;
        if ( $soft_deadline_ts > 0 ) {
            $remaining = $soft_deadline_ts - time() - 30;
            if ( $remaining < 60 ) {
                return array(
                    'success'              => false,
                    'error'                => 'Nedovoljno vremena u ovom turnu za novo generisanje slike (generisanje traje 1-3 min). NE pokreci novo generisanje sada: odgovori korisniku sta je do sada uradjeno i nastavi preostale slike u sledecem turnu, jednu po turnu (generisi -> odmah zakaci u galeriju -> javi progres).',
                    'retry_in_next_turn'   => true,
                    'remaining_budget_sec' => max( 0, $soft_deadline_ts - time() ),
                );
            }
            $timeout = min( 180, $remaining );
        }

        $auth_mode = $this->eval_auth_manager->get_mode();
        $creds = $this->eval_auth_manager->get_credentials();
        if ( 'oauth' !== strtolower( (string) $auth_mode ) ) { return array( 'success' => false, 'error' => 'Image generation zahteva Codex OAuth connection u Personal Assistant/Founder settings.' ); }
        $generated = $this->eval_openai_client->generate_image( $prompt, $auth_mode, $creds, $quality, $size, $timeout, $reference_image_urls );
        if ( empty( $generated['success'] ) ) { return array( 'success' => false, 'error' => isset( $generated['error'] ) ? (string) $generated['error'] : 'Codex image generation nije uspela.' ); }
        $references_used = isset( $generated['reference_images_used'] ) ? intval( $generated['reference_images_used'] ) : count( $reference_image_urls );
        $bytes = base64_decode( isset( $generated['image_b64'] ) ? (string) $generated['image_b64'] : '', true );
        if ( false === $bytes || '' === $bytes || 0 !== strncmp( $bytes, "\x89PNG\r\n\x1a\n", 8 ) ) { return array( 'success' => false, 'error' => 'Generated image payload nije validan PNG.' ); }
        if ( ! function_exists( 'wp_upload_bits' ) || ! function_exists( 'wp_insert_attachment' ) ) { return array( 'success' => false, 'error' => 'WordPress media upload functions nisu dostupne.' ); }
        $filename = sanitize_title( $title );
        if ( '' === $filename ) { $filename = 'niwa-generated-image'; }
        $upload = wp_upload_bits( $filename . '-' . gmdate( 'Ymd-His' ) . '.png', null, $bytes );
        if ( ! empty( $upload['error'] ) ) { return array( 'success' => false, 'error' => 'Generated image upload nije uspeo: ' . (string) $upload['error'] ); }
        $file = isset( $upload['file'] ) ? (string) $upload['file'] : '';
        $url = isset( $upload['url'] ) ? (string) $upload['url'] : '';
        $attachment_id = wp_insert_attachment( array( 'post_mime_type' => 'image/png', 'post_title' => $title, 'post_content' => '', 'post_excerpt' => '', 'post_status' => 'inherit', 'post_parent' => $parent_id ), $file, $parent_id );
        if ( is_wp_error( $attachment_id ) ) { return array( 'success' => false, 'error' => 'Generated image attachment nije sacuvan: ' . $attachment_id->get_error_message() ); }
        $attachment_id = intval( $attachment_id );
        if ( function_exists( 'wp_generate_attachment_metadata' ) && function_exists( 'wp_update_attachment_metadata' ) ) { $metadata = wp_generate_attachment_metadata( $attachment_id, $file ); if ( is_array( $metadata ) ) { wp_update_attachment_metadata( $attachment_id, $metadata ); } }
        if ( '' !== $alt_text ) { update_post_meta( $attachment_id, '_wp_attachment_image_alt', $alt_text ); }
        if ( function_exists( 'wp_get_attachment_url' ) ) { $resolved_url = wp_get_attachment_url( $attachment_id ); if ( is_string( $resolved_url ) && '' !== $resolved_url ) { $url = $resolved_url; } }
        return array( 'success' => true, 'summary' => 'Generisana slika "' . $title . '" sacuvana u Media Library (att_id=' . $attachment_id . ', quality=' . $quality . ', references=' . $references_used . ', url=' . $url . ').', 'attachment_id' => $attachment_id, 'url' => $url, 'quality' => $quality, 'size' => $size, 'reference_image_count' => $references_used, 'executed_actions' => array( array( 'type' => 'generate_image', 'attachment_id' => $attachment_id, 'url' => $url, 'quality' => $quality, 'reference_image_count' => $references_used ) ), 'rollback_entries' => array( array( 'type' => 'attachment_delete', 'attachment_id' => $attachment_id ) ) );
    }

    private function execute_upload_image_to_media_action( $action ) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/media.php';
        require_once ABSPATH . 'wp-admin/includes/image.php';

        $image_url     = esc_url_raw( isset( $action['args']['image_url'] ) ? (string) $action['args']['image_url'] : '' );
        $title         = sanitize_text_field( isset( $action['args']['title'] ) ? (string) $action['args']['title'] : '' );
        $alt_text      = sanitize_text_field( isset( $action['args']['alt_text'] ) ? (string) $action['args']['alt_text'] : '' );
        $parent_id     = isset( $action['args']['parent_post_id'] ) ? intval( $action['args']['parent_post_id'] ) : 0;
        $attachment_id = isset( $action['args']['attachment_id'] ) ? intval( $action['args']['attachment_id'] ) : 0;

        // Shortcut: image already ingested into Media Library from Telegram.
        $attachment_post = $attachment_id > 0 ? get_post( $attachment_id ) : null;
        if ( $attachment_post ) {
            $previous_alt_exists = metadata_exists( 'post', $attachment_id, '_wp_attachment_image_alt' );
            $previous_alt_text   = (string) get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
            $changed_fields      = array();
            $post_update         = array( 'ID' => $attachment_id );

            if ( '' !== $title && $title !== (string) $attachment_post->post_title ) {
                $post_update['post_title'] = $title;
                $changed_fields[]          = 'title';
            }

            if ( $parent_id > 0 && $parent_id !== intval( $attachment_post->post_parent ) ) {
                $post_update['post_parent'] = $parent_id;
                $changed_fields[]           = 'parent';
            }

            if ( count( $post_update ) > 1 ) {
                $updated = wp_update_post( $post_update, true );
                if ( is_wp_error( $updated ) ) {
                    return array( 'success' => false, 'error' => 'Media attachment metadata nije sacuvan: ' . $updated->get_error_message() );
                }
            }

            if ( '' !== $alt_text && $alt_text !== $previous_alt_text ) {
                update_post_meta( $attachment_id, '_wp_attachment_image_alt', $alt_text );
                $changed_fields[] = 'alt';
            }

            $url = wp_get_attachment_url( $attachment_id );
            $rollback_entries = array();
            if ( ! empty( $changed_fields ) ) {
                $rollback_entries[] = array(
                    'type'                => 'attachment_metadata_restore',
                    'attachment_id'       => $attachment_id,
                    'previous_title'      => (string) $attachment_post->post_title,
                    'previous_parent_id'  => intval( $attachment_post->post_parent ),
                    'previous_alt_exists' => $previous_alt_exists,
                    'previous_alt_text'   => $previous_alt_text,
                );
            }

            return array(
                'success'          => true,
                'summary'          => 'Slika "' . $title . '" sacuvana u Media Library (att_id=' . $attachment_id . ', url=' . $url . ').',
                'executed_actions' => array(
                    array(
                        'type'          => 'upload_image_to_media',
                        'attachment_id' => $attachment_id,
                        'changed_fields' => $changed_fields,
                    ),
                ),
                'rollback_entries' => $rollback_entries,
            );
        }

        if ( '' === $image_url ) {
            return array( 'success' => false, 'error' => 'Nema image_url ni attachment_id za upload.' );
        }

        // Full pipeline: download → detect → WebP convert → sideload.
        $ingested = Niwa_AI_Admin_Autonomy_Image_Utils::ingest_from_url( $image_url, $title, $parent_id );
        if ( ! empty( $ingested['error'] ) ) {
            return array( 'success' => false, 'error' => 'Upload slike: ' . $ingested['error'] );
        }

        $new_attachment_id = intval( $ingested['attachment_id'] );

        if ( '' !== $alt_text ) {
            update_post_meta( $new_attachment_id, '_wp_attachment_image_alt', $alt_text );
        }

        return array(
            'success'          => true,
            'summary'          => 'Slika "' . $title . '" uploadovana u Media Library (att_id=' . $new_attachment_id . ', url=' . $ingested['url'] . ').',
            'executed_actions' => array(
                array(
                    'type'          => 'upload_image_to_media',
                    'attachment_id' => $new_attachment_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'          => 'attachment_delete',
                    'attachment_id' => $new_attachment_id,
                ),
            ),
        );
    }

    private function execute_restore_product_from_trash_action( $action ) {
        $product_id = intval( isset( $action['args']['product_id'] ) ? $action['args']['product_id'] : 0 );
        $post       = get_post( $product_id );

        if ( ! $post || 'product' !== $post->post_type ) {
            return array( 'success' => false, 'error' => 'Ciljni proizvod ne postoji.' );
        }

        if ( 'trash' !== (string) $post->post_status ) {
            return array( 'success' => false, 'error' => 'Proizvod vise nije u trash-u.' );
        }

        $restored = wp_untrash_post( $product_id );
        if ( false === $restored ) {
            return array( 'success' => false, 'error' => 'Vracanjem proizvoda iz trash-a nije uspelo.' );
        }

        return array(
            'success'          => true,
            'summary'          => 'Proizvod "' . get_the_title( $product_id ) . '" je vracen iz trash-a.',
            'executed_actions' => array(
                array(
                    'type'       => 'restore_product_from_trash',
                    'product_id' => $product_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'       => 'post_retrash',
                    'post_id'    => $product_id,
                ),
            ),
        );
    }

    private function execute_remove_color_action( $action ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }
        if ( ! $this->product_management_service || ! method_exists( $this->product_management_service, 'remove_color' ) ) {
            return array( 'success' => false, 'error' => 'Shared product-management service nije ucitan za remove_color.' );
        }

        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $product_id = intval( isset( $args['product_id'] ) ? $args['product_id'] : 0 );
        $color      = sanitize_text_field( trim( (string) ( isset( $args['color'] ) ? $args['color'] : '' ) ) );
        $product    = wc_get_product( $product_id );
        if ( ! $product || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'remove_color trazi validan variable proizvod.' );
        }

        $rollback_entries = array(
            $this->build_product_attribute_restore_entry( $product_id, $product ),
        );

        foreach ( (array) $product->get_children() as $variation_id ) {
            $variation = wc_get_product( intval( $variation_id ) );
            if ( ! $variation || ! $variation->is_type( 'variation' ) ) {
                continue;
            }

            $attrs = $this->product_management_service->extract_var_attrs( $variation );
            if ( mb_strtolower( (string) $attrs['color'] ) !== mb_strtolower( $color ) ) {
                continue;
            }

            $rollback_entries[] = $this->build_variation_recreate_entry( $variation );
        }

        $service_result = $this->product_management_service->remove_color( $product_id, $color );
        if ( empty( $service_result['success'] ) ) {
            return array(
                'success' => false,
                'error'   => isset( $service_result['error'] ) ? (string) $service_result['error'] : 'remove_color nije uspeo.',
            );
        }

        return array(
            'success'          => true,
            'summary'          => 'Uklonjena je boja "' . $color . '" sa proizvoda "' . $product->get_name() . '". Obrisano varijacija: ' . intval( isset( $service_result['deleted_count'] ) ? $service_result['deleted_count'] : 0 ) . '.',
            'executed_actions' => array(
                array(
                    'type'         => 'remove_color',
                    'product_id'   => $product_id,
                    'color'        => $color,
                    'deleted_count'=> intval( isset( $service_result['deleted_count'] ) ? $service_result['deleted_count'] : 0 ),
                ),
            ),
            'rollback_entries' => $rollback_entries,
        );
    }

    private function execute_move_to_outlet_action( $action ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }
        if ( ! $this->product_management_service || ! method_exists( $this->product_management_service, 'move_to_outlet' ) ) {
            return array( 'success' => false, 'error' => 'Shared product-management service nije ucitan za move_to_outlet.' );
        }

        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $product_id = intval( isset( $args['product_id'] ) ? $args['product_id'] : 0 );
        $specs      = isset( $args['specs'] ) && is_array( $args['specs'] ) ? array_values( $args['specs'] ) : array();
        $product    = wc_get_product( $product_id );
        if ( ! $product || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'move_to_outlet trazi validan variable proizvod.' );
        }

        $before_ids        = array();
        $restore_entries   = array();
        $recreate_entries  = array();
        foreach ( (array) $product->get_children() as $variation_id ) {
            $variation = wc_get_product( intval( $variation_id ) );
            if ( ! $variation || ! $variation->is_type( 'variation' ) ) {
                continue;
            }

            $variation_id = intval( $variation_id );
            $before_ids[] = $variation_id;
            $restore_entries[ $variation_id ]  = $this->build_variation_restore_entry( $variation );
            $recreate_entries[ $variation_id ] = $this->build_variation_recreate_entry( $variation );
        }

        $rollback_entries = array(
            $this->build_simple_product_restore_entry( $product_id, $product ),
            $this->build_product_attribute_restore_entry( $product_id, $product ),
        );

        $service_result = $this->product_management_service->move_to_outlet( $product_id, $specs );
        if ( empty( $service_result['success'] ) ) {
            return array(
                'success' => false,
                'error'   => isset( $service_result['error'] ) ? (string) $service_result['error'] : 'move_to_outlet nije uspeo.',
            );
        }

        $product_after = wc_get_product( $product_id );
        $after_ids     = $product_after ? array_map( 'intval', (array) $product_after->get_children() ) : array();
        $created_ids   = array_values( array_diff( $after_ids, $before_ids ) );

        foreach ( $before_ids as $variation_id ) {
            if ( in_array( $variation_id, $after_ids, true ) ) {
                $rollback_entries[] = $restore_entries[ $variation_id ];
            } else {
                $rollback_entries[] = $recreate_entries[ $variation_id ];
            }
        }

        foreach ( $created_ids as $variation_id ) {
            $rollback_entries[] = array(
                'type'         => 'variation_delete',
                'variation_id' => intval( $variation_id ),
                'parent_id'    => $product_id,
            );
        }

        $summary = 'Proizvod "' . $product->get_name() . '" je prebacen u outlet. Obrisano varijacija: ' . intval( isset( $service_result['deleted_count'] ) ? $service_result['deleted_count'] : 0 ) . ', azurirano: ' . intval( isset( $service_result['updated_count'] ) ? $service_result['updated_count'] : 0 ) . ', kreirano: ' . intval( isset( $service_result['created_count'] ) ? $service_result['created_count'] : 0 ) . '.';

        return array(
            'success'          => true,
            'summary'          => $summary,
            'executed_actions' => array(
                array(
                    'type'          => 'move_to_outlet',
                    'product_id'    => $product_id,
                    'deleted_count' => intval( isset( $service_result['deleted_count'] ) ? $service_result['deleted_count'] : 0 ),
                    'updated_count' => intval( isset( $service_result['updated_count'] ) ? $service_result['updated_count'] : 0 ),
                    'created_count' => intval( isset( $service_result['created_count'] ) ? $service_result['created_count'] : 0 ),
                ),
            ),
            'rollback_entries' => $rollback_entries,
        );
    }

    private function execute_update_simple_product_action( $action ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'error' => 'WooCommerce nije dostupan.' );
        }

        $args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $product_id = intval( isset( $action['args']['product_id'] ) ? $action['args']['product_id'] : 0 );
        $post       = get_post( $product_id );
        if ( ! $post || 'product' !== $post->post_type ) {
            return array( 'success' => false, 'error' => 'Ciljni proizvod ne postoji.' );
        }

        if ( 'trash' === (string) $post->post_status ) {
            return array( 'success' => false, 'error' => 'Proizvod je i dalje u trash-u.' );
        }

        $product = wc_get_product( $product_id );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'WC proizvod nije ucitan.' );
        }

        $is_simple = $product->is_type( 'simple' );
        $has_price_or_stock_change = (
            ( array_key_exists( 'regular_price', $args ) && null !== $args['regular_price'] && '' !== trim( (string) $args['regular_price'] ) )
            || ( array_key_exists( 'sale_price', $args ) && null !== $args['sale_price'] && '' !== trim( (string) $args['sale_price'] ) )
            || ! empty( $args['clear_sale_price'] )
            || ! empty( $args['clear_sale_dates'] )
            || ( array_key_exists( 'date_on_sale_from', $args ) && null !== $args['date_on_sale_from'] && '' !== trim( (string) $args['date_on_sale_from'] ) )
            || ( array_key_exists( 'date_on_sale_to', $args ) && null !== $args['date_on_sale_to'] && '' !== trim( (string) $args['date_on_sale_to'] ) )
            || ( array_key_exists( 'stock_quantity', $args ) && null !== $args['stock_quantity'] && '' !== (string) $args['stock_quantity'] )
        );

        if ( $has_price_or_stock_change && ! $is_simple ) {
            return array(
                'success' => false,
                'error'   => 'Ciljni proizvod nije simple. Za variable parent koristi variation-level Woo alate za cenu, sale cenu ili stock.',
            );
        }

        $rollback_entry = $this->build_simple_product_restore_entry( $product_id, $product );

        $changes                        = array();
        $requested_changes              = array();
        $service_changes                = array();
        $use_shared_product_management  = $this->product_management_service && method_exists( $this->product_management_service, 'update_simple_product_state' );
        $shared_product_management_args = array();

        /* ── Title ── */
        if ( ! empty( $args['title'] ) && is_string( $args['title'] ) ) {
            $product->set_name( sanitize_text_field( $args['title'] ) );
            $changes[] = 'title';
        }

        /* ── Description ── */
        if ( array_key_exists( 'description', $args ) && null !== $args['description'] && is_string( $args['description'] ) ) {
            $product->set_description( wp_kses_post( $args['description'] ) );
            $changes[] = 'description';
        }

        /* ── Short description ── */
        if ( array_key_exists( 'short_description', $args ) && null !== $args['short_description'] && is_string( $args['short_description'] ) ) {
            $product->set_short_description( wp_kses_post( $args['short_description'] ) );
            $changes[] = 'short description';
        }

        /* ── SKU ── */
        if ( array_key_exists( 'sku', $args ) && null !== $args['sku'] && is_string( $args['sku'] ) ) {
            try {
                $product->set_sku( wc_clean( $args['sku'] ) );
                $changes[] = 'SKU';
            } catch ( \Exception $e ) {
                return array( 'success' => false, 'error' => 'SKU greska: ' . $e->getMessage() );
            }
        }

        /* ── Status ── */
        if ( ! empty( $args['status'] ) && in_array( (string) $args['status'], array( 'publish', 'draft', 'private', 'pending' ), true ) ) {
            $product->set_status( (string) $args['status'] );
            $changes[] = 'status → ' . (string) $args['status'];
        }

        /* ── Weight ── */
        if ( array_key_exists( 'weight', $args ) && null !== $args['weight'] && '' !== trim( (string) $args['weight'] ) ) {
            $product->set_weight( (string) $args['weight'] );
            $changes[] = 'weight';
        }

        /* ── Dimensions ── */
        if ( array_key_exists( 'length', $args ) && null !== $args['length'] && '' !== trim( (string) $args['length'] ) ) {
            $product->set_length( (string) $args['length'] );
            $changes[] = 'length';
        }
        if ( array_key_exists( 'width', $args ) && null !== $args['width'] && '' !== trim( (string) $args['width'] ) ) {
            $product->set_width( (string) $args['width'] );
            $changes[] = 'width';
        }
        if ( array_key_exists( 'height', $args ) && null !== $args['height'] && '' !== trim( (string) $args['height'] ) ) {
            $product->set_height( (string) $args['height'] );
            $changes[] = 'height';
        }

        /* ── Catalog visibility ── */
        if ( ! empty( $args['catalog_visibility'] ) && in_array( (string) $args['catalog_visibility'], array( 'visible', 'catalog', 'search', 'hidden' ), true ) ) {
            $product->set_catalog_visibility( (string) $args['catalog_visibility'] );
            $changes[] = 'visibility → ' . (string) $args['catalog_visibility'];
        }

        /* ── Featured ── */
        if ( array_key_exists( 'featured', $args ) && null !== $args['featured'] ) {
            $product->set_featured( (bool) $args['featured'] );
            $changes[] = 'featured → ' . ( $args['featured'] ? 'yes' : 'no' );
        }

        /* ── Virtual / Downloadable ── */
        if ( array_key_exists( 'virtual', $args ) && null !== $args['virtual'] ) {
            $product->set_virtual( (bool) $args['virtual'] );
            $changes[] = 'virtual → ' . ( $args['virtual'] ? 'yes' : 'no' );
        }
        if ( array_key_exists( 'downloadable', $args ) && null !== $args['downloadable'] ) {
            $product->set_downloadable( (bool) $args['downloadable'] );
            $changes[] = 'downloadable → ' . ( $args['downloadable'] ? 'yes' : 'no' );
        }

        /* ── Tax ── */
        if ( ! empty( $args['tax_status'] ) && in_array( (string) $args['tax_status'], array( 'taxable', 'shipping', 'none' ), true ) ) {
            $product->set_tax_status( (string) $args['tax_status'] );
            $changes[] = 'tax status → ' . (string) $args['tax_status'];
        }
        if ( array_key_exists( 'tax_class', $args ) && null !== $args['tax_class'] ) {
            $product->set_tax_class( (string) $args['tax_class'] );
            $changes[] = 'tax class';
        }

        /* ── Sold individually ── */
        if ( array_key_exists( 'sold_individually', $args ) && null !== $args['sold_individually'] ) {
            $product->set_sold_individually( (bool) $args['sold_individually'] );
            $changes[] = 'sold individually → ' . ( $args['sold_individually'] ? 'yes' : 'no' );
        }

        /* ── Backorders ── */
        if ( ! empty( $args['backorders'] ) && in_array( (string) $args['backorders'], array( 'no', 'notify', 'yes' ), true ) ) {
            $product->set_backorders( (string) $args['backorders'] );
            $changes[] = 'backorders → ' . (string) $args['backorders'];
        }

        /* ── Low stock amount ── */
        if ( array_key_exists( 'low_stock_amount', $args ) && null !== $args['low_stock_amount'] && '' !== (string) $args['low_stock_amount'] ) {
            $product->set_low_stock_amount( intval( $args['low_stock_amount'] ) );
            $changes[] = 'low stock amount';
        }

        /* ── Purchase note ── */
        if ( array_key_exists( 'purchase_note', $args ) && null !== $args['purchase_note'] && is_string( $args['purchase_note'] ) ) {
            $product->set_purchase_note( wp_kses_post( $args['purchase_note'] ) );
            $changes[] = 'purchase note';
        }

        /* ── Reviews allowed ── */
        if ( array_key_exists( 'reviews_allowed', $args ) && null !== $args['reviews_allowed'] ) {
            $product->set_reviews_allowed( (bool) $args['reviews_allowed'] );
            $changes[] = 'reviews → ' . ( $args['reviews_allowed'] ? 'allowed' : 'disabled' );
        }

        /* ── Menu order ── */
        if ( array_key_exists( 'menu_order', $args ) && null !== $args['menu_order'] && '' !== (string) $args['menu_order'] ) {
            $product->set_menu_order( intval( $args['menu_order'] ) );
            $changes[] = 'menu order';
        }

        /* ── Upsells / Cross-sells ── */
        if ( ! empty( $args['upsell_ids'] ) && is_array( $args['upsell_ids'] ) ) {
            $product->set_upsell_ids( array_map( 'intval', $args['upsell_ids'] ) );
            $changes[] = 'upsells (' . count( $args['upsell_ids'] ) . ')';
        }
        if ( ! empty( $args['cross_sell_ids'] ) && is_array( $args['cross_sell_ids'] ) ) {
            $product->set_cross_sell_ids( array_map( 'intval', $args['cross_sell_ids'] ) );
            $changes[] = 'cross-sells (' . count( $args['cross_sell_ids'] ) . ')';
        }

        /* ── Prices (only for simple products) ── */
        if ( $is_simple ) {
            if ( array_key_exists( 'regular_price', $args ) && null !== $args['regular_price'] && '' !== trim( (string) $args['regular_price'] ) ) {
                if ( $use_shared_product_management ) {
                    $shared_product_management_args['regular_price'] = (string) $args['regular_price'];
                } else {
                    $product->set_regular_price( (string) $args['regular_price'] );
                    $changes[] = 'regular price';
                }
            }

            if ( ! empty( $args['clear_sale_price'] ) ) {
                if ( $use_shared_product_management ) {
                    $shared_product_management_args['clear_sale_price'] = 1;
                } else {
                    $product->set_sale_price( '' );
                    $changes[] = 'sale price cleared';
                }
            } elseif ( array_key_exists( 'sale_price', $args ) && null !== $args['sale_price'] && '' !== trim( (string) $args['sale_price'] ) ) {
                if ( $use_shared_product_management ) {
                    $shared_product_management_args['sale_price'] = (string) $args['sale_price'];
                } else {
                    $product->set_sale_price( (string) $args['sale_price'] );
                    $changes[] = 'sale price';
                }
            }

            if ( ! empty( $args['clear_sale_dates'] ) ) {
                if ( $use_shared_product_management ) {
                    $shared_product_management_args['clear_sale_dates'] = 1;
                } else {
                    if ( method_exists( $product, 'set_date_on_sale_from' ) ) {
                        $product->set_date_on_sale_from( null );
                    }
                    if ( method_exists( $product, 'set_date_on_sale_to' ) ) {
                        $product->set_date_on_sale_to( null );
                    }
                    $changes[] = 'sale schedule cleared';
                }
            } else {
                if ( array_key_exists( 'date_on_sale_from', $args ) && null !== $args['date_on_sale_from'] && '' !== trim( (string) $args['date_on_sale_from'] ) ) {
                    if ( $use_shared_product_management ) {
                        $shared_product_management_args['date_on_sale_from'] = (string) $args['date_on_sale_from'];
                    } elseif ( method_exists( $product, 'set_date_on_sale_from' ) ) {
                        $product->set_date_on_sale_from( (string) $args['date_on_sale_from'] );
                        $changes[] = 'sale start';
                    }
                }
                if ( array_key_exists( 'date_on_sale_to', $args ) && null !== $args['date_on_sale_to'] && '' !== trim( (string) $args['date_on_sale_to'] ) ) {
                    if ( $use_shared_product_management ) {
                        $shared_product_management_args['date_on_sale_to'] = (string) $args['date_on_sale_to'];
                    } elseif ( method_exists( $product, 'set_date_on_sale_to' ) ) {
                        $product->set_date_on_sale_to( (string) $args['date_on_sale_to'] );
                        $changes[] = 'sale end';
                    }
                }
            }

            if ( array_key_exists( 'stock_quantity', $args ) && null !== $args['stock_quantity'] && '' !== (string) $args['stock_quantity'] ) {
                if ( $use_shared_product_management ) {
                    $shared_product_management_args['stock_quantity'] = max( 0, intval( $args['stock_quantity'] ) );
                } else {
                    $qty = max( 0, intval( $args['stock_quantity'] ) );
                    $product->set_manage_stock( true );
                    $product->set_stock_quantity( $qty );
                    $product->set_stock_status( $qty > 0 ? 'instock' : 'outofstock' );
                    $changes[] = 'stock quantity';
                }
            }
        }

        /* ── Save product object changes ── */
        if ( ! empty( $changes ) ) {
            $requested_changes = array_merge( $requested_changes, $changes );
            if ( method_exists( $product, 'get_changes' ) && empty( $product->get_changes() ) ) {
                $changes = array();
            } else {
                $saved = $product->save();
                if ( ! $saved ) {
                    return array( 'success' => false, 'error' => 'Woo proizvod nije sacuvan.' );
                }
            }
        }

        /* ── Categories ── */
        if ( $use_shared_product_management && ! empty( $args['resolved_category_ids'] ) && is_array( $args['resolved_category_ids'] ) ) {
            $shared_product_management_args['category_ids']    = array_map( 'intval', $args['resolved_category_ids'] );
            $shared_product_management_args['category_action'] = isset( $args['category_action'] ) ? (string) $args['category_action'] : 'set';
            $requested_changes[] = 'categories ' . $shared_product_management_args['category_action'];
        }

        if ( $use_shared_product_management && ! empty( $shared_product_management_args ) ) {
            $shared_product_management_args['skip_reindex'] = 1;
            $service_result = $this->product_management_service->update_simple_product_state( $product_id, $shared_product_management_args );
            if ( empty( $service_result['success'] ) ) {
                return array(
                    'success' => false,
                    'error'   => isset( $service_result['error'] ) ? (string) $service_result['error'] : 'Shared product-management update nije uspela.',
                );
            }

            if ( ! empty( $service_result['changes'] ) && is_array( $service_result['changes'] ) ) {
                $service_changes = array_values( array_filter( array_map( 'strval', $service_result['changes'] ) ) );
            }
            if ( ! empty( $service_result['requested_changes'] ) && is_array( $service_result['requested_changes'] ) ) {
                $requested_changes = array_merge( $requested_changes, array_map( 'strval', $service_result['requested_changes'] ) );
            }
        }

        if ( ! $use_shared_product_management && ! empty( $args['resolved_category_ids'] ) && is_array( $args['resolved_category_ids'] ) ) {
            $cat_action = isset( $args['category_action'] ) ? (string) $args['category_action'] : 'set';
            $cat_ids    = array_values( array_unique( array_filter( array_map( 'intval', $args['resolved_category_ids'] ) ) ) );
            $requested_changes[] = 'categories ' . $cat_action;
            $current_cat_ids = wp_get_object_terms( $product_id, 'product_cat', array( 'fields' => 'ids' ) );
            if ( is_wp_error( $current_cat_ids ) ) {
                return array( 'success' => false, 'error' => 'Kategorije nisu mogle da se ucitaju: ' . $current_cat_ids->get_error_message() );
            }
            $current_cat_ids = array_values( array_unique( array_map( 'intval', (array) $current_cat_ids ) ) );
            $next_cat_ids    = $current_cat_ids;

            if ( 'remove' === $cat_action ) {
                $next_cat_ids = array_values(
                    array_filter(
                        $current_cat_ids,
                        function ( $item ) use ( $cat_ids ) {
                            return ! in_array( intval( $item ), $cat_ids, true );
                        }
                    )
                );
            } elseif ( 'add' === $cat_action ) {
                $next_cat_ids = array_values( array_unique( array_merge( $current_cat_ids, $cat_ids ) ) );
            } else {
                $next_cat_ids = $cat_ids;
            }

            $current_compare = $current_cat_ids;
            $next_compare    = $next_cat_ids;
            sort( $current_compare );
            sort( $next_compare );

            if ( $next_compare !== $current_compare ) {
                wp_set_object_terms( $product_id, $next_cat_ids, 'product_cat', false );
                if ( 'remove' === $cat_action ) {
                    $changes[] = 'categories removed (' . count( array_diff( $current_cat_ids, $next_cat_ids ) ) . ')';
                } elseif ( 'add' === $cat_action ) {
                    $changes[] = 'categories added (' . count( array_diff( $next_cat_ids, $current_cat_ids ) ) . ')';
                } else {
                    $changes[] = 'categories set (' . count( $next_cat_ids ) . ')';
                }
            }
        }

        /* ── Tags ── */
        if ( ! empty( $args['resolved_tag_ids'] ) && is_array( $args['resolved_tag_ids'] ) ) {
            $tag_action = isset( $args['tag_action'] ) ? (string) $args['tag_action'] : 'set';
            $tag_ids    = array_values( array_unique( array_filter( array_map( 'intval', $args['resolved_tag_ids'] ) ) ) );
            $requested_changes[] = 'tags ' . $tag_action;
            $current_tag_ids = wp_get_object_terms( $product_id, 'product_tag', array( 'fields' => 'ids' ) );
            if ( is_wp_error( $current_tag_ids ) ) {
                return array( 'success' => false, 'error' => 'Tagovi nisu mogli da se ucitaju: ' . $current_tag_ids->get_error_message() );
            }
            $current_tag_ids = array_values( array_unique( array_map( 'intval', (array) $current_tag_ids ) ) );
            $next_tag_ids    = $current_tag_ids;

            if ( 'remove' === $tag_action ) {
                $next_tag_ids = array_values(
                    array_filter(
                        $current_tag_ids,
                        function ( $item ) use ( $tag_ids ) {
                            return ! in_array( intval( $item ), $tag_ids, true );
                        }
                    )
                );
            } elseif ( 'add' === $tag_action ) {
                $next_tag_ids = array_values( array_unique( array_merge( $current_tag_ids, $tag_ids ) ) );
            } else {
                $next_tag_ids = $tag_ids;
            }

            $current_compare = $current_tag_ids;
            $next_compare    = $next_tag_ids;
            sort( $current_compare );
            sort( $next_compare );

            if ( $next_compare !== $current_compare ) {
                wp_set_object_terms( $product_id, $next_tag_ids, 'product_tag', false );
                if ( 'remove' === $tag_action ) {
                    $changes[] = 'tags removed (' . count( array_diff( $current_tag_ids, $next_tag_ids ) ) . ')';
                } elseif ( 'add' === $tag_action ) {
                    $changes[] = 'tags added (' . count( array_diff( $next_tag_ids, $current_tag_ids ) ) . ')';
                } else {
                    $changes[] = 'tags set (' . count( $next_tag_ids ) . ')';
                }
            }
        }

        if ( ! empty( $service_changes ) ) {
            $changes = array_merge( $changes, $service_changes );
        }

        if ( empty( $changes ) ) {
            $requested_changes = array_values( array_unique( array_filter( array_map( 'strval', $requested_changes ) ) ) );
            if ( ! empty( $requested_changes ) ) {
                return array(
                    'success'          => true,
                    'unchanged'        => true,
                    'summary'          => 'Woo proizvod "' . get_the_title( $product_id ) . '" vec odgovara trazenom stanju.',
                    'executed_actions' => array(
                        array(
                            'type'              => 'update_simple_product',
                            'product_id'        => $product_id,
                            'changes'           => array(),
                            'requested_changes' => $requested_changes,
                            'unchanged'         => true,
                        ),
                    ),
                    'rollback_entries' => array(),
                );
            }
            return array( 'success' => false, 'error' => 'Nema Woo promena za izvrsenje.' );
        }

        $this->sync_product_ops_write( $product_id );

        return array(
            'success'          => true,
            'summary'          => 'Azuriran Woo proizvod "' . get_the_title( $product_id ) . '" (' . implode( ', ', $changes ) . ').',
            'executed_actions' => array(
                array(
                    'type'       => 'update_simple_product',
                    'product_id' => $product_id,
                    'changes'    => $changes,
                ),
            ),
            'rollback_entries' => array( $rollback_entry ),
        );
    }

    private function sync_product_ops_write( $product_id ) {
        $product_id = intval( $product_id );
        if ( $product_id <= 0 ) {
            return;
        }

        $cache_ids = array( $product_id );
        if ( function_exists( 'wc_get_product' ) ) {
            $product = wc_get_product( $product_id );
            if ( $product && method_exists( $product, 'is_type' ) && $product->is_type( 'variable' ) && method_exists( $product, 'get_children' ) ) {
                $cache_ids = array_merge( $cache_ids, array_map( 'intval', (array) $product->get_children() ) );
            } elseif ( $product && method_exists( $product, 'is_type' ) && $product->is_type( 'variation' ) && method_exists( $product, 'get_parent_id' ) ) {
                $cache_ids[] = intval( $product->get_parent_id() );
            }
        }

        foreach ( array_values( array_unique( array_filter( $cache_ids ) ) ) as $cache_id ) {
            if ( function_exists( 'wc_delete_product_transients' ) ) {
                wc_delete_product_transients( $cache_id );
            }
            if ( function_exists( 'clean_post_cache' ) ) {
                clean_post_cache( $cache_id );
            }
        }
        $this->clear_product_ops_stale_noindex( $product_id );
        do_action( 'niwa_ai_index_upsert', $product_id, 'product', true );

        if ( class_exists( 'Niwa_AI_External_Page_Cache' ) ) {
            Niwa_AI_External_Page_Cache::refresh_seraphinite_after_change( 'woocommerce_product_write' );
        }
    }

    private function clear_product_ops_stale_noindex( $product_id ) {
        $product_id = intval( $product_id );
        if ( $product_id <= 0 || ! metadata_exists( 'post', $product_id, '_za_noindex' ) ) {
            return;
        }

        $post = get_post( $product_id );
        if ( ! $post || 'product' !== (string) $post->post_type || 'publish' !== (string) $post->post_status ) {
            return;
        }

        if ( 'outofstock' === $this->get_product_ops_effective_stock_status( $product_id ) ) {
            return;
        }

        delete_post_meta( $product_id, '_za_noindex' );
    }

    private function get_product_ops_effective_stock_status( $product_id ) {
        $product_id = intval( $product_id );
        if ( $product_id <= 0 ) {
            return 'outofstock';
        }

        if ( function_exists( 'wc_get_product' ) ) {
            $product = wc_get_product( $product_id );
            if ( $product && method_exists( $product, 'is_type' ) && $product->is_type( 'variable' ) && method_exists( $product, 'get_children' ) ) {
                foreach ( (array) $product->get_children() as $variation_id ) {
                    $variation = wc_get_product( intval( $variation_id ) );
                    if ( ! $variation || ! method_exists( $variation, 'is_type' ) || ! $variation->is_type( 'variation' ) ) {
                        continue;
                    }
                    if ( method_exists( $variation, 'get_status' ) && ! in_array( (string) $variation->get_status(), array( 'publish', 'private' ), true ) ) {
                        continue;
                    }
                    if ( method_exists( $variation, 'is_in_stock' ) && $variation->is_in_stock() ) {
                        return 'instock';
                    }
                    if ( method_exists( $variation, 'get_stock_status' ) && 'outofstock' !== (string) $variation->get_stock_status() ) {
                        return (string) $variation->get_stock_status();
                    }
                }

                return 'outofstock';
            }

            if ( $product && method_exists( $product, 'get_stock_status' ) ) {
                return (string) $product->get_stock_status();
            }
        }

        $stock_status = (string) get_post_meta( $product_id, '_stock_status', true );
        return '' !== $stock_status ? $stock_status : 'instock';
    }

    private function build_product_attribute_restore_entry( $product_id, $product ) {
        $prev_attributes_raw = array();
        foreach ( $product->get_attributes() as $key => $attr_obj ) {
            if ( ! is_a( $attr_obj, 'WC_Product_Attribute' ) ) {
                continue;
            }

            $prev_attributes_raw[ $key ] = array(
                'name'      => $attr_obj->get_name(),
                'options'   => $attr_obj->get_options(),
                'visible'   => $attr_obj->get_visible(),
                'variation' => $attr_obj->get_variation(),
            );
        }

        return array(
            'type'                => 'attribute_restore',
            'product_id'          => intval( $product_id ),
            'previous_attributes' => $prev_attributes_raw,
        );
    }

    private function build_simple_product_restore_entry( $product_id, $product ) {
        return array(
            'type'                          => 'simple_product_restore',
            'product_id'                    => intval( $product_id ),
            'previous_title'                => (string) $product->get_name(),
            'previous_description'          => (string) $product->get_description(),
            'previous_short_description'    => (string) $product->get_short_description(),
            'previous_sku'                  => (string) $product->get_sku(),
            'previous_status'               => (string) $product->get_status(),
            'previous_regular_price'        => (string) $product->get_regular_price(),
            'previous_sale_price'           => (string) $product->get_sale_price(),
            'previous_date_on_sale_from'    => method_exists( $product, 'get_date_on_sale_from' ) && method_exists( $this, 'format_wc_datetime_for_tool' ) ? $this->format_wc_datetime_for_tool( $product->get_date_on_sale_from( 'edit' ) ) : '',
            'previous_date_on_sale_to'      => method_exists( $product, 'get_date_on_sale_to' ) && method_exists( $this, 'format_wc_datetime_for_tool' ) ? $this->format_wc_datetime_for_tool( $product->get_date_on_sale_to( 'edit' ) ) : '',
            'previous_manage_stock'         => $product->get_manage_stock() ? 1 : 0,
            'previous_stock_quantity'       => null !== $product->get_stock_quantity() ? intval( $product->get_stock_quantity() ) : '',
            'previous_stock_status'         => (string) $product->get_stock_status(),
            'previous_weight'               => (string) $product->get_weight(),
            'previous_length'               => (string) $product->get_length(),
            'previous_width'                => (string) $product->get_width(),
            'previous_height'               => (string) $product->get_height(),
            'previous_categories'           => wp_get_object_terms( intval( $product_id ), 'product_cat', array( 'fields' => 'ids' ) ),
            'previous_tags'                 => wp_get_object_terms( intval( $product_id ), 'product_tag', array( 'fields' => 'ids' ) ),
            'previous_catalog_visibility'   => method_exists( $product, 'get_catalog_visibility' ) ? (string) $product->get_catalog_visibility() : 'visible',
            'previous_featured'             => method_exists( $product, 'get_featured' ) ? ( $product->get_featured() ? 1 : 0 ) : 0,
            'previous_virtual'              => method_exists( $product, 'get_virtual' ) ? ( $product->get_virtual() ? 1 : 0 ) : 0,
            'previous_downloadable'         => method_exists( $product, 'get_downloadable' ) ? ( $product->get_downloadable() ? 1 : 0 ) : 0,
            'previous_tax_status'           => method_exists( $product, 'get_tax_status' ) ? (string) $product->get_tax_status() : 'taxable',
            'previous_tax_class'            => method_exists( $product, 'get_tax_class' ) ? (string) $product->get_tax_class() : '',
            'previous_sold_individually'    => method_exists( $product, 'get_sold_individually' ) ? ( $product->get_sold_individually() ? 1 : 0 ) : 0,
            'previous_backorders'           => method_exists( $product, 'get_backorders' ) ? (string) $product->get_backorders() : 'no',
            'previous_low_stock_amount'     => method_exists( $product, 'get_low_stock_amount' ) ? (string) $product->get_low_stock_amount() : '',
            'previous_purchase_note'        => method_exists( $product, 'get_purchase_note' ) ? (string) $product->get_purchase_note() : '',
            'previous_reviews_allowed'      => method_exists( $product, 'get_reviews_allowed' ) ? ( $product->get_reviews_allowed() ? 1 : 0 ) : 1,
            'previous_menu_order'           => method_exists( $product, 'get_menu_order' ) ? intval( $product->get_menu_order() ) : 0,
            'previous_upsell_ids'           => method_exists( $product, 'get_upsell_ids' ) ? $product->get_upsell_ids() : array(),
            'previous_cross_sell_ids'       => method_exists( $product, 'get_cross_sell_ids' ) ? $product->get_cross_sell_ids() : array(),
        );
    }

    private function apply_bulk_price_item_to_product( $product, $item ) {
        $changes = array();

        if ( array_key_exists( 'new_regular_price', $item ) && null !== $item['new_regular_price'] && '' !== trim( (string) $item['new_regular_price'] ) ) {
            $product->set_regular_price( (string) $item['new_regular_price'] );
            $changes[] = 'regular price';
        }

        if ( ! empty( $item['clear_sale_price'] ) ) {
            $product->set_sale_price( '' );
            $changes[] = 'sale price cleared';
        } elseif ( array_key_exists( 'new_sale_price', $item ) && null !== $item['new_sale_price'] && '' !== trim( (string) $item['new_sale_price'] ) ) {
            $product->set_sale_price( (string) $item['new_sale_price'] );
            $changes[] = 'sale price';
        }

        if ( empty( $changes ) ) {
            return array(
                'success' => false,
                'error'   => 'Bulk Woo stavka nema nijednu cenovnu promenu za upis.',
            );
        }

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