<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\Commerce;

use Help4BuilderSuite\Support\RemoteUrlGuard;
use WP_Error;

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

final class ProductImporter
{
    private ProductRepository $products;
    /**
     * @var array<string, int>
     */
    private array $imageCache = [];

    public function __construct(ProductRepository $products)
    {
        $this->products = $products;
    }

    /**
     * @param array<int, array<string, mixed>> $items
     * @param array<string, mixed> $options
     * @return array<string, mixed>
     */
    public function import(array $items, array $options = []): array
    {
        $downloadImages = ! empty($options['download_images']);
        $replaceVariants = ! array_key_exists('replace_variants', $options)
            || rest_sanitize_boolean($options['replace_variants']);
        $created = 0;
        $updated = 0;
        $variants = 0;
        $errors = [];

        foreach ($items as $index => $item) {
            if (! is_array($item)) {
                $errors[] = 'Row ' . ($index + 1) . ' is not an object.';
                continue;
            }

            try {
                $result = $this->importItem($item, $downloadImages, $replaceVariants);
                if ($result['created']) {
                    $created++;
                } else {
                    $updated++;
                }
                $variants += (int) $result['variants'];
            } catch (\Throwable $e) {
                $errors[] = 'Row ' . ($index + 1) . ': ' . $e->getMessage();
            }
        }

        return [
            'total'    => count($items),
            'created'  => $created,
            'updated'  => $updated,
            'variants' => $variants,
            'errors'   => $errors,
        ];
    }

    /**
     * @param array<string, mixed> $item
     * @return array{created: bool, product_id: int, variants: int}
     */
    private function importItem(array $item, bool $downloadImages, bool $replaceVariants): array
    {
        $sku = $this->normalizeProductSku($item);
        $name = sanitize_text_field((string) ($item['name'] ?? 'Untitled Product'));
        $slug = sanitize_title((string) ($item['slug'] ?? $name));
        $status = sanitize_key((string) ($item['status'] ?? 'active'));
        $price = (float) ($item['price'] ?? 0);
        $currency = strtoupper(sanitize_text_field((string) ($item['currency'] ?? 'USD')));
        $stockQty = (int) ($item['stock_qty'] ?? 0);

        $featuredImage = $this->extractFeaturedImage($item);
        $galleryUrls = $this->extractGalleryUrls($item, $featuredImage);
        $variantsRaw = $this->extractVariants($item);
        $type = sanitize_key((string) ($item['type'] ?? (count($variantsRaw) > 0 ? 'variable' : 'simple')));
        $existing = $sku !== '' ? $this->products->getBySku($sku) : null;
        $existingPayload = is_array($existing)
            ? json_decode((string) ($existing['payload'] ?? ''), true)
            : [];
        if (! is_array($existingPayload)) {
            $existingPayload = [];
        }

        $featuredId = 0;
        $galleryIds = [];
        if ($downloadImages) {
            if ($featuredImage !== '') {
                $featuredId = $this->downloadImage($featuredImage, $name . ' Featured');
            }
            foreach ($galleryUrls as $galleryUrl) {
                $id = $this->downloadImage($galleryUrl, $name . ' Gallery');
                if ($id > 0) {
                    $galleryIds[] = $id;
                }
            }
        }

        $payload = array_merge(
            $existingPayload,
            [
                'type'                => $type === '' ? 'simple' : $type,
                'description'         => wp_kses_post((string) ($item['description'] ?? '')),
                'short_description'   => wp_kses_post((string) ($item['short_description'] ?? '')),
                'brand'               => sanitize_text_field((string) ($item['brand'] ?? '')),
                'categories'          => $this->normalizeTextArray($item['categories'] ?? []),
                'tags'                => $this->normalizeTextArray($item['tags'] ?? []),
                'featured_image_url'  => $featuredImage,
                'featured_image_id'   => $featuredId,
                'gallery_urls'        => $galleryUrls,
                'gallery_image_ids'   => array_values(array_unique($galleryIds)),
                'external_id'         => sanitize_text_field((string) ($item['external_id'] ?? ($item['id'] ?? ''))),
                'external_url'        => esc_url_raw((string) ($item['external_url'] ?? ($item['url'] ?? ''))),
                'shipping_profile'    => [
                    'weight' => max(0, (float) ($item['shipping_weight'] ?? ($item['weight'] ?? 0))),
                    'weight_unit' => $this->normalizeWeightUnit((string) ($item['shipping_weight_unit'] ?? ($item['weight_unit'] ?? 'lb'))),
                    'length' => max(0, (float) ($item['shipping_length'] ?? ($item['length'] ?? 0))),
                    'width' => max(0, (float) ($item['shipping_width'] ?? ($item['width'] ?? 0))),
                    'height' => max(0, (float) ($item['shipping_height'] ?? ($item['height'] ?? 0))),
                    'dimension_unit' => $this->normalizeDimensionUnit((string) ($item['shipping_dimension_unit'] ?? ($item['dimension_unit'] ?? 'in'))),
                ],
                'inventory' => $this->normalizeImportedInventory($item, $existingPayload),
                'pricing' => $this->normalizeImportedPricing($item, $existingPayload, $price),
                'merchandising' => $this->normalizeImportedMerchandising($item, $existingPayload),
                'tax' => $this->normalizeImportedTax($item, $existingPayload),
                'raw_source'          => $item,
            ]
        );

        $productData = [
            'sku'       => $sku,
            'name'      => $name,
            'slug'      => $slug,
            'status'    => $status,
            'price'     => $price,
            'currency'  => $currency,
            'stock_qty' => $stockQty,
            'payload'   => $payload,
        ];

        if (is_array($existing) && ! empty($existing['id'])) {
            $productId = (int) $existing['id'];
            $this->products->update($productId, $productData);
            $created = false;
        } else {
            $productId = $this->products->create($productData);
            $created = true;
        }

        $variantCount = 0;
        if (array_key_exists('variants', $item) || array_key_exists('variations', $item)) {
            $variantCount = $this->syncVariants(
                $productId,
                $variantsRaw,
                $downloadImages,
                $replaceVariants,
                $name,
                $price
            );
        }

        return [
            'created'   => $created,
            'product_id'=> $productId,
            'variants'  => $variantCount,
        ];
    }

