<?php

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

class Niwa_AI_Admin_Product_Management_Service {
    private $outlet_category_id = 0;

    public function __construct( $outlet_category_id = 0 ) {
        $this->outlet_category_id = max( 0, intval( $outlet_category_id ) );
    }

    public function search_category( $query ) {
        $query = trim( (string) $query );
        if ( '' === $query ) {
            return array( 'success' => false, 'error' => 'Query je prazan.' );
        }

        $terms = get_terms(
            array(
                'taxonomy'   => 'product_cat',
                'name__like' => $query,
                'hide_empty' => false,
                'number'     => 10,
            )
        );
        if ( is_wp_error( $terms ) ) {
            return array( 'success' => false, 'error' => $terms->get_error_message() );
        }

        if ( empty( $terms ) ) {
            $terms = get_terms(
                array(
                    'taxonomy'   => 'product_cat',
                    'slug'       => sanitize_title( $query ),
                    'hide_empty' => false,
                )
            );
            if ( is_wp_error( $terms ) ) {
                return array( 'success' => false, 'error' => $terms->get_error_message() );
            }
        }

        $results = array();
        foreach ( (array) $terms as $term ) {
            $parent_name = '';
            if ( ! empty( $term->parent ) ) {
                $parent = get_term( intval( $term->parent ), 'product_cat' );
                if ( $parent && ! is_wp_error( $parent ) ) {
                    $parent_name = (string) $parent->name;
                }
            }

            $results[] = array(
                'id'          => intval( $term->term_id ),
                'name'        => (string) $term->name,
                'slug'        => (string) $term->slug,
                'parent_id'   => intval( $term->parent ),
                'parent_name' => $parent_name,
                'count'       => intval( $term->count ),
            );
        }

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

    public function create_category( $name, $parent_id = 0 ) {
        $name = sanitize_text_field( trim( (string) $name ) );
        if ( '' === $name ) {
            return array( 'success' => false, 'error' => 'Ime kategorije je obavezno.' );
        }

        $existing = get_term_by( 'name', $name, 'product_cat' );
        if ( $existing && ! is_wp_error( $existing ) ) {
            return array(
                'success'         => true,
                'already_existed' => true,
                'category_id'     => intval( $existing->term_id ),
                'name'            => (string) $existing->name,
                'slug'            => (string) $existing->slug,
            );
        }

        $insert_args = array(
            'slug' => sanitize_title( $name ),
        );
        if ( intval( $parent_id ) > 0 ) {
            $insert_args['parent'] = intval( $parent_id );
        }

        $inserted = wp_insert_term( $name, 'product_cat', $insert_args );
        if ( is_wp_error( $inserted ) ) {
            return array( 'success' => false, 'error' => $inserted->get_error_message() );
        }

        $term = get_term( intval( $inserted['term_id'] ), 'product_cat' );
        if ( ! $term || is_wp_error( $term ) ) {
            return array( 'success' => false, 'error' => 'Kategorija je kreirana ali nije mogla da se ucita.' );
        }

        return array(
            'success'         => true,
            'already_existed' => false,
            'category_id'     => intval( $term->term_id ),
            'name'            => (string) $term->name,
            'slug'            => (string) $term->slug,
        );
    }

    public function add_product_category( $product_id, $category_id ) {
        $product = wc_get_product( intval( $product_id ) );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen.' );
        }

        $term = get_term( intval( $category_id ), 'product_cat' );
        if ( ! $term || is_wp_error( $term ) ) {
            return array( 'success' => false, 'error' => 'Kategorija ne postoji.' );
        }

        $categories = array_map( 'intval', (array) $product->get_category_ids() );
        if ( in_array( intval( $category_id ), $categories, true ) ) {
            return array(
                'success'       => false,
                'error'         => 'Proizvod je vec u zadatoj kategoriji; nema kategorije za dodavanje.',
                'product_id'    => intval( $product_id ),
                'product_name'  => (string) $product->get_name(),
                'category_id'   => intval( $category_id ),
                'category_name' => (string) $term->name,
            );
        }

        $categories[] = intval( $category_id );
        $product->set_category_ids( $categories );
        $product->save();
        $this->sync_simple_product( intval( $product_id ) );

        return array(
            'success'        => true,
            'product_id'     => intval( $product_id ),
            'product_name'   => (string) $product->get_name(),
            'added_category' => (string) $term->name,
            'all_categories' => wp_get_post_terms( intval( $product_id ), 'product_cat', array( 'fields' => 'names' ) ),
        );
    }

    public function remove_product_category( $product_id, $category_id ) {
        $product = wc_get_product( intval( $product_id ) );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen.' );
        }

        $term = get_term( intval( $category_id ), 'product_cat' );
        if ( ! $term || is_wp_error( $term ) ) {
            return array( 'success' => false, 'error' => 'Kategorija ne postoji.' );
        }

        $current_categories = array_map( 'intval', (array) $product->get_category_ids() );
        if ( ! in_array( intval( $category_id ), $current_categories, true ) ) {
            return array(
                'success'       => false,
                'error'         => 'Proizvod nije u zadatoj kategoriji; nema kategorije za uklanjanje.',
                'product_id'    => intval( $product_id ),
                'product_name'  => (string) $product->get_name(),
                'category_id'   => intval( $category_id ),
                'category_name' => (string) $term->name,
            );
        }

        $categories = array_values(
            array_filter(
                $current_categories,
                function ( $item ) use ( $category_id ) {
                    return intval( $item ) !== intval( $category_id );
                }
            )
        );

        $product->set_category_ids( $categories );
        $product->save();
        $this->sync_simple_product( intval( $product_id ) );