    /**
     * @param array<string, mixed> $item
     * @param array<string, mixed> $existingPayload
     * @return array{status: string, class: string}
     */
    private function normalizeImportedTax(array $item, array $existingPayload): array
    {
        $existing = is_array($existingPayload['tax'] ?? null) ? $existingPayload['tax'] : [];
        $status = sanitize_key((string) ($item['tax_status'] ?? ($existing['status'] ?? 'taxable')));
        if (array_key_exists('taxable', $item)) {
            $status = rest_sanitize_boolean($item['taxable']) ? 'taxable' : 'none';
        }
        if (! in_array($status, ['taxable', 'none'], true)) {
            $status = 'taxable';
        }
        $class = sanitize_key((string) ($item['tax_class'] ?? ($existing['class'] ?? 'standard'))) ?: 'standard';

        return ['status' => $status, 'class' => $class];
    }

    /**
     * @param array<int, mixed> $variantsRaw
     */
    private function syncVariants(
        int $productId,
        array $variantsRaw,
        bool $downloadImages,
        bool $replaceVariants,
        string $productName,
        float $fallbackPrice
    ): int {
        if (! $variantsRaw) {
            if ($replaceVariants) {
                $this->products->deleteVariantsByProduct($productId);
            }
            return 0;
        }

        $existing = $this->products->listVariants($productId);
        $existingBySku = [];
        $existingByAttributes = [];
        foreach ($existing as $variant) {
            if (! is_array($variant)) {
                continue;
            }

            $variantId = absint($variant['id'] ?? 0);
            if ($variantId <= 0) {
                continue;
            }

            $existingSku = sanitize_text_field((string) ($variant['sku'] ?? ''));
            if ($existingSku !== '') {
                $existingBySku[$existingSku] = $variantId;
            }

            $attributes = json_decode((string) ($variant['attributes_json'] ?? ''), true);
            if (is_array($attributes)) {
                $attributesKey = $this->attributesFingerprint($attributes);
                if ($attributesKey !== '') {
                    $existingByAttributes[$attributesKey] = $variantId;
                }
            }
        }

        $keepVariantIds = [];
        foreach ($variantsRaw as $index => $variantRaw) {
            if (! is_array($variantRaw)) {
                continue;
            }

            $variant = $this->normalizeVariant($variantRaw, $fallbackPrice, $downloadImages, $productName, $index);
            $variantSku = sanitize_text_field((string) ($variant['sku'] ?? ''));
            $attributesKey = $this->attributesFingerprint(is_array($variant['attributes'] ?? null) ? $variant['attributes'] : []);
            $existingId = 0;

            if ($variantSku !== '' && isset($existingBySku[$variantSku])) {
                $existingId = (int) $existingBySku[$variantSku];
            } elseif ($attributesKey !== '' && isset($existingByAttributes[$attributesKey])) {
                $existingId = (int) $existingByAttributes[$attributesKey];
            }

            if ($existingId > 0) {
                $this->products->updateVariant($existingId, $variant);
                $keepVariantIds[] = $existingId;
                continue;
            }

            $newVariantId = $this->products->createVariant($productId, $variant);
            if ($newVariantId > 0) {
                $keepVariantIds[] = $newVariantId;
                if ($variantSku !== '') {
                    $existingBySku[$variantSku] = $newVariantId;
                }
                if ($attributesKey !== '') {
                    $existingByAttributes[$attributesKey] = $newVariantId;
                }
            }
        }

        if ($replaceVariants) {
            $this->products->deleteVariantsExcept($productId, $keepVariantIds);
        }

        return count($keepVariantIds);
    }

    /**
     * @param array<string, mixed> $variantRaw
     * @return array<string, mixed>
     */
    private function normalizeVariant(
        array $variantRaw,
        float $fallbackPrice,
        bool $downloadImages,
        string $productName,
        int $index
    ): array {
        $attributes = $this->normalizeVariantAttributes($variantRaw['attributes'] ?? ($variantRaw['options'] ?? []));
        $variantImageUrl = $this->extractFeaturedImage($variantRaw);
        $variantImageId = absint($variantRaw['image_id'] ?? 0);
        if ($downloadImages && $variantImageId <= 0 && $variantImageUrl !== '') {
            $variantImageId = $this->downloadImage($variantImageUrl, $productName . ' Variant');
        }

        return [
            'sku'       => sanitize_text_field((string) ($variantRaw['sku'] ?? '')),
            'attributes'=> $attributes,
            'price'     => (float) ($variantRaw['price'] ?? $fallbackPrice),
            'stock_qty' => (int) ($variantRaw['stock_qty'] ?? 0),
            'image_id'  => $variantImageId,
            'position'  => (int) ($variantRaw['position'] ?? $index),
        ];
    }

    /**
     * @param mixed $raw
     * @return array<string, string>
     */
    private function normalizeVariantAttributes($raw): array
    {
        if (! is_array($raw)) {
            return [];
        }

        $attributes = [];
        if (array_is_list($raw)) {
            foreach ($raw as $entry) {
                if (! is_array($entry)) {
                    continue;
                }
                $name = sanitize_title((string) ($entry['name'] ?? ($entry['slug'] ?? '')));
                if ($name === '') {
                    continue;
                }
                $value = sanitize_text_field((string) ($entry['option'] ?? ($entry['value'] ?? '')));
                if ($value !== '') {
                    $attributes[$name] = $value;
                }
            }
            return $attributes;
        }

        foreach ($raw as $key => $value) {
            if (is_array($value)) {
                continue;
            }
            $name = sanitize_title((string) $key);
            $text = sanitize_text_field((string) $value);
            if ($name !== '' && $text !== '') {
                $attributes[$name] = $text;
            }
        }

        return $attributes;
    }

    /**
     * @param array<string, mixed> $item
     * @return array<int, mixed>
     */
    private function extractVariants(array $item): array
    {
        $variants = $item['variants'] ?? ($item['variations'] ?? []);
        return is_array($variants) ? array_values($variants) : [];
    }