        return array(
            'success'          => true,
            'product_id'       => intval( $product_id ),
            'product_name'     => (string) $product->get_name(),
            'removed_category' => (string) $term->name,
            'all_categories'   => wp_get_post_terms( intval( $product_id ), 'product_cat', array( 'fields' => 'names' ) ),
        );
    }

    public function list_product_categories( $product_id ) {
        $product = wc_get_product( intval( $product_id ) );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen.' );
        }

        $terms = wp_get_post_terms( intval( $product_id ), 'product_cat' );
        if ( is_wp_error( $terms ) ) {
            return array( 'success' => false, 'error' => $terms->get_error_message() );
        }

        $categories = array();
        foreach ( (array) $terms as $term ) {
            $categories[] = array(
                'id'   => intval( $term->term_id ),
                'name' => (string) $term->name,
                'slug' => (string) $term->slug,
            );
        }

        return array(
            'success'      => true,
            'product_id'   => intval( $product_id ),
            'product_name' => (string) $product->get_name(),
            'categories'   => $categories,
        );
    }

    public function update_simple_product_state( $product_id, $args ) {
        $product_id = intval( $product_id );
        $product    = wc_get_product( $product_id );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen.' );
        }

        $args    = is_array( $args ) ? $args : array();
        $changes = array();
        $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 && ! $product->is_type( 'simple' ) ) {
            return array(
                'success' => false,
                'error'   => 'Za variable proizvod koristi variation alate ili bulk_set_individual_prices za cenu, sale cenu i stock.',
            );
        }

        if ( array_key_exists( 'regular_price', $args ) && null !== $args['regular_price'] && '' !== trim( (string) $args['regular_price'] ) ) {
            $product->set_regular_price( (string) $args['regular_price'] );
            $changes[] = 'regular_price';
        }

        if ( ! empty( $args['clear_sale_price'] ) ) {
            $product->set_sale_price( '' );
            $changes[] = 'sale_price_cleared';
        } elseif ( array_key_exists( 'sale_price', $args ) && null !== $args['sale_price'] && '' !== trim( (string) $args['sale_price'] ) ) {
            $product->set_sale_price( (string) $args['sale_price'] );
            $changes[] = 'sale_price';
        }

        if ( ! empty( $args['clear_sale_dates'] ) ) {
            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_dates_cleared';
        } else {
            if ( array_key_exists( 'date_on_sale_from', $args ) && null !== $args['date_on_sale_from'] && '' !== trim( (string) $args['date_on_sale_from'] ) && method_exists( $product, 'set_date_on_sale_from' ) ) {
                $product->set_date_on_sale_from( (string) $args['date_on_sale_from'] );
                $changes[] = 'date_on_sale_from';
            }
            if ( array_key_exists( 'date_on_sale_to', $args ) && null !== $args['date_on_sale_to'] && '' !== trim( (string) $args['date_on_sale_to'] ) && method_exists( $product, 'set_date_on_sale_to' ) ) {
                $product->set_date_on_sale_to( (string) $args['date_on_sale_to'] );
                $changes[] = 'date_on_sale_to';
            }
        }

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

        $requested_state_changes = $changes;
        $has_pending_product_save = false;
        if ( ! empty( $requested_state_changes ) ) {
            if ( method_exists( $product, 'get_changes' ) && empty( $product->get_changes() ) ) {
                $changes = array();
            } else {
                $has_pending_product_save = true;
            }
        }

        $category_ids = isset( $args['category_ids'] ) && is_array( $args['category_ids'] ) ? array_values( array_filter( array_map( 'intval', $args['category_ids'] ) ) ) : array();
        if ( ! empty( $category_ids ) ) {
            $category_action = isset( $args['category_action'] ) ? (string) $args['category_action'] : 'set';
            $requested_state_changes[] = 'categories_' . $category_action;
            $current_categories = array_values( array_unique( array_map( 'intval', (array) $product->get_category_ids() ) ) );
            $categories         = $current_categories;

            if ( 'add' === $category_action ) {
                $categories = array_values( array_unique( array_merge( $categories, $category_ids ) ) );
            } elseif ( 'remove' === $category_action ) {
                $categories = array_values(
                    array_filter(
                        $categories,
                        function ( $item ) use ( $category_ids ) {
                            return ! in_array( intval( $item ), $category_ids, true );
                        }
                    )
                );
            } else {
                $categories = $category_ids;
            }

            sort( $current_categories );
            sort( $categories );
            if ( $categories !== $current_categories ) {
                $product->set_category_ids( $categories );
                $has_pending_product_save = true;
                $changes[] = 'categories_' . $category_action;
            }
        }
        if ( $has_pending_product_save ) {
            if ( method_exists( $product, 'get_changes' ) && empty( $product->get_changes() ) ) {
                $changes = array();
            } else {
                $product->save();
            }
        }
        if ( ! empty( $changes ) && empty( $args['skip_reindex'] ) ) {
            $this->sync_simple_product( $product_id );
        }

        if ( empty( $changes ) && ! empty( $requested_state_changes ) ) {
            return array(
                'success'           => true,
                'unchanged'         => true,
                'product_id'        => $product_id,
                'product_name'      => (string) $product->get_name(),
                'requested_changes' => array_values( array_unique( $requested_state_changes ) ),
                'changes'           => array(),
            );
        }

        if ( empty( $changes ) ) {
            return array(
                'success'      => false,
                'error'        => 'Nema product-management promene za proizvod.',
                'product_id'   => $product_id,
                'product_name' => (string) $product->get_name(),
            );
        }

        return array(
            'success'      => true,
            'product_id'   => $product_id,
            'product_name' => (string) $product->get_name(),
            'changes'      => $changes,
        );
    }

    public function update_named_attribute( $product_id, $args ) {
        $product_id = intval( $product_id );
        $product    = wc_get_product( $product_id );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen.' );
        }

        $args            = is_array( $args ) ? $args : array();
        $attribute_name  = isset( $args['attribute_name'] ) ? sanitize_text_field( trim( (string) $args['attribute_name'] ) ) : '';
        $action          = isset( $args['action'] ) ? (string) $args['action'] : 'add';
        $values          = isset( $args['attribute_values'] ) && is_array( $args['attribute_values'] ) ? array_values( array_filter( array_map( 'sanitize_text_field', $args['attribute_values'] ) ) ) : array();
        $visible         = ! array_key_exists( 'visible', $args ) || ! empty( $args['visible'] );
        $for_variations  = ! array_key_exists( 'used_for_variations', $args ) || ! empty( $args['used_for_variations'] );

        if ( '' === $attribute_name ) {
            return array( 'success' => false, 'error' => 'Naziv atributa je obavezan.' );
        }

        $attributes = $product->get_attributes();
        $attribute_slug = $this->resolve_attribute_taxonomy_for_product(
            $product,
            $attribute_name,
            isset( $args['attribute_slug'] ) ? (string) $args['attribute_slug'] : ''
        );

        if ( 'remove' === $action ) {
            $removed              = false;
            $had_assigned_terms   = false;
            $assigned_term_ids    = array();
            foreach ( $attributes as $key => $attribute ) {
                if ( is_a( $attribute, 'WC_Product_Attribute' ) && (string) $attribute->get_name() === $attribute_slug ) {
                    unset( $attributes[ $key ] );
                    $removed = true;
                }
            }

            if ( taxonomy_exists( $attribute_slug ) ) {
                $assigned_term_ids = wp_get_object_terms( $product_id, $attribute_slug, array( 'fields' => 'ids' ) );
                if ( ! is_wp_error( $assigned_term_ids ) ) {
                    $assigned_term_ids  = array_values( array_filter( array_map( 'intval', (array) $assigned_term_ids ) ) );
                    $had_assigned_terms = ! empty( $assigned_term_ids );
                } else {
                    $assigned_term_ids = array();
                }
            }

            if ( ! $removed && ! $had_assigned_terms ) {
                return array(
                    'success'      => true,
                    'unchanged'    => true,
                    'product_id'   => $product_id,
                    'product_name' => (string) $product->get_name(),
                    'action'       => 'remove',
                    'attribute'    => $attribute_name,
                );
            }

            if ( $removed ) {
                $product->set_attributes( array_values( $attributes ) );
                $product->save();
            }
            if ( taxonomy_exists( $attribute_slug ) && $had_assigned_terms ) {
                wp_set_object_terms( $product_id, array(), $attribute_slug, false );
            }
            if ( $product->is_type( 'variable' ) ) {
                $this->sync_parent( $product_id );
            } else {
                $this->sync_simple_product( $product_id );
            }

            return array(
                'success'      => true,
                'product_id'   => $product_id,
                'product_name' => (string) $product->get_name(),
                'action'       => 'remove',
                'attribute'    => $attribute_name,
            );
        }

        if ( empty( $values ) ) {
            return array( 'success' => false, 'error' => 'Lista vrednosti atributa je prazna.' );
        }

        $this->ensure_product_attribute_taxonomy( $attribute_name, $attribute_slug );
        $term_ids = $this->ensure_attribute_terms( $attribute_slug, $values );
        if ( empty( $term_ids ) ) {
            return array(
                'success'      => false,
                'error'        => 'Nijedna vrednost atributa nije mogla da se razresi ili kreira.',
                'product_id'   => $product_id,
                'product_name' => (string) $product->get_name(),
                'action'       => 'add',
                'attribute'    => $attribute_name,
                'values'       => $values,
            );
        }

        $assigned_term_ids = array();
        if ( taxonomy_exists( $attribute_slug ) ) {
            $current_terms = wp_get_object_terms( $product_id, $attribute_slug, array( 'fields' => 'ids' ) );
            if ( ! is_wp_error( $current_terms ) ) {
                $assigned_term_ids = array_values( array_filter( array_map( 'intval', (array) $current_terms ) ) );
            }
        }
        $updated  = false;

        foreach ( $attributes as $key => $attribute ) {
            if ( ! is_a( $attribute, 'WC_Product_Attribute' ) || (string) $attribute->get_name() !== $attribute_slug ) {
                continue;
            }

            $existing_options = array_map( 'intval', (array) $attribute->get_options() );
            $next_options     = array_values( array_unique( array_merge( $existing_options, $term_ids ) ) );
            $existing_compare = array_values( array_unique( array_filter( $existing_options ) ) );
            $next_compare     = array_values( array_unique( array_filter( $next_options ) ) );
            sort( $existing_compare );
            sort( $next_compare );

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

            if (
                $existing_compare === $next_compare
                && (bool) $attribute->get_visible() === $visible
                && (bool) $attribute->get_variation() === $for_variations
                && $terms_already_assigned
            ) {
                return array(
                    'success'      => true,
                    'unchanged'    => true,
                    'product_id'   => $product_id,
                    'product_name' => (string) $product->get_name(),
                    'action'       => 'add',
                    'attribute'    => $attribute_name,
                    'values'       => $values,
                );
            }

            $attributes[ $key ] = $this->clone_attribute_with_options( $attribute, $next_options, $visible, $for_variations );
            $updated            = true;
            break;
        }

        if ( ! $updated ) {
            $attribute = new WC_Product_Attribute();
            $attribute->set_id( wc_attribute_taxonomy_id_by_name( str_replace( 'pa_', '', $attribute_slug ) ) );
            $attribute->set_name( $attribute_slug );
            $attribute->set_options( $term_ids );
            $attribute->set_visible( $visible );
            $attribute->set_variation( $for_variations );
            $attribute->set_position( count( $attributes ) );
            $attributes[] = $attribute;
        }

        $product->set_attributes( $attributes );
        $product->save();
        wp_set_object_terms( $product_id, $term_ids, $attribute_slug, true );
        if ( $product->is_type( 'variable' ) && $for_variations ) {
            $this->sync_parent( $product_id );
        } else {
            $this->sync_simple_product( $product_id );
        }

        return array(
            'success'      => true,
            'product_id'   => $product_id,
            'product_name' => (string) $product->get_name(),
            'action'       => 'add',
            'attribute'    => $attribute_name,
            'values'       => $values,
        );
    }

    public function update_variation_state( $product_id, $variation_id, $args ) {
        $product_id   = intval( $product_id );
        $variation_id = intval( $variation_id );
        $variation    = wc_get_product( $variation_id );
        if ( ! $variation || ! $variation->is_type( 'variation' ) || intval( $variation->get_parent_id() ) !== $product_id ) {
            return array( 'success' => false, 'error' => 'Varijacija nije pronadjena za dati proizvod.' );
        }

        $args              = is_array( $args ) ? $args : array();
        $changes           = array();
        $defer_parent_sync = ! empty( $args['defer_parent_sync'] );

        if ( array_key_exists( 'manage_stock', $args ) ) {
            $variation->set_manage_stock( ! empty( $args['manage_stock'] ) );
            $changes[] = 'manage_stock';
        }
        if ( array_key_exists( 'stock_quantity', $args ) && null !== $args['stock_quantity'] && '' !== (string) $args['stock_quantity'] ) {
            $quantity = max( 0, intval( $args['stock_quantity'] ) );
            $variation->set_manage_stock( true );
            $variation->set_stock_quantity( $quantity );
            $variation->set_stock_status( $quantity > 0 ? 'instock' : 'outofstock' );
            $changes[] = 'stock_quantity';
        }
        if ( array_key_exists( 'status', $args ) && '' !== trim( (string) $args['status'] ) ) {
            $variation->set_status( sanitize_text_field( (string) $args['status'] ) );
            $changes[] = 'status';
        }
        if ( array_key_exists( 'regular_price', $args ) && null !== $args['regular_price'] && '' !== trim( (string) $args['regular_price'] ) ) {
            $variation->set_regular_price( (string) $args['regular_price'] );
            $changes[] = 'regular_price';
        }
        if ( ! empty( $args['clear_sale_price'] ) ) {
            $variation->set_sale_price( '' );
            $changes[] = 'sale_price_cleared';
        } elseif ( array_key_exists( 'sale_price', $args ) && null !== $args['sale_price'] && '' !== trim( (string) $args['sale_price'] ) ) {
            $variation->set_sale_price( (string) $args['sale_price'] );
            $changes[] = 'sale_price';
        }
        if ( array_key_exists( 'sku', $args ) && null !== $args['sku'] && '' !== trim( (string) $args['sku'] ) ) {
            $variation->set_sku( sanitize_text_field( (string) $args['sku'] ) );
            $changes[] = 'sku';
        }

        if ( empty( $changes ) ) {
            return array( 'success' => false, 'error' => 'Nema product-management promene za varijaciju.' );
        }

        if ( method_exists( $variation, 'get_changes' ) && empty( $variation->get_changes() ) ) {
            return array(
                'success'           => false,
                'error'             => 'Varijacija je vec u trazenom stanju; nema promene za snimanje.',
                'variation_id'      => $variation_id,
                'product_id'        => $product_id,
                'requested_changes' => $changes,
                'stock_quantity'    => $variation->get_stock_quantity(),
                'stock_status'      => $variation->get_stock_status(),
            );
        }

        $variation->save();
        if ( ! $defer_parent_sync ) {
            $this->sync_parent( $product_id );
        }

        return array(
            'success'        => true,
            'variation_id'   => $variation_id,
            'product_id'     => $product_id,
            'changes'        => $changes,
            'stock_quantity' => $variation->get_stock_quantity(),
            'stock_status'   => $variation->get_stock_status(),
        );
    }

    public function create_variation( $product_id, $args ) {
        $product_id = intval( $product_id );
        $product    = wc_get_product( $product_id );
        if ( ! $product || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'Ciljni proizvod nije variable.' );
        }

        $args            = is_array( $args ) ? $args : array();
        $normalized_meta = isset( $args['normalized_meta'] ) && is_array( $args['normalized_meta'] ) ? $args['normalized_meta'] : array();
        $attributes      = array();

        if ( ! empty( $normalized_meta ) ) {
            foreach ( $normalized_meta as $meta_key => $value ) {
                $meta_key = (string) $meta_key;
                if ( 0 !== strpos( $meta_key, 'attribute_' ) ) {
                    continue;
                }
                $attributes[ substr( $meta_key, 10 ) ] = (string) $value;
            }
        } else {
            $attr_keys = $this->detect_attr_keys( $product );
            if ( ! empty( $args['color'] ) ) {
                $attributes[ $attr_keys['boja'] ] = sanitize_text_field( (string) $args['color'] );
            }
            if ( ! empty( $args['size'] ) ) {
                $attributes[ $attr_keys['velicina'] ] = sanitize_text_field( (string) $args['size'] );
            }
        }

        if ( empty( $attributes ) ) {
            return array( 'success' => false, 'error' => 'Nedostaju atributi za novu varijaciju.' );
        }

        $existing_variation_id = $this->find_existing_variation_id_by_attributes( $product, $attributes );
        if ( $existing_variation_id > 0 ) {
            return array(
                'success'               => false,
                'error'                 => 'Takva varijacija vec postoji za zadati proizvod; nema nove varijacije za kreiranje.',
                'product_id'            => $product_id,
                'existing_variation_id' => $existing_variation_id,
                'attributes'            => $attributes,
            );
        }

        $variation = new WC_Product_Variation();
        $variation->set_parent_id( $product_id );
        $variation->set_attributes( $attributes );

        if ( array_key_exists( 'regular_price', $args ) && null !== $args['regular_price'] && '' !== trim( (string) $args['regular_price'] ) ) {
            $variation->set_regular_price( (string) $args['regular_price'] );
        } elseif ( ! empty( $args['price'] ) ) {
            $variation->set_regular_price( (string) $args['price'] );
        }

        if ( array_key_exists( 'sale_price', $args ) && null !== $args['sale_price'] && '' !== trim( (string) $args['sale_price'] ) ) {
            $variation->set_sale_price( (string) $args['sale_price'] );
        }

        if ( array_key_exists( 'sku', $args ) && null !== $args['sku'] && '' !== trim( (string) $args['sku'] ) ) {
            $variation->set_sku( sanitize_text_field( (string) $args['sku'] ) );
        }

        $has_stock_quantity = array_key_exists( 'stock_quantity', $args ) && null !== $args['stock_quantity'] && '' !== (string) $args['stock_quantity'];
        if ( ! $has_stock_quantity && array_key_exists( 'qty', $args ) && null !== $args['qty'] && '' !== (string) $args['qty'] ) {
            $args['stock_quantity'] = $args['qty'];
            $has_stock_quantity     = true;
        }

        if ( $has_stock_quantity ) {
            $quantity = max( 0, intval( $args['stock_quantity'] ) );
            $variation->set_manage_stock( true );
            $variation->set_stock_quantity( $quantity );
            $variation->set_stock_status( $quantity > 0 ? 'instock' : 'outofstock' );
        } else {
            $manage_stock = array_key_exists( 'manage_stock', $args ) ? (bool) $args['manage_stock'] : false;
            $stock_status = isset( $args['stock_status'] ) ? sanitize_key( (string) $args['stock_status'] ) : 'instock';
            if ( ! in_array( $stock_status, array( 'instock', 'outofstock', 'onbackorder' ), true ) ) {
                $stock_status = 'instock';
            }
            $variation->set_manage_stock( $manage_stock );
            $variation->set_stock_status( $stock_status );
        }

        if ( isset( $args['backorders'] ) && in_array( (string) $args['backorders'], array( 'no', 'notify', 'yes' ), true ) ) {
            $variation->set_backorders( (string) $args['backorders'] );
        }

        $image_id = isset( $args['attachment_id'] ) ? intval( $args['attachment_id'] ) : 0;
        if ( $image_id <= 0 && ! empty( $args['image_url'] ) && ( ! array_key_exists( 'set_image', $args ) || ! empty( $args['set_image'] ) ) ) {
            $image = Niwa_AI_Admin_Autonomy_Image_Utils::ingest_from_url( esc_url_raw( (string) $args['image_url'] ), get_the_title( $product_id ), $product_id );
            if ( ! empty( $image['success'] ) && ! empty( $image['attachment_id'] ) ) {
                $image_id = intval( $image['attachment_id'] );
            }
        }
        if ( $image_id > 0 && ( ! array_key_exists( 'set_image', $args ) || ! empty( $args['set_image'] ) ) ) {
            $variation->set_image_id( $image_id );
        }

        $variation->set_status( 'publish' );
        $variation->save();

        $this->sync_parent( $product_id );

        return array(
            'success'      => true,
            'variation_id' => intval( $variation->get_id() ),
            'product_id'   => $product_id,
            'attributes'   => $attributes,
            'image_id'      => $image_id,
        );
    }

    public function set_price( $product_id, $args ) {
        $product_id = intval( $product_id );
        $product    = wc_get_product( $product_id );
        if ( ! $product ) {
            return array( 'success' => false, 'error' => 'Proizvod nije pronadjen.' );
        }

        $args             = is_array( $args ) ? $args : array();
        $color_filter     = ! empty( $args['color'] ) ? mb_strtolower( trim( (string) $args['color'] ) ) : null;
        $variation_filter = ! empty( $args['variation_id'] ) ? intval( $args['variation_id'] ) : 0;
        $fixed_price      = isset( $args['price'] ) ? $args['price'] : null;
        $percent_change   = isset( $args['percent_change'] ) ? floatval( $args['percent_change'] ) : null;
        $amount_change    = isset( $args['amount_change'] ) ? floatval( $args['amount_change'] ) : null;
        $sale_price       = array_key_exists( 'sale_price', $args ) ? $args['sale_price'] : null;

        if ( $product->is_type( 'simple' ) ) {
            $old_price = floatval( $product->get_regular_price() );
            $new_price = $this->calc_new_price( $old_price, $fixed_price, $percent_change, $amount_change );
            $product->set_regular_price( strval( round( $new_price ) ) );
            if ( null !== $sale_price ) {
                $product->set_sale_price( '' === (string) $sale_price ? '' : strval( round( floatval( $sale_price ) ) ) );
            }

            if ( method_exists( $product, 'get_changes' ) && empty( $product->get_changes() ) ) {
                return array(
                    'success'      => false,
                    'error'        => 'Cena proizvoda je vec u trazenom stanju; nema promene za snimanje.',
                    'product_id'   => $product_id,
                    'product_name' => (string) $product->get_name(),
                    'type'         => 'simple',
                    'old_price'    => $old_price,
                    'new_price'    => round( $new_price ),
                    'sale_price'   => $product->get_sale_price(),
                );
            }

            $product->save();
            $this->sync_simple_product( $product_id );

            return array(
                'success'    => true,
                'product_id' => $product_id,
                'type'       => 'simple',
                'old_price'  => $old_price,
                'new_price'  => round( $new_price ),
                'sale_price' => $product->get_sale_price(),
            );
        }

        if ( ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'Proizvod nije simple ni variable.' );
        }

        $updated = array();
        $matched = 0;
        foreach ( (array) $product->get_children() as $variation_id ) {
            if ( $variation_filter > 0 && intval( $variation_id ) !== $variation_filter ) {
                continue;
            }

            $variation = wc_get_product( intval( $variation_id ) );
            if ( ! $variation || ! $variation->is_type( 'variation' ) ) {
                continue;
            }

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

            ++$matched;
            $old_price = floatval( $variation->get_regular_price() );
            $new_price = $this->calc_new_price( $old_price, $fixed_price, $percent_change, $amount_change );
            $variation->set_regular_price( strval( round( $new_price ) ) );

            if ( null !== $sale_price ) {
                $variation->set_sale_price( '' === (string) $sale_price ? '' : strval( round( floatval( $sale_price ) ) ) );
            }

            if ( method_exists( $variation, 'get_changes' ) && empty( $variation->get_changes() ) ) {
                continue;
            }

            $variation->save();

            $attrs     = $this->extract_var_attrs( $variation );
            $updated[] = array(
                'variation_id' => intval( $variation_id ),
                'boja'         => $attrs['color'],
                'velicina'     => $attrs['size'],
                'old_price'    => $old_price,
                'new_price'    => round( $new_price ),
                'sale_price'   => $variation->get_sale_price(),
            );
        }

        if ( empty( $updated ) ) {
            if ( $matched > 0 ) {
                return array(
                    'success'       => false,
                    'error'         => 'Cena varijacija je vec u trazenom stanju; nema promene za snimanje.',
                    'product_id'    => $product_id,
                    'product_name'  => (string) $product->get_name(),
                    'type'          => 'variable',
                    'filter_color'  => $color_filter,
                    'variation_id'  => $variation_filter,
                    'matched_count' => $matched,
                );
            }

            return array(
                'success'      => false,
                'error'        => 'Nijedna varijacija nije odgovarala zadatom selectoru za promenu cene.',
                'product_id'   => $product_id,
                'product_name' => (string) $product->get_name(),
                'type'         => 'variable',
                'filter_color' => $color_filter,
                'variation_id' => $variation_filter,
            );
        }

        $this->sync_parent( $product_id );

        return array(
            'success'       => true,
            'product_id'    => $product_id,
            'product_name'  => (string) $product->get_name(),
            'type'          => 'variable',
            'filter_color'  => $color_filter,
            'updated_count' => count( $updated ),
            'variations'    => $updated,
        );
    }

    public function remove_color( $product_id, $color ) {
        $product_id = intval( $product_id );
        $product    = wc_get_product( $product_id );
        if ( ! $product || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'Variable proizvod nije pronadjen.' );
        }

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

        $deleted                  = array();
        $attribute_option_removed = false;
        foreach ( (array) $product->get_children() as $variation_id ) {
            $variation = wc_get_product( intval( $variation_id ) );
            if ( ! $variation || ! $variation->is_type( 'variation' ) ) {
                continue;
            }
            $attrs = $this->extract_var_attrs( $variation );
            if ( mb_strtolower( (string) $attrs['color'] ) !== mb_strtolower( $color ) ) {
                continue;
            }
            $variation->delete( true );
            $deleted[] = intval( $variation_id );
        }

        $attributes = $product->get_attributes();
        foreach ( $attributes as $key => $attribute ) {
            $normalized_key  = $this->norm_attr_key( $key );
            $normalized_name = $this->norm_attr_key( $attribute->get_name() );
            if ( 'boja' !== $normalized_key && 'boja' !== $normalized_name ) {
                continue;
            }
            $original_options = (array) $attribute->get_options();
            $options          = array_values(
                array_filter(
                    $original_options,
                    function ( $item ) use ( $color ) {
                        return mb_strtolower( (string) $item ) !== mb_strtolower( $color );
                    }
                )
            );
            if ( count( $options ) === count( $original_options ) && $attribute->is_taxonomy() ) {
                $taxonomy = $attribute->get_taxonomy();
                $options  = array_values(
                    array_filter(
                        $original_options,
                        function ( $term_id ) use ( $color, $taxonomy ) {
                            $term = get_term( intval( $term_id ), $taxonomy );
                            if ( ! $term || is_wp_error( $term ) ) {
                                return true;
                            }
                            return mb_strtolower( $term->name ) !== mb_strtolower( $color )
                                && mb_strtolower( $term->slug ) !== mb_strtolower( $color );
                        }
                    )
                );
            }
            if ( count( $options ) !== count( $original_options ) ) {
                $attribute_option_removed = true;
                $attributes[ $key ]       = $this->clone_attribute_with_options( $attribute, $options );
            }
        }

        if ( empty( $deleted ) && ! $attribute_option_removed ) {
            return array(
                'success'       => false,
                'error'         => 'Boja nije pronadjena na zadatom proizvodu; nijedna varijacija ni atribut opcija nisu uklonjeni.',
                'product_id'    => $product_id,
                'product_name'  => (string) $product->get_name(),
                'color'         => $color,
                'deleted_count' => 0,
            );
        }

        if ( $attribute_option_removed ) {
            $product->set_attributes( $attributes );
            $product->save();

            $attr_keys        = $this->detect_attr_keys( $product );
            $remaining_colors = array();
            foreach ( $attributes as $key => $attribute ) {
                $normalized_key  = $this->norm_attr_key( $key );
                $normalized_name = $this->norm_attr_key( $attribute->get_name() );
                if ( 'boja' !== $normalized_key && 'boja' !== $normalized_name ) {
                    continue;
                }
                $remaining_colors = (array) $attribute->get_options();
            }

            // $remaining_colors comes from parent get_options() — already term IDs.
            $this->sync_attribute_terms( $product_id, $attr_keys['boja'], $remaining_colors, true );
        }
        $this->sync_parent( $product_id );

        return array(
            'success'              => true,
            'product_id'           => $product_id,
            'color_removed'        => $color,
            'deleted_variation_ids'=> $deleted,
            'deleted_count'        => count( $deleted ),
            'attribute_option_removed' => $attribute_option_removed,
        );
    }

    public function move_to_outlet( $product_id, $specs ) {
        $product_id = intval( $product_id );
        $product    = wc_get_product( $product_id );
        if ( ! $product || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'error' => 'Variable proizvod nije pronadjen.' );
        }

        $raw_specs = is_array( $specs ) ? array_values( $specs ) : array();
        $specs     = array();
        foreach ( $raw_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;
            }

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

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

        $attr_keys       = $this->detect_attr_keys( $product );
        $fallback_price  = null;
        $price_by_color  = array();
        $vars_by_key     = array();
        $vars_color_only = array();
        $all_vars        = array();

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

            $attrs = $this->extract_var_attrs( $variation );
            $price = $variation->get_regular_price();

            if ( null === $fallback_price && '' !== (string) $price ) {
                $fallback_price = $price;
            }
            if ( '' !== (string) $price && '' !== (string) $attrs['color'] && ! isset( $price_by_color[ mb_strtolower( (string) $attrs['color'] ) ] ) ) {
                $price_by_color[ mb_strtolower( (string) $attrs['color'] ) ] = $price;
            }

            $entry = array(
                'id'    => intval( $variation_id ),
                'color' => (string) $attrs['color'],
                'size'  => (string) $attrs['size'],
                'price' => $price,
            );

            if ( '' !== (string) $attrs['color'] && '' !== (string) $attrs['size'] ) {
                $vars_by_key[ mb_strtolower( (string) $attrs['color'] ) . ':' . (string) $attrs['size'] ] = $entry;
            } elseif ( '' !== (string) $attrs['color'] ) {
                $vars_color_only[ mb_strtolower( (string) $attrs['color'] ) ] = $entry;
            }

            $all_vars[] = $entry;
        }

        $used_variation_ids = array();
        $spec_to_variation  = array();
        $specs_by_color     = array();
        foreach ( $specs as $spec ) {
            $color = (string) $spec['color'];
            if ( ! isset( $specs_by_color[ $color ] ) ) {
                $specs_by_color[ $color ] = array();
            }
            $specs_by_color[ $color ][] = $spec;
        }

        foreach ( $specs_by_color as $color => $color_specs ) {
            usort(
                $color_specs,
                function ( $left, $right ) {
                    $left_size  = isset( $left['size'] ) ? (string) $left['size'] : '';
                    $right_size = isset( $right['size'] ) ? (string) $right['size'] : '';
                    if ( is_numeric( $left_size ) && is_numeric( $right_size ) ) {
                        return intval( $left_size ) <=> intval( $right_size );
                    }
                    return strcmp( $left_size, $right_size );
                }
            );

            $reused_color_only = false;
            $color_key         = mb_strtolower( (string) $color );
            foreach ( $color_specs as $spec ) {
                $size = isset( $spec['size'] ) ? (string) $spec['size'] : '';
                $key  = $color_key . ':' . $size;

                if ( isset( $vars_by_key[ $key ] ) ) {
                    $spec_to_variation[ $key ] = intval( $vars_by_key[ $key ]['id'] );
                    $used_variation_ids[ intval( $vars_by_key[ $key ]['id'] ) ] = true;
                    continue;
                }

                if ( ! $reused_color_only && isset( $vars_color_only[ $color_key ] ) ) {
                    $spec_to_variation[ $key ] = intval( $vars_color_only[ $color_key ]['id'] );
                    $used_variation_ids[ intval( $vars_color_only[ $color_key ]['id'] ) ] = true;
                    $reused_color_only = true;
                }
            }
        }

        $deleted = array();
        foreach ( $all_vars as $entry ) {
            if ( isset( $used_variation_ids[ intval( $entry['id'] ) ] ) ) {
                continue;
            }
            $variation = wc_get_product( intval( $entry['id'] ) );
            if ( $variation ) {
                $variation->delete( true );
                $deleted[] = array(
                    'id'    => intval( $entry['id'] ),
                    'color' => (string) $entry['color'],
                    'size'  => (string) $entry['size'],
                );
            }
        }

        $category_ids = array_map( 'intval', (array) $product->get_category_ids() );
        $outlet_id    = $this->get_outlet_category_id();
        if ( $outlet_id > 0 && ! in_array( $outlet_id, $category_ids, true ) ) {
            $category_ids[] = $outlet_id;
            $product->set_category_ids( $category_ids );
        }

        $colors_used = array_values( array_unique( array_filter( array_map( 'strval', wp_list_pluck( $specs, 'color' ) ) ) ) );
        $sizes_used  = array_values( array_unique( array_filter( array_map( 'strval', wp_list_pluck( $specs, 'size' ) ) ) ) );
        usort(
            $sizes_used,
            function ( $left, $right ) {
                if ( is_numeric( $left ) && is_numeric( $right ) ) {
                    return intval( $left ) <=> intval( $right );
                }
                return strcmp( (string) $left, (string) $right );
            }
        );
        sort( $colors_used );

        $attributes                = $product->get_attributes();
        $had_color                 = false;
        $had_size                  = false;
        $parent_attributes_changed = false;
        foreach ( $attributes as $key => $attribute ) {
            $normalized_key  = $this->norm_attr_key( $key );
            $normalized_name = $this->norm_attr_key( $attribute->get_name() );
            if ( 'boja' === $normalized_key || 'boja' === $normalized_name ) {
                $had_color    = true;
                $next_options = $this->resolve_attribute_options_for_set( $attribute, $colors_used );
                if ( ! $this->attribute_matches_state( $attribute, $next_options, true, true ) ) {
                    $attributes[ $key ]          = $this->clone_attribute_with_options( $attribute, $next_options, true, true );
                    $parent_attributes_changed = true;
                }
            } elseif ( 'velicina' === $normalized_key || 'velicina' === $normalized_name ) {
                $had_size     = true;
                $next_options = $this->resolve_attribute_options_for_set( $attribute, $sizes_used );
                if ( ! $this->attribute_matches_state( $attribute, $next_options, true, true ) ) {
                    $attributes[ $key ]          = $this->clone_attribute_with_options( $attribute, $next_options, true, true );
                    $parent_attributes_changed = true;
                }
            }
        }

        if ( ! $had_color ) {
            $attribute = new WC_Product_Attribute();
            $attribute->set_name( $attr_keys['boja'] );
            $attribute->set_options( $colors_used );
            $attribute->set_variation( true );
            $attribute->set_visible( true );
            $attributes[] = $attribute;
            $parent_attributes_changed = true;
        }
        if ( ! $had_size ) {
            $attribute = new WC_Product_Attribute();
            $attribute->set_name( $attr_keys['velicina'] );
            $attribute->set_options( $sizes_used );
            $attribute->set_variation( true );
            $attribute->set_visible( true );
            $attributes[] = $attribute;
            $parent_attributes_changed = true;
        }

        if ( $parent_attributes_changed ) {
            $product->set_attributes( $attributes );
        }

        $parent_saved = false;
        if ( ! method_exists( $product, 'get_changes' ) || ! empty( $product->get_changes() ) ) {
            $product->save();
            $parent_saved = true;
        }

        if ( $parent_attributes_changed ) {
            $this->sync_attribute_terms( $product_id, $attr_keys['boja'], $colors_used );
            $this->sync_attribute_terms( $product_id, $attr_keys['velicina'], $sizes_used );
        }

        $product   = wc_get_product( $product_id );
        $attr_keys = $this->detect_attr_keys( $product );
        $created   = 0;
        $updated   = 0;
        $unchanged = 0;

        foreach ( $specs as $spec ) {
            $color = isset( $spec['color'] ) ? (string) $spec['color'] : '';
            $size  = isset( $spec['size'] ) ? (string) $spec['size'] : '';
            if ( '' === $color && '' === $size ) {
                continue;
            }
            $qty   = max( 0, intval( isset( $spec['qty'] ) ? $spec['qty'] : 1 ) );
            $price = isset( $price_by_color[ mb_strtolower( $color ) ] ) ? $price_by_color[ mb_strtolower( $color ) ] : ( null !== $fallback_price ? $fallback_price : '0' );
            $key   = mb_strtolower( $color ) . ':' . $size;

            $variation_attrs = array(
                $attr_keys['boja']     => $color,
                $attr_keys['velicina'] => $size,
            );

            if ( isset( $spec_to_variation[ $key ] ) ) {
                $variation = wc_get_product( intval( $spec_to_variation[ $key ] ) );
                if ( $variation ) {
                    $variation->set_manage_stock( true );
                    $variation->set_stock_quantity( $qty );
                    $variation->set_stock_status( $qty > 0 ? 'instock' : 'outofstock' );
                    $variation->set_attributes( $variation_attrs );
                    if ( method_exists( $variation, 'get_changes' ) && empty( $variation->get_changes() ) ) {
                        $unchanged++;
                        continue;
                    }
                    $variation->save();
                    $updated++;
                }
                continue;
            }

            $variation = new WC_Product_Variation();
            $variation->set_parent_id( $product_id );
            $variation->set_attributes( $variation_attrs );
            $variation->set_regular_price( (string) $price );
            $variation->set_manage_stock( true );
            $variation->set_stock_quantity( $qty );
            $variation->set_stock_status( $qty > 0 ? 'instock' : 'outofstock' );
            $variation->set_status( 'publish' );
            $variation->save();
            $created++;
        }

        if ( empty( $deleted ) && 0 === $updated && 0 === $created && ! $parent_saved ) {
            return array(
                'success'         => false,
                'error'           => 'Proizvod je vec u trazenom outlet stanju; nema promene za snimanje.',
                'product_id'      => $product_id,
                'product_name'    => (string) $product->get_name(),
                'deleted_count'   => 0,
                'updated_count'   => 0,
                'unchanged_count' => $unchanged,
                'created_count'   => 0,
                'categories'      => wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'names' ) ),
            );
        }

        $this->sync_parent( $product_id );

        return array(
            'success'         => true,
            'product_id'      => $product_id,
            'product_name'    => (string) $product->get_name(),
            'deleted_count'   => count( $deleted ),
            'deleted_details' => $deleted,
            'updated_count'   => $updated,
            'unchanged_count' => $unchanged,
            'created_count'   => $created,
            'parent_saved'    => $parent_saved,
            'categories'      => wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'names' ) ),
        );
    }

    private function get_outlet_category_id() {
        if ( $this->outlet_category_id > 0 ) {
            return $this->outlet_category_id;
        }

        if ( defined( 'GSM_OUTLET_CAT_ID' ) ) {
            return max( 0, intval( constant( 'GSM_OUTLET_CAT_ID' ) ) );
        }

        return max( 0, intval( apply_filters( 'niwa_ai_product_management_outlet_category_id', 596 ) ) );
    }

    private function ensure_product_attribute_taxonomy( $attribute_name, $attribute_slug ) {
        if ( taxonomy_exists( $attribute_slug ) ) {
            return;
        }

        $created = wc_create_attribute(
            array(
                'name'         => $attribute_name,
                'slug'         => sanitize_title( $attribute_name ),
                'type'         => 'select',
                'order_by'     => 'menu_order',
                'has_archives' => false,
            )
        );

        if ( is_wp_error( $created ) ) {
            $existing = wc_attribute_taxonomy_id_by_name( str_replace( 'pa_', '', $attribute_slug ) );
            if ( ! $existing ) {
                return;
            }
        }

        if ( ! taxonomy_exists( $attribute_slug ) ) {
            register_taxonomy(
                $attribute_slug,
                array( 'product' ),
                array(
                    'hierarchical' => false,
                    'show_ui'      => false,
                    'query_var'    => true,
                    'rewrite'      => false,
                )
            );
        }
    }

    private function ensure_attribute_terms( $taxonomy, $values ) {
        $term_ids = array();
        foreach ( (array) $values as $value ) {
            $value = sanitize_text_field( trim( (string) $value ) );
            if ( '' === $value ) {
                continue;
            }

            $term = get_term_by( 'name', $value, $taxonomy );
            if ( ! $term ) {
                $term = get_term_by( 'slug', sanitize_title( $value ), $taxonomy );
            }
            if ( $term && ! is_wp_error( $term ) ) {
                $term_ids[] = intval( $term->term_id );
                continue;
            }

            $inserted = wp_insert_term( $value, $taxonomy );
            if ( is_wp_error( $inserted ) ) {
                continue;
            }
            $term_ids[] = intval( $inserted['term_id'] );
        }

        return array_values( array_unique( array_filter( $term_ids ) ) );
    }

    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 ( (array) wc_get_attribute_taxonomies() as $attribute ) {
            $label = isset( $attribute->attribute_label ) ? $attribute->attribute_label : '';
            $slug  = isset( $attribute->attribute_name ) ? $attribute->attribute_name : '';
            if ( $needle === $this->normalize_attribute_name_for_comparison( $label ) || $needle === $this->normalize_attribute_name_for_comparison( $slug ) ) {
                return 'pa_' . $slug;
            }
        }

        return '';
    }

    private function normalize_attribute_name_for_comparison( $value ) {
        $value = trim( (string) $value );
        if ( '' === $value ) {
            return '';
        }

        if ( function_exists( 'remove_accents' ) ) {
            $value = remove_accents( $value );
        }

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

    private function resolve_attribute_options_for_set( $attribute, $values ) {
        if ( ! $attribute->is_taxonomy() ) {
            return $values;
        }
        $taxonomy = $attribute->get_taxonomy();
        if ( ! taxonomy_exists( $taxonomy ) ) {
            return $values;
        }
        $term_ids = array();
        foreach ( $values as $value ) {
            $term = get_term_by( 'name', (string) $value, $taxonomy );
            if ( ! $term || is_wp_error( $term ) ) {
                $term = get_term_by( 'slug', sanitize_title( (string) $value ), $taxonomy );
            }
            if ( $term && ! is_wp_error( $term ) ) {
                $term_ids[] = intval( $term->term_id );
            } else {
                $term_ids[] = (string) $value;
            }
        }
        return $term_ids;
    }

    private function clone_attribute_with_options( $attribute, $options, $visible = null, $variation = null ) {
        $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( null === $visible ? $attribute->get_visible() : (bool) $visible );
        $updated_attribute->set_variation( null === $variation ? $attribute->get_variation() : (bool) $variation );

        return $updated_attribute;
    }

    private function attribute_matches_state( $attribute, $options, $visible = null, $variation = null ) {
        $current_options = array_map( 'strval', (array) $attribute->get_options() );
        $next_options    = array_map( 'strval', (array) $options );

        if ( $current_options !== $next_options ) {
            return false;
        }
        if ( null !== $visible && (bool) $attribute->get_visible() !== (bool) $visible ) {
            return false;
        }
        if ( null !== $variation && (bool) $attribute->get_variation() !== (bool) $variation ) {
            return false;
        }

        return true;
    }

    private function find_existing_variation_id_by_attributes( $product, $attributes ) {
        $needle = $this->normalize_variation_attribute_map( $attributes );
        if ( empty( $needle ) ) {
            return 0;
        }

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

            if ( $needle === $this->normalize_variation_attribute_map( $variation->get_attributes() ) ) {
                return intval( $variation_id );
            }
        }

        return 0;
    }

    private function normalize_variation_attribute_map( $attributes ) {
        $normalized = array();
        foreach ( (array) $attributes as $key => $value ) {
            $key = preg_replace( '/^attribute_/', '', (string) $key );
            $key = sanitize_title( $key );
            if ( '' === $key || '' === trim( (string) $value ) ) {
                continue;
            }
            $normalized[ $key ] = sanitize_title( (string) $value );
        }
        ksort( $normalized );

        return $normalized;
    }

    /**
     * Attach a variable product's parent taxonomy terms for one attribute.
     *
     * Size names are dangerous because "38"/"41" are simultaneously valid term
     * NAMES and valid term IDs. Resolution therefore ALWAYS goes name → slug, and
     * a numeric value is never silently read as a term_id for human-supplied
     * size/color values. A numeric value is only honoured as a term_id when the
     * caller explicitly passes already-resolved IDs ($values_are_term_ids = true,
     * e.g. parent `get_options()` output) AND that term actually exists in this
     * taxonomy.
     *
     * @param int    $product_id           Variable parent product ID.
     * @param string $taxonomy_key         Attribute taxonomy (e.g. pa_velicina).
     * @param array  $desired_values       Term names/slugs, or term IDs when the flag is set.
     * @param bool   $values_are_term_ids  Whether $desired_values are already term IDs.
     */
    private function sync_attribute_terms( $product_id, $taxonomy_key, $desired_values, $values_are_term_ids = false ) {
        $taxonomy_key = (string) $taxonomy_key;
        if ( 0 !== strpos( $taxonomy_key, 'pa_' ) || ! taxonomy_exists( $taxonomy_key ) ) {
            return;
        }

        $term_ids = array();
        foreach ( (array) $desired_values as $value ) {
            $value = trim( (string) $value );
            if ( '' === $value ) {
                continue;
            }

            $term = false;

            // Internal callers (parent get_options()) pass verified term IDs.
            if ( $values_are_term_ids && is_numeric( $value ) ) {
                $candidate = get_term( intval( $value ), $taxonomy_key );
                if ( $candidate && ! is_wp_error( $candidate ) ) {
                    $term = $candidate;
                }
            }

            // Human values ("38", "41", "crna") are NAMES — never term IDs.
            if ( ! $term ) {
                $term = get_term_by( 'name', $value, $taxonomy_key );
                if ( ! $term || is_wp_error( $term ) ) {
                    $term = get_term_by( 'slug', sanitize_title( $value ), $taxonomy_key );
                }
            }

            if ( $term && ! is_wp_error( $term ) ) {
                $term_ids[] = intval( $term->term_id );
            }
        }

        $term_ids = array_values( array_unique( $term_ids ) );

        wp_set_object_terms( intval( $product_id ), $term_ids, $taxonomy_key, false );
    }

    /**
     * Reconcile a variable product's parent variation attributes against the
     * REAL state of its active variations.
     *
     * WooCommerce keeps two layers of data for a variable product: the variations
     * themselves and the parent taxonomy terms / attribute options. After any
     * variation edit these can drift apart (e.g. wildcard "any size" metadata
     * lost, or a duplicate size-like attribute added beside the real size
     * attribute). This reads explicit child metadata as the source of truth,
     * recomputes the correct parent options when children use concrete values,
     * and rewrites the parent ONLY when it differs. Numeric values are resolved
     * by name/slug, never as raw term IDs.
     *
     * @param int $parent_id Variable product ID.
     * @return array{success:bool,changed:bool,colors?:array,sizes?:array}
     */
    public function reconcile_parent_attributes_from_variations( $parent_id ) {
        $parent_id = intval( $parent_id );
        if ( $parent_id <= 0 || ! function_exists( 'wc_get_product' ) ) {
            return array( 'success' => false, 'changed' => false );
        }

        $product = wc_get_product( $parent_id );
        if ( ! $product || ! method_exists( $product, 'is_type' ) || ! $product->is_type( 'variable' ) ) {
            return array( 'success' => false, 'changed' => false );
        }

        $attr_keys   = $this->detect_attr_keys( $product );
        $schema_repair = $this->repair_variation_attribute_meta_schema( $product );
        $this->ensure_explicit_wildcard_variation_meta( $product );
        $values_used_by_attr  = array();
        $has_wildcard_by_attr = array();

        foreach ( (array) $product->get_children() as $variation_id ) {
            $variation = wc_get_product( intval( $variation_id ) );
            if ( ! $variation || ! $variation->is_type( 'variation' ) ) {
                continue;
            }
            if ( ! in_array( (string) $variation->get_status(), array( 'publish', 'private' ), true ) ) {
                continue;
            }

            foreach ( (array) $product->get_attributes() as $attribute ) {
                if ( ! is_a( $attribute, 'WC_Product_Attribute' ) || ! $attribute->get_variation() ) {
                    continue;
                }
                $name     = (string) $attribute->get_name();
                $meta_key = $this->get_variation_meta_key_for_parent_attribute( $attribute );
                $has_meta = metadata_exists( 'post', intval( $variation_id ), $meta_key );
                $value    = $has_meta ? (string) get_post_meta( intval( $variation_id ), $meta_key, true ) : '';
                if ( ! isset( $values_used_by_attr[ $name ] ) ) {
                    $values_used_by_attr[ $name ] = array();
                }
                if ( ! isset( $has_wildcard_by_attr[ $name ] ) ) {
                    $has_wildcard_by_attr[ $name ] = false;
                }
                if ( $has_meta && '' === trim( $value ) ) {
                    $has_wildcard_by_attr[ $name ] = true;
                }
                if ( '' !== trim( $value ) ) {
                    $values_used_by_attr[ $name ][ $value ] = $value;
                }
            }
        }

        $colors_used = array_values( isset( $values_used_by_attr[ $attr_keys['boja'] ] ) ? $values_used_by_attr[ $attr_keys['boja'] ] : array() );
        $sizes_used  = $this->sort_size_values( array_values( isset( $values_used_by_attr[ $attr_keys['velicina'] ] ) ? $values_used_by_attr[ $attr_keys['velicina'] ] : array() ) );
        sort( $colors_used );

        $attributes = $product->get_attributes();
        $changed    = false;
        foreach ( $attributes as $key => $attribute ) {
            if ( ! is_a( $attribute, 'WC_Product_Attribute' ) ) {
                continue;
            }
            if ( ! $attribute->get_variation() ) {
                continue;
            }
            $name   = (string) $attribute->get_name();
            if ( ! empty( $has_wildcard_by_attr[ $name ] ) ) {
                continue;
            }
            $values = isset( $values_used_by_attr[ $name ] ) ? array_values( $values_used_by_attr[ $name ] ) : array();
            if ( empty( $values ) ) {
                continue;
            }
            $values = 'velicina' === $this->semantic_attribute_group( $name ) ? $this->sort_size_values( $values ) : $values;
            sort( $values );
            $next_options = $this->resolve_attribute_options_for_set( $attribute, $values );
            if ( ! $this->attribute_options_match( $attribute, $next_options ) ) {
                $attributes[ $key ] = $this->clone_attribute_with_options( $attribute, $next_options );
                $changed            = true;
            }
        }

        $duplicate_result = $this->remove_duplicate_semantic_variation_attributes( $product, $attributes );
        if ( ! empty( $duplicate_result['changed'] ) ) {
            $attributes = $duplicate_result['attributes'];
            $changed    = true;
        }

        if ( $changed ) {
            $product->set_attributes( array_values( $attributes ) );
            $product->save();
        }

        foreach ( $attributes as $attribute ) {
            if ( ! is_a( $attribute, 'WC_Product_Attribute' ) || ! $attribute->get_variation() ) {
                continue;
            }
            $name   = (string) $attribute->get_name();
            if ( ! empty( $has_wildcard_by_attr[ $name ] ) ) {
                continue;
            }
            $values = isset( $values_used_by_attr[ $name ] ) ? array_values( $values_used_by_attr[ $name ] ) : array();
            if ( ! empty( $values ) ) {
                $values = 'velicina' === $this->semantic_attribute_group( $name ) ? $this->sort_size_values( $values ) : $values;
                $this->sync_attribute_terms( $parent_id, $name, $values );
            }
        }

        return array(
            'success'                 => true,
            'changed'                 => $changed || ! empty( $schema_repair['changed'] ),
            'colors'                  => $colors_used,
            'sizes'                   => $sizes_used,
            'repaired_variation_meta' => isset( $schema_repair['repaired'] ) ? intval( $schema_repair['repaired'] ) : 0,
            'schema_conflicts'        => isset( $schema_repair['conflicts'] ) ? intval( $schema_repair['conflicts'] ) : 0,
        );
    }

    /**
     * Move stale child meta between custom and global Woo attribute schemas.
     * The mapping is structural (pa_ prefix), never based on language keywords.
     */
    private function repair_variation_attribute_meta_schema( $product ) {
        if ( ! $product || ! method_exists( $product, 'get_attributes' ) || ! method_exists( $product, 'get_children' ) ) {
            return array( 'changed' => false, 'repaired' => 0, 'conflicts' => 0 );
        }

        $definitions = array();
        foreach ( (array) $product->get_attributes() as $attribute ) {
            if ( ! is_a( $attribute, 'WC_Product_Attribute' ) || ! $attribute->get_variation() ) {
                continue;
            }

            $name          = sanitize_title( (string) $attribute->get_name() );
            $expected_name = $attribute->is_taxonomy() ? (string) $attribute->get_name() : $name;
            $alternate     = 0 === strpos( $expected_name, 'pa_' ) ? substr( $expected_name, 3 ) : 'pa_' . $expected_name;
            if ( '' === $expected_name || $alternate === $expected_name ) {
                continue;
            }

            $definitions[] = array(
                'attribute'     => $attribute,
                'expected_key'  => 'attribute_' . $expected_name,
                'alternate_key' => 'attribute_' . $alternate,
            );
        }

        $repaired  = 0;
        $conflicts = 0;
        foreach ( (array) $product->get_children() as $variation_id ) {
            $variation = wc_get_product( intval( $variation_id ) );
            if ( ! $variation || ! $variation->is_type( 'variation' ) || ! in_array( (string) $variation->get_status(), array( 'publish', 'private' ), true ) ) {
                continue;
            }

            foreach ( $definitions as $definition ) {
                $expected_exists = metadata_exists( 'post', intval( $variation_id ), $definition['expected_key'] );
                $alternate_exists = metadata_exists( 'post', intval( $variation_id ), $definition['alternate_key'] );
                if ( ! $alternate_exists ) {
                    continue;
                }

                $expected_value  = $expected_exists ? trim( (string) get_post_meta( intval( $variation_id ), $definition['expected_key'], true ) ) : '';
                $alternate_value = trim( (string) get_post_meta( intval( $variation_id ), $definition['alternate_key'], true ) );
                if ( '' === $alternate_value ) {
                    if ( ! $expected_exists ) {
                        update_post_meta( intval( $variation_id ), $definition['expected_key'], '' );
                    }
                    delete_post_meta( intval( $variation_id ), $definition['alternate_key'] );
                    $repaired++;
                    continue;
                }

                $resolved = $this->resolve_variation_meta_value_for_parent_attribute( $definition['attribute'], $alternate_value );
                if ( null === $resolved ) {
                    $conflicts++;
                    continue;
                }

                if ( '' !== $expected_value && $this->normalize_attribute_name_for_comparison( $expected_value ) !== $this->normalize_attribute_name_for_comparison( $resolved ) ) {
                    $conflicts++;
                    continue;
                }

                if ( '' === $expected_value ) {
                    update_post_meta( intval( $variation_id ), $definition['expected_key'], $resolved );
                }
                delete_post_meta( intval( $variation_id ), $definition['alternate_key'] );
                if ( function_exists( 'clean_post_cache' ) ) {
                    clean_post_cache( intval( $variation_id ) );
                }
                $repaired++;
            }
        }

        return array( 'changed' => $repaired > 0, 'repaired' => $repaired, 'conflicts' => $conflicts );
    }

    private function resolve_variation_meta_value_for_parent_attribute( $attribute, $value ) {
        $value = trim( (string) $value );
        if ( '' === $value || ! is_a( $attribute, 'WC_Product_Attribute' ) ) {
            return null;
        }

        if ( $attribute->is_taxonomy() ) {
            $taxonomy = (string) $attribute->get_name();
            $term     = get_term_by( 'slug', sanitize_title( $value ), $taxonomy );
            if ( ! $term || is_wp_error( $term ) ) {
                $term = get_term_by( 'name', $value, $taxonomy );
            }
            if ( ! $term || is_wp_error( $term ) || ! in_array( intval( $term->term_id ), array_map( 'intval', (array) $attribute->get_options() ), true ) ) {
                return null;
            }
            return (string) $term->slug;
        }

        $needle = $this->normalize_attribute_name_for_comparison( $value );
        foreach ( (array) $attribute->get_options() as $option ) {
            if ( $needle === $this->normalize_attribute_name_for_comparison( $option ) ) {
                return (string) $option;
            }
        }

        return null;
    }

    private function get_variation_meta_key_for_parent_attribute( $attribute ) {
        $name = (string) $attribute->get_name();
        return 'attribute_' . ( $attribute->is_taxonomy() ? $name : sanitize_title( $name ) );
    }

    private function ensure_explicit_wildcard_variation_meta( $product ) {
        if ( ! $product || ! method_exists( $product, 'get_attributes' ) || ! method_exists( $product, 'get_children' ) ) {
            return;
        }

        $variation_attr_names = array();
        foreach ( (array) $product->get_attributes() as $attribute ) {
            if ( is_a( $attribute, 'WC_Product_Attribute' ) && $attribute->get_variation() ) {
                $variation_attr_names[] = (string) $attribute->get_name();
            }
        }
        if ( empty( $variation_attr_names ) ) {
            return;
        }

        foreach ( (array) $product->get_children() as $variation_id ) {
            $variation = wc_get_product( intval( $variation_id ) );
            if ( ! $variation || ! $variation->is_type( 'variation' ) || ! in_array( (string) $variation->get_status(), array( 'publish', 'private' ), true ) ) {
                continue;
            }
            foreach ( $variation_attr_names as $name ) {
                $meta_key = $this->get_variation_meta_key_for_parent_attribute( $attribute );
                if ( ! metadata_exists( 'post', intval( $variation_id ), $meta_key ) ) {
                    update_post_meta( intval( $variation_id ), $meta_key, '' );
                }
            }
        }
    }

    private function remove_duplicate_semantic_variation_attributes( $product, $attributes ) {
        $seen    = array();
        $changed = false;
        foreach ( $attributes as $key => $attribute ) {
            if ( ! is_a( $attribute, 'WC_Product_Attribute' ) || ! $attribute->get_variation() ) {
                continue;
            }
            $semantic = $this->semantic_attribute_group( $attribute->get_name() );
            if ( '' === $semantic ) {
                continue;
            }
            if ( isset( $seen[ $semantic ] ) ) {
                $this->delete_variation_attribute_meta( $product, (string) $attribute->get_name() );
                if ( taxonomy_exists( (string) $attribute->get_name() ) ) {
                    wp_set_object_terms( intval( $product->get_id() ), array(), (string) $attribute->get_name(), false );
                }
                unset( $attributes[ $key ] );
                $changed = true;
                continue;
            }
            $seen[ $semantic ] = $attribute->get_name();
        }

        return array(
            'changed'    => $changed,
            'attributes' => $attributes,
        );
    }

    private function delete_variation_attribute_meta( $product, $attribute_name ) {
        if ( ! $product || ! method_exists( $product, 'get_children' ) ) {
            return;
        }
        $attribute_name = sanitize_key( (string) $attribute_name );
        if ( '' === $attribute_name ) {
            return;
        }
        $meta_key = 'attribute_' . $attribute_name;
        foreach ( (array) $product->get_children() as $variation_id ) {
            if ( metadata_exists( 'post', intval( $variation_id ), $meta_key ) ) {
                delete_post_meta( intval( $variation_id ), $meta_key );
            }
        }
    }

    private function resolve_attribute_taxonomy_for_product( $product, $attribute_name, $requested_slug = '' ) {
        $needle = $this->normalize_attribute_name_for_comparison( $attribute_name );
        if ( $product && '' !== $needle ) {
            foreach ( (array) $product->get_attributes() as $attribute ) {
                if ( ! is_a( $attribute, 'WC_Product_Attribute' ) ) {
                    continue;
                }
                $name  = (string) $attribute->get_name();
                $label = function_exists( 'wc_attribute_label' ) ? wc_attribute_label( $name, $product ) : $name;
                if (
                    $needle === $this->normalize_attribute_name_for_comparison( $name )
                    || $needle === $this->normalize_attribute_name_for_comparison( str_replace( 'pa_', '', $name ) )
                    || $needle === $this->normalize_attribute_name_for_comparison( $label )
                ) {
                    return $name;
                }
            }
        }

        $semantic = $this->semantic_attribute_group( $attribute_name );
        if ( $product && '' !== $semantic ) {
            foreach ( (array) $product->get_attributes() as $attribute ) {
                if ( ! is_a( $attribute, 'WC_Product_Attribute' ) ) {
                    continue;
                }
                if ( $semantic === $this->semantic_attribute_group( $attribute->get_name() ) ) {
                    return (string) $attribute->get_name();
                }
            }
        }

        $requested_slug = trim( (string) $requested_slug );
        if ( '' !== $requested_slug && taxonomy_exists( $requested_slug ) ) {
            return $requested_slug;
        }

        $attribute_slug = $this->resolve_existing_attribute_taxonomy( $attribute_name );
        if ( '' !== $attribute_slug ) {
            return $attribute_slug;
        }

        return 'pa_' . sanitize_title( $attribute_name );
    }

    private function semantic_attribute_group( $name ) {
        $key = $this->normalize_attribute_name_for_comparison( str_replace( 'pa_', '', (string) $name ) );
        if ( in_array( $key, array( 'velicina', 'broj', 'br', 'size', 'sizes', 'shoesize', 'number' ), true ) ) {
            return 'velicina';
        }
        if ( in_array( $key, array( 'boja', 'color', 'colour', 'farbe', 'farben' ), true ) ) {
            return 'boja';
        }
        return '';
    }

    /**
     * Order-insensitive comparison of an attribute's current options against a
     * freshly resolved option set, so reconciliation only writes on real change.
     */
    private function attribute_options_match( $attribute, $next_options ) {
        $current = array_map( 'strval', (array) $attribute->get_options() );
        $next    = array_map( 'strval', (array) $next_options );
        sort( $current );
        sort( $next );

        return $current === $next;
    }

    /**
     * Sort size values numerically when possible (38, 40, 41…), otherwise alpha.
     */
    private function sort_size_values( $sizes ) {
        usort(
            $sizes,
            function ( $left, $right ) {
                if ( is_numeric( $left ) && is_numeric( $right ) ) {
                    return intval( $left ) <=> intval( $right );
                }
                return strcmp( (string) $left, (string) $right );
            }
        );

        return $sizes;
    }

    private function detect_attr_keys( $product ) {
        $keys = array(
            'boja'     => 'pa_boja',
            'velicina' => 'pa_velicina',
        );

        foreach ( (array) $product->get_attributes() as $taxonomy_key => $attribute ) {
            $name            = method_exists( $attribute, 'get_name' ) ? (string) $attribute->get_name() : (string) $taxonomy_key;
            $normalized_key  = $this->norm_attr_key( $taxonomy_key );
            $normalized_name = $this->norm_attr_key( $name );

            if ( 'boja' === $normalized_key || 'boja' === $normalized_name || false !== strpos( $normalized_key, 'boja' ) ) {
                $keys['boja'] = (string) $taxonomy_key;
            }
            if ( 'velicina' === $normalized_key || 'velicina' === $normalized_name || false !== strpos( $normalized_key, 'velicina' ) ) {
                $keys['velicina'] = (string) $taxonomy_key;
            }
        }

        return $keys;
    }

    private function norm_attr_key( $key ) {
        $key = str_replace( 'pa_', '', (string) $key );
        if ( function_exists( 'remove_accents' ) ) {
            $key = remove_accents( $key );
        }
        $key = mb_strtolower( $key );
        $key = preg_replace( '/[^a-z0-9]+/', '', $key );
        return (string) $key;
    }

    public function extract_var_attrs( $variation ) {
        $color = '';
        $size  = '';
        foreach ( (array) $variation->get_attributes() as $key => $value ) {
            $normalized = $this->norm_attr_key( $key );
            if ( 'boja' === $normalized ) {
                $color = (string) $value;
            }
            if ( 'velicina' === $normalized ) {
                $size = (string) $value;
            }
        }

        return array(
            'color' => $color,
            'size'  => $size,
        );
    }

    private function calc_new_price( $old_price, $fixed, $percent, $amount ) {
        if ( null !== $fixed && '' !== (string) $fixed ) {
            return floatval( $fixed );
        }
        if ( null !== $percent ) {
            return max( 0, floatval( $old_price ) * ( 1 + ( floatval( $percent ) / 100 ) ) );
        }
        if ( null !== $amount ) {
            return max( 0, floatval( $old_price ) + floatval( $amount ) );
        }

        return floatval( $old_price );
    }

    private function sync_parent( $parent_id ) {
        $parent_id = intval( $parent_id );
        if ( $parent_id <= 0 ) {
            return;
        }

        if ( class_exists( 'WC_Product_Variable' ) && method_exists( 'WC_Product_Variable', 'sync' ) ) {
            WC_Product_Variable::sync( $parent_id );
        }

        // Mandatory backstop: after any variation change, force the parent
        // taxonomy attributes back in line with the real active variations
        // (self-guards to variable products, only writes on a real mismatch).
        $this->reconcile_parent_attributes_from_variations( $parent_id );

        $this->sync_simple_product( $parent_id );
    }

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

        if ( function_exists( 'wc_delete_product_transients' ) ) {
            wc_delete_product_transients( $product_id );
        }

        $this->clear_stale_product_noindex( $product_id );
        $this->reindex_product( $product_id );
    }

    private function clear_stale_product_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_effective_product_stock_status( $product_id ) ) {
            return;
        }

        delete_post_meta( $product_id, '_za_noindex' );
    }

    private function get_effective_product_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 reindex_product( $product_id ) {
        $product_id = intval( $product_id );
        if ( $product_id <= 0 ) {
            return;
        }

        do_action( 'niwa_ai_index_upsert', $product_id, 'product', true );
    }
}