    /**
     * @param array<string, mixed> $item
     */
    private function extractFeaturedImage(array $item): string
    {
        $featured = $this->safeRemoteUrl((string) ($item['image_url'] ?? ''));
        if ($featured !== '') {
            return $featured;
        }

        $imageField = $item['image'] ?? null;
        if (is_string($imageField)) {
            return $this->safeRemoteUrl($imageField);
        }
        if (is_array($imageField)) {
            $fromObject = $this->safeRemoteUrl((string) ($imageField['url'] ?? ($imageField['src'] ?? ($imageField['image_url'] ?? ''))));
            if ($fromObject !== '') {
                return $fromObject;
            }
        }

        $candidates = $this->normalizeImageUrls(
            $item['image_urls']
            ?? ($item['images'] ?? ($item['gallery_urls'] ?? ($item['gallery'] ?? [])))
        );
        return $candidates[0] ?? '';
    }

    /**
     * @param array<string, mixed> $item
     * @return array<int, string>
     */
    private function extractGalleryUrls(array $item, string $featuredImage): array
    {
        $gallery = $this->normalizeImageUrls(
            $item['gallery_urls']
            ?? ($item['gallery'] ?? ($item['image_urls'] ?? ($item['images'] ?? [])))
        );

        if ($featuredImage !== '' && ! in_array($featuredImage, $gallery, true)) {
            array_unshift($gallery, $featuredImage);
        }

        return array_values(array_unique($gallery));
    }

    /**
     * @param mixed $raw
     * @return array<int, string>
     */
    private function normalizeTextArray($raw): array
    {
        if (is_string($raw) && $raw !== '') {
            $raw = array_map('trim', explode(',', $raw));
        }
        if (! is_array($raw)) {
            return [];
        }

        $values = [];
        foreach ($raw as $value) {
            if (is_array($value)) {
                $value = $value['name'] ?? $value['slug'] ?? '';
            }
            $text = sanitize_text_field((string) $value);
            if ($text !== '') {
                $values[] = $text;
            }
        }

        return array_values(array_unique($values));
    }

    /**
     * @param mixed $raw
     * @return array<int, string>
     */
    private function normalizeImageUrls($raw): array
    {
        if (is_string($raw) && $raw !== '') {
            $raw = array_map('trim', explode(',', $raw));
        }
        if (! is_array($raw)) {
            return [];
        }

        $urls = [];
        foreach ($raw as $value) {
            if (is_array($value)) {
                $value = $value['url'] ?? ($value['src'] ?? ($value['image_url'] ?? ''));
            }
            $url = RemoteUrlGuard::normalize((string) $value);
            if ($url !== '' && RemoteUrlGuard::isAllowed($url)) {
                $urls[] = $url;
            }
        }
        return array_values(array_unique($urls));
    }

    private function safeRemoteUrl(string $url): string
    {
        $url = RemoteUrlGuard::normalize($url);
        if ($url === '' || ! RemoteUrlGuard::isAllowed($url)) {
            return '';
        }
        return $url;
    }

    private function downloadImage(string $url, string $description): int
    {
        $url = RemoteUrlGuard::normalize($url);
        if ($url === '' || ! RemoteUrlGuard::isAllowed($url)) {
            return 0;
        }

        if (isset($this->imageCache[$url])) {
            return $this->imageCache[$url];
        }

        $existingAttachment = $this->findAttachmentBySourceUrl($url);
        if ($existingAttachment > 0) {
            $this->cacheSourceAttachmentUrl($url, $existingAttachment);
            return $existingAttachment;
        }

        $existingLocalAttachment = attachment_url_to_postid($url);
        if ($existingLocalAttachment > 0) {
            update_post_meta($existingLocalAttachment, '_h4bs_source_url', $url);
            $this->cacheSourceAttachmentUrl($url, $existingLocalAttachment);
            return $existingLocalAttachment;
        }

        require_once ABSPATH . 'wp-admin/includes/media.php';
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/image.php';

        $attachmentId = $this->sideloadImageFromLocalTemp($url, $description);
        if ($attachmentId <= 0) {
            return 0;
        }

        update_post_meta($attachmentId, '_h4bs_source_url', $url);
        $this->cacheSourceAttachmentUrl($url, $attachmentId);

        return $attachmentId;
    }

    private function sideloadImageFromLocalTemp(string $url, string $description): int
    {
        $upload = wp_upload_dir(null, false);
        $base = isset($upload['basedir']) ? (string) $upload['basedir'] : '';
        if ($base === '') {
            return 0;
        }

        $tempDir = trailingslashit($base) . 'h4bs-temp';
        if (! wp_mkdir_p($tempDir) || ! wp_is_writable($tempDir)) {
            return 0;
        }

        $tempName = wp_unique_filename($tempDir, 'h4bs-import-' . substr(hash('sha256', $url), 0, 16) . '.tmp');
        $tempFile = trailingslashit($tempDir) . $tempName;
        $response = wp_safe_remote_get(
            $url,
            [
                'timeout'             => 30,
                'redirection'         => 5,
                'stream'              => true,
                'filename'            => $tempFile,
                'limit_response_size' => 20 * MB_IN_BYTES,
            ]
        );

        if ($response instanceof WP_Error || wp_remote_retrieve_response_code($response) >= 400 || ! is_file($tempFile)) {
            wp_delete_file($tempFile);
            return 0;
        }

        $filename = $this->remoteImageFilename($url, (string) wp_remote_retrieve_header($response, 'content-type'));
        $attachmentId = media_handle_sideload(
            [
                'name'     => $filename,
                'tmp_name' => $tempFile,
            ],
            0,
            $description
        );

        if ($attachmentId instanceof WP_Error) {
            wp_delete_file($tempFile);
            return 0;
        }

        return absint($attachmentId);
    }

    private function remoteImageFilename(string $url, string $contentType): string
    {
        $path = (string) wp_parse_url($url, PHP_URL_PATH);
        $filename = sanitize_file_name(wp_basename($path));
        $extension = strtolower((string) pathinfo($filename, PATHINFO_EXTENSION));
        $allowedExtensions = ['avif', 'gif', 'jpeg', 'jpg', 'png', 'webp'];
        if ($filename !== '' && in_array($extension, $allowedExtensions, true)) {
            return $filename;
        }

        $type = strtolower(trim((string) strtok($contentType, ';')));
        $extensionsByType = [
            'image/avif' => 'avif',
            'image/gif'  => 'gif',
            'image/jpeg' => 'jpg',
            'image/png'  => 'png',
            'image/webp' => 'webp',
        ];
        $extension = $extensionsByType[$type] ?? 'jpg';

        return 'h4bs-import-' . substr(hash('sha256', $url), 0, 16) . '.' . $extension;
    }

    /**
     * @param array<string, mixed> $item
     */
    private function normalizeProductSku(array $item): string
    {
        $sku = sanitize_text_field((string) ($item['sku'] ?? ''));
        if ($sku !== '') {
            return substr($sku, 0, 100);
        }

        $external = sanitize_text_field((string) ($item['external_id'] ?? ($item['id'] ?? '')));
        if ($external !== '') {
            return substr('EXT-' . $external, 0, 100);
        }

        $slug = sanitize_title((string) ($item['slug'] ?? ($item['name'] ?? 'product')));
        $price = (string) ($item['price'] ?? '');
        $externalUrl = esc_url_raw((string) ($item['external_url'] ?? ($item['url'] ?? '')));
        $description = sanitize_text_field((string) ($item['description'] ?? ''));
        $hash = strtoupper(substr(md5($slug . '|' . $price . '|' . $externalUrl . '|' . $description), 0, 12));
        return substr('AUTO-' . $hash, 0, 100);
    }

    private function findAttachmentBySourceUrl(string $url): int
    {
        $cacheKey = 'source_url_' . md5($url);
        $cached = wp_cache_get($cacheKey, 'h4bs_product_importer');
        if ($cached !== false) {
            return absint($cached);
        }

        global $wpdb;
        $metaTable = $wpdb->postmeta;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Indexed attachment source URL lookup with object-cache guard above.
        $postId = $wpdb->get_var(
            $wpdb->prepare(
                'SELECT post_id FROM %i WHERE meta_key = %s AND meta_value = %s ORDER BY meta_id DESC LIMIT 1',
                $metaTable,
                '_h4bs_source_url',
                $url
            )
        ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

        $attachmentId = absint($postId);
        wp_cache_set($cacheKey, $attachmentId, 'h4bs_product_importer', HOUR_IN_SECONDS);

        return $attachmentId;
    }

    private function cacheSourceAttachmentUrl(string $url, int $attachmentId): void
    {
        $this->imageCache[$url] = $attachmentId;
        wp_cache_set('source_url_' . md5($url), $attachmentId, 'h4bs_product_importer', HOUR_IN_SECONDS);
    }

    /**
     * @param array<string, mixed> $attributes
     */
    private function attributesFingerprint(array $attributes): string
    {
        $normalized = [];
        foreach ($attributes as $key => $value) {
            if (is_array($value)) {
                continue;
            }
            $name = sanitize_title((string) $key);
            $text = sanitize_text_field((string) $value);
            if ($name !== '' && $text !== '') {
                $normalized[$name] = $text;
            }
        }

        if (! $normalized) {
            return '';
        }

        ksort($normalized);
        return (string) wp_json_encode($normalized);
    }

    /**
     * @param array<string, mixed> $item
     * @param array<string, mixed> $existingPayload
     * @return array<string, mixed>
     */
    private function normalizeImportedInventory(array $item, array $existingPayload): array
    {
        $source = is_array($item['inventory'] ?? null) ? $item['inventory'] : [];
        $existing = is_array($existingPayload['inventory'] ?? null) ? $existingPayload['inventory'] : [];
        $trackStock = $source['track_stock']
            ?? ($item['track_stock']
            ?? ($item['manage_stock']
            ?? ($existing['track_stock'] ?? false)));
        $policy = sanitize_key(
            (string) (
                $source['policy']
                ?? ($item['inventory_policy']
                ?? ($item['backorders'] ?? ($existing['policy'] ?? 'deny')))
            )
        );
        $policyAliases = [
            'no' => 'deny',
            'none' => 'deny',
            'notify' => 'backorder_notify',
            'yes' => 'backorder',
            'allow' => 'backorder',
        ];
        $policy = (string) ($policyAliases[$policy] ?? $policy);
        if (! in_array($policy, ['deny', 'backorder_notify', 'backorder', 'preorder'], true)) {
            $policy = 'deny';
        }

        $minimum = max(0.001, min(1000, (float) ($source['min_qty'] ?? ($item['min_qty'] ?? ($existing['min_qty'] ?? 1)))));
        $maximum = max(0.0, min(1000, (float) ($source['max_qty'] ?? ($item['max_qty'] ?? ($existing['max_qty'] ?? 0)))));
        if ($maximum > 0 && $maximum < $minimum) {
            $maximum = $minimum;
        }
        $quantityEnabled = ! array_key_exists('quantity_enabled', $source)
            && ! array_key_exists('quantity_enabled', $item)
            && ! array_key_exists('allow_quantity', $item)
            ? rest_sanitize_boolean($existing['quantity_enabled'] ?? true)
            : rest_sanitize_boolean(
                $source['quantity_enabled']
                ?? ($item['quantity_enabled']
                ?? ($item['allow_quantity'] ?? true))
            );
        $quantityStep = max(0.001, min(1000, (float) ($source['qty_step'] ?? ($item['qty_step'] ?? ($existing['qty_step'] ?? 1)))));
        if (! $quantityEnabled) {
            $minimum = 1.0;
            $maximum = 1.0;
            $quantityStep = 1.0;
        }

        return [
            'track_stock' => rest_sanitize_boolean($trackStock),
            'policy' => $policy,
            'low_stock_threshold' => max(0, (int) ($source['low_stock_threshold'] ?? ($item['low_stock_amount'] ?? ($existing['low_stock_threshold'] ?? 5)))),
            'sold_individually' => rest_sanitize_boolean($source['sold_individually'] ?? ($item['sold_individually'] ?? ($existing['sold_individually'] ?? false))),
            'quantity_enabled' => $quantityEnabled,
            'min_qty' => $minimum,
            'max_qty' => $maximum,
            'qty_step' => $quantityStep,
            'preorder_available_on' => $this->sanitizeImportDate((string) ($source['preorder_available_on'] ?? ($item['preorder_available_on'] ?? ($existing['preorder_available_on'] ?? '')))),
            'preorder_message' => sanitize_text_field((string) ($source['preorder_message'] ?? ($item['preorder_message'] ?? ($existing['preorder_message'] ?? '')))),
        ];
    }

    /**
     * @param array<string, mixed> $item
     * @param array<string, mixed> $existingPayload
     * @return array<string, float>
     */
    private function normalizeImportedPricing(array $item, array $existingPayload, float $currentPrice): array
    {
        $source = is_array($item['pricing'] ?? null) ? $item['pricing'] : [];
        $existing = is_array($existingPayload['pricing'] ?? null) ? $existingPayload['pricing'] : [];
        $compareAt = $source['compare_at_price']
            ?? ($item['compare_at_price']
            ?? ($item['regular_price'] ?? ($existing['compare_at_price'] ?? 0)));
        if ((float) $compareAt <= $currentPrice && isset($item['sale_price']) && (float) $item['sale_price'] < $currentPrice) {
            $compareAt = $currentPrice;
        }

        return [
            'compare_at_price' => max(0.0, (float) $compareAt),
            'cost' => max(0.0, (float) ($source['cost'] ?? ($item['unit_cost'] ?? ($item['cost_of_goods'] ?? ($existing['cost'] ?? 0))))),
        ];
    }

    /**
     * @param array<string, mixed> $item
     * @param array<string, mixed> $existingPayload
     * @return array<string, mixed>
     */
    private function normalizeImportedMerchandising(array $item, array $existingPayload): array
    {
        $source = is_array($item['merchandising'] ?? null) ? $item['merchandising'] : [];
        $existing = is_array($existingPayload['merchandising'] ?? null) ? $existingPayload['merchandising'] : [];

        return [
            'badge' => sanitize_text_field((string) ($source['badge'] ?? ($item['custom_badge'] ?? ($existing['badge'] ?? '')))),
            'purchase_note' => sanitize_textarea_field((string) ($source['purchase_note'] ?? ($item['purchase_note'] ?? ($existing['purchase_note'] ?? '')))),
            'related_product_ids' => $this->normalizeProductIds($source['related_product_ids'] ?? ($item['related_product_ids'] ?? ($existing['related_product_ids'] ?? []))),
            'upsell_product_ids' => $this->normalizeProductIds($source['upsell_product_ids'] ?? ($item['upsell_product_ids'] ?? ($existing['upsell_product_ids'] ?? []))),
            'cross_sell_product_ids' => $this->normalizeProductIds($source['cross_sell_product_ids'] ?? ($item['cross_sell_product_ids'] ?? ($existing['cross_sell_product_ids'] ?? []))),
        ];
    }

    /**
     * @param mixed $value
     * @return array<int, int>
     */
    private function normalizeProductIds($value): array
    {
        if (is_string($value)) {
            $value = preg_split('/[\s,]+/', $value) ?: [];
        }
        if (! is_array($value)) {
            return [];
        }

        return array_values(
            array_unique(
                array_filter(
                    array_map('absint', $value),
                    static fn(int $id): bool => $id > 0
                )
            )
        );
    }

    private function sanitizeImportDate(string $date): string
    {
        $date = sanitize_text_field($date);
        if ($date === '' || preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) !== 1) {
            return '';
        }
        [$year, $month, $day] = array_map('intval', explode('-', $date));
        return checkdate($month, $day, $year) ? $date : '';
    }

    private function normalizeWeightUnit(string $unit): string
    {
        $unit = sanitize_key($unit);
        if (! in_array($unit, ['lb', 'kg', 'oz', 'g'], true)) {
            return 'lb';
        }
        return $unit;
    }

    private function normalizeDimensionUnit(string $unit): string
    {
        $unit = sanitize_key($unit);
        if (! in_array($unit, ['in', 'cm', 'm'], true)) {
            return 'in';
        }
        return $unit;
    }
}
