<?php

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

function iapc_get_language() {
    if (function_exists('pll_current_language')) {
        $pll_locale = pll_current_language('locale');

        if (!empty($pll_locale)) {
            return apply_filters('iapc_locale', $pll_locale);
        }
    }

    $locale = function_exists('determine_locale') ? determine_locale() : get_locale();

    /**
     * Locale activo del sitio (Ajustes → General, usuario, plugins de traducción).
     * Filtrable para personalizaciones puntuales sin acoplar a un plugin concreto.
     * Shortcode lang= uses switch_to_locale() temporarily (not a global force).
     */
    return apply_filters('iapc_locale', $locale);
}

function iapc_get_plugin_legal_links() {
    return array(
        array(
            'label' => __('Terms of Service', 'idesoftbcn-ai-product-customizer'),
            'url'   => IAPC_TERMS_OF_SERVICE_URL,
        ),
        array(
            'label' => __('Privacy Policy', 'idesoftbcn-ai-product-customizer'),
            'url'   => IAPC_PRIVACY_POLICY_URL,
        ),
        array(
            'label' => __('AI Content Policy', 'idesoftbcn-ai-product-customizer'),
            'url'   => IAPC_AI_CONTENT_POLICY_URL,
        ),
        array(
            'label' => __('License Agreement', 'idesoftbcn-ai-product-customizer'),
            'url'   => IAPC_LICENSE_AGREEMENT_URL,
        ),
        array(
            'label' => __('Acceptable Use Policy', 'idesoftbcn-ai-product-customizer'),
            'url'   => IAPC_ACCEPTABLE_USE_POLICY_URL,
        ),
    );
}

function iapc_get_admin_guide_image_lang() {
    $locale = function_exists('determine_locale') ? determine_locale() : get_locale();
    $lang   = strtolower(strtok($locale, '_'));

    if (in_array($lang, array('en', 'es'), true)) {
        return $lang;
    }

    return 'en';
}

function iapc_get_admin_guide_image_url($filename) {
    $filename = basename((string) $filename);
    $base_dir = IAPC_PLUGIN_DIR . 'assets/admin/images/';
    $base_url = IAPC_PLUGIN_URL . 'assets/admin/images/';
    $lang     = iapc_get_admin_guide_image_lang();

    $candidates = array($lang);

    if ($lang === 'en') {
        $candidates[] = 'es';
    } else {
        $candidates[] = 'en';
    }

    foreach ($candidates as $candidate) {
        $relative = $candidate . '/' . $filename;
        $path     = $base_dir . $relative;

        if (file_exists($path)) {
            return $base_url . $relative;
        }
    }

    $legacy = $base_dir . $filename;

    if (file_exists($legacy)) {
        return $base_url . $filename;
    }

    return $base_url . $lang . '/' . $filename;
}

function iapc_get_js_i18n() {
    return array(
        'chooseGuidedOption' => __('Choose at least one style, mood, color or element.', 'idesoftbcn-ai-product-customizer'),
        'generateImageFirst' => __('Generate an image first.', 'idesoftbcn-ai-product-customizer'),
        'aiImageRemoved' => __('AI image removed from the design.', 'idesoftbcn-ai-product-customizer'),
        /* translators: %1$s: file name, %2$s: maximum allowed size in megabytes */
        'fileTooLarge' => __('%1$s is too large. Maximum allowed: %2$s MB.', 'idesoftbcn-ai-product-customizer'),
        'invalidFileType' => __('Invalid format. Upload JPG, PNG or WEBP.', 'idesoftbcn-ai-product-customizer'),
        'enableBrush' => __('Enable brush', 'idesoftbcn-ai-product-customizer'),
        'disableBrush' => __('Disable brush', 'idesoftbcn-ai-product-customizer'),
        'eraser' => __('Eraser', 'idesoftbcn-ai-product-customizer'),
        'disableEraser' => __('Disable eraser', 'idesoftbcn-ai-product-customizer'),
        'expandCanvas' => __('Full screen', 'idesoftbcn-ai-product-customizer'),
        'exitCanvasFs' => __('Exit full screen', 'idesoftbcn-ai-product-customizer'),
        'enterAiPrompt' => __('Enter a description to generate the image.', 'idesoftbcn-ai-product-customizer'),
        'generatingImage' => __('Generating image...', 'idesoftbcn-ai-product-customizer'),
        'applyingImage' => __('Placing image on the product...', 'idesoftbcn-ai-product-customizer'),
        'imageGenerated' => __('Image generated and added to the design.', 'idesoftbcn-ai-product-customizer'),
        'imageGenerateError' => __('Error generating the image. Try another description.', 'idesoftbcn-ai-product-customizer'),
        'no3dContainer' => __('3D container not found.', 'idesoftbcn-ai-product-customizer'),
        'noThreeJs' => __('Three.js or GLTFLoader was not loaded.', 'idesoftbcn-ai-product-customizer'),
        'no3dModel' => __('This product does not have a 3D GLB model.', 'idesoftbcn-ai-product-customizer'),
        'loading3d' => __('Loading 3D model...', 'idesoftbcn-ai-product-customizer'),
        'loaded3d' => __('3D model loaded successfully.', 'idesoftbcn-ai-product-customizer'),
        'loaded3dNoBody' => __('3D model loaded, but no body meshes are configured.', 'idesoftbcn-ai-product-customizer'),
        'error3d' => __('Error loading the 3D model.', 'idesoftbcn-ai-product-customizer'),
        'error3dAlert' => __('Could not load the 3D model.', 'idesoftbcn-ai-product-customizer'),
        'no3dParts' => __('No parts found in the 3D model.', 'idesoftbcn-ai-product-customizer'),
        'invalidNumber' => __('Invalid number.', 'idesoftbcn-ai-product-customizer'),
        /* translators: %s: selected 3D body mesh name */
        'bodySelected' => __('Body selected: %s', 'idesoftbcn-ai-product-customizer'),
        'view3d' => __('3D View', 'idesoftbcn-ai-product-customizer'),
        'view3dDisabled' => __('3D view is temporarily disabled.', 'idesoftbcn-ai-product-customizer'),
        'stop3d' => __('Stop 3D', 'idesoftbcn-ai-product-customizer'),
        'chooseSticker' => __('Choose an included sticker or upload your own first.', 'idesoftbcn-ai-product-customizer'),
        'confirmStickerRights' => __('You must confirm that you own the rights or license to use this sticker.', 'idesoftbcn-ai-product-customizer'),
        'stickerLoading' => __('The sticker is still loading. Wait a moment and try again.', 'idesoftbcn-ai-product-customizer'),
        'chooseGraphic' => __('Choose a graphic element first.', 'idesoftbcn-ai-product-customizer'),
        'graphicLoading' => __('The element is still loading. Wait a moment and try again.', 'idesoftbcn-ai-product-customizer'),
        'connectionError' => __('Connection error.', 'idesoftbcn-ai-product-customizer'),
        'aiThumbUse' => __('Use', 'idesoftbcn-ai-product-customizer'),
        'theImage' => __('The image', 'idesoftbcn-ai-product-customizer'),
        'theSticker' => __('The sticker', 'idesoftbcn-ai-product-customizer'),
        'closeToast' => __('Close', 'idesoftbcn-ai-product-customizer'),
        'regenerate' => __('Regenerate', 'idesoftbcn-ai-product-customizer'),
        'generateImageWithAi' => __('Generate image', 'idesoftbcn-ai-product-customizer'),
        'aiLimitReached' => __('Limit reached', 'idesoftbcn-ai-product-customizer'),
        /* translators: 1: maximum AI images allowed, 2: remaining AI images */
        'aiLimitText' => __('You can generate up to %1$d AI images. %2$d remaining.', 'idesoftbcn-ai-product-customizer'),
        'aiImageLoadError' => __('Could not load the AI image into the design.', 'idesoftbcn-ai-product-customizer'),
        'enterTextFirst' => __('Enter text first.', 'idesoftbcn-ai-product-customizer'),
        'aiImageAlt' => __('AI image', 'idesoftbcn-ai-product-customizer'),
        'imageSelected' => __('Image selected.', 'idesoftbcn-ai-product-customizer'),
        'selectImageFirst' => __('Select an image first.', 'idesoftbcn-ai-product-customizer'),
        'imageAdded' => __('Image added to the design.', 'idesoftbcn-ai-product-customizer'),
        'selectBodyMesh' => __('Select the number of the part that will be the body:', 'idesoftbcn-ai-product-customizer'),
        'missingProductImage' => __('This view is not available for customization.', 'idesoftbcn-ai-product-customizer'),
        /* translators: %s: product view or side label */
        'missingProductImageForSide' => __('Customization is not available for %s.', 'idesoftbcn-ai-product-customizer'),
        'noProductImages' => __('This product cannot be customized.', 'idesoftbcn-ai-product-customizer'),
        'sideNotCustomizable' => __('This view is not available for customization.', 'idesoftbcn-ai-product-customizer'),
        'additionalSides' => __('Additional sides', 'idesoftbcn-ai-product-customizer'),
    );
}

// Canvas editor shortcode output.
function iapc_shortcode_canvas() {
    if (!iapc_resolve_designer_product()) {
        return '';
    }

    ob_start();
    ?>
    <div id="iapc_canvas_wrap">
    <canvas id="iapc_mug_canvas"></canvas>
    <div id="iapc_3d_wrap" style="display:none;"></div>
    <div id="iapc_canvas_loading" class="iapc-canvas-loading iapc-hidden" aria-live="polite" aria-busy="false">
        <div class="iapc-canvas-loading__card">
            <span class="iapc-canvas-loading__spinner" aria-hidden="true"></span>
            <span class="iapc-canvas-loading__text"><?php esc_html_e('Generating image...', 'idesoftbcn-ai-product-customizer'); ?></span>
        </div>
    </div>
    <button type="button" class="iapc-canvas-fs-toggle" aria-label="<?php esc_attr_e('Full screen', 'idesoftbcn-ai-product-customizer'); ?>" title="<?php esc_attr_e('Full screen', 'idesoftbcn-ai-product-customizer'); ?>">
        <svg class="iapc-canvas-fs-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
            <path d="M8 3H4a1 1 0 0 0-1 1v4M16 3h4a1 1 0 0 1 1 1v4M8 21H4a1 1 0 0 1-1-1v-4M16 21h4a1 1 0 0 0 1-1v-4" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
        </svg>
    </button>
    <button type="button" class="iapc-canvas-fs-exit iapc-hidden" aria-label="<?php esc_attr_e('Exit full screen', 'idesoftbcn-ai-product-customizer'); ?>" title="<?php esc_attr_e('Exit full screen', 'idesoftbcn-ai-product-customizer'); ?>">
        <svg class="iapc-canvas-fs-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
            <path d="M9 3v3a1 1 0 0 1-1 1H5M15 3v3a1 1 0 0 0 1 1h3M9 21v-3a1 1 0 0 0-1-1H5M15 21v-3a1 1 0 0 1 1-1h3" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
        </svg>
    </button>
</div>
    <?php
    return ob_get_clean();
}

function iapc_add_admin_menu() {
    if (!class_exists('WooCommerce')) {
        return;
    }

    add_submenu_page(
        'woocommerce',
        __('Idesoftbcn AI Product Customizer', 'idesoftbcn-ai-product-customizer'),
        __('Idesoftbcn AI Product Customizer', 'idesoftbcn-ai-product-customizer'),
        'manage_woocommerce',
        'idesoftbcn-ai-product-customizer',
        'iapc_admin_page'
    );
}


/* Renders the plugin admin guide page under WooCommerce. */
function iapc_admin_page() {
    require IAPC_ADMIN_DIR . 'views/admin-guide.php';
}
/** Enqueues styles for the admin guide page. */
function iapc_admin_page_styles($hook) {

    if ($hook !== 'woocommerce_page_idesoftbcn-ai-product-customizer') {
        return;
    }

    wp_enqueue_style(
        'iapc-admin-guide',
        IAPC_ADMIN_URL . 'css/admin-guide.css',
        array(),
        IAPC_VERSION
    );
}

function iapc_is_product_edit_screen($hook) {
    global $post;

    return in_array($hook, array('post.php', 'post-new.php'), true)
        && $post instanceof WP_Post
        && $post->post_type === 'product';
}

function iapc_should_load_cart_assets() {
    if (is_admin() || !class_exists('WooCommerce')) {
        return false;
    }

    return function_exists('is_cart') && (is_cart() || is_checkout());
}

function iapc_should_show_admin_plugin_notice() {
    if (!is_admin()) {
        return false;
    }

    if (!current_user_can('manage_options') && !current_user_can('manage_woocommerce')) {
        return false;
    }

    $screen = function_exists('get_current_screen') ? get_current_screen() : null;

    if (!$screen) {
        return false;
    }

    // Limit notices to plugin-related screens only (Guideline 11).
    if ($screen->id === 'woocommerce_page_idesoftbcn-ai-product-customizer') {
        return true;
    }

    if ($screen->post_type === 'product' && in_array($screen->base, array('post', 'edit'), true)) {
        return true;
    }

    return false;
}

function iapc_should_enqueue_license_notice_styles($hook = '') {
    if (!iapc_should_show_admin_plugin_notice()) {
        return false;
    }

    if (iapc_get_ai_license_state() !== 'active') {
        return true;
    }

    return iapc_is_product_edit_screen($hook);
}

function iapc_disable_woocommerce_zoom() {
    if (!iapc_should_load_designer_assets()) {
        return;
    }

    remove_theme_support('wc-product-gallery-zoom');
}

function iapc_set_forced_designer_product_id($product_id) {
    if ($product_id) {
        $GLOBALS['iapc_forced_designer_product_id'] = absint($product_id);
        return;
    }

    unset($GLOBALS['iapc_forced_designer_product_id']);
}

function iapc_get_forced_designer_product_id() {
    if (empty($GLOBALS['iapc_forced_designer_product_id'])) {
        return 0;
    }

    return absint($GLOBALS['iapc_forced_designer_product_id']);
}

function iapc_normalize_designer_locale($value) {
    if ($value === null || $value === false || $value === '') {
        return '';
    }

    $value = html_entity_decode((string) $value, ENT_QUOTES, 'UTF-8');
    $value = trim($value, " \t\n\r\0\x0B\"'`´“”‘’");
    $value = strtolower(str_replace('-', '_', $value));

    if (preg_match('/^([a-z]{2})_([a-z]{2})$/', $value, $matches)) {
        return $matches[1] . '_' . strtoupper($matches[2]);
    }

    $map = array(
        'es' => 'es_ES',
        'en' => 'en_US',
    );

    if (isset($map[$value])) {
        return $map[$value];
    }

    return '';
}

function iapc_parse_shortcode_lang_from_atts($atts) {
    if (!is_array($atts)) {
        $atts = array();
    }

    $defaults = array(
        'lang'     => '',
        'locale'   => '',
        'language' => '',
    );

    $atts = shortcode_atts($defaults, $atts, 'iapc_designer');

    foreach (array('lang', 'locale', 'language') as $key) {
        $locale = iapc_normalize_designer_locale($atts[$key]);

        if ($locale !== '') {
            return $locale;
        }
    }

    return '';
}

function iapc_extract_designer_locale_from_text($text) {
    if (!is_string($text) || $text === '') {
        return '';
    }

    if (preg_match('/\[iapc_designer[^\]]*?(?:lang|locale|language)\s*=\s*["\']?([^"\'\]\s]+)["\']?/i', $text, $matches)) {
        return iapc_normalize_designer_locale($matches[1]);
    }

    return '';
}

function iapc_get_designer_shortcode_post_content($post = null) {
    $post = get_post($post);

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

    $content = (string) $post->post_content;

    if (!empty($post->post_excerpt)) {
        $content .= "\n" . (string) $post->post_excerpt;
    }

    if (get_post_meta($post->ID, '_elementor_edit_mode', true)) {
        $elementor_data = get_post_meta($post->ID, '_elementor_data', true);

        if (is_string($elementor_data) && $elementor_data !== '') {
            $content .= "\n" . $elementor_data;
        }
    }

    return $content;
}

function iapc_is_product_page_integration($product_id = 0) {
    $product_id = absint($product_id);

    if ($product_id <= 0) {
        $product_id = get_queried_object_id();
    }

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

    return iapc_is_customizer_enabled($product_id)
        && iapc_get_integration_mode($product_id) === 'product_page';
}

function iapc_setup_product_page_integration() {
    if (!function_exists('is_product') || !is_product()) {
        return;
    }

    if (!iapc_is_product_page_integration()) {
        return;
    }

    remove_action('woocommerce_before_single_product_summary', 'woocommerce_show_product_images', 20);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_title', 5);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_rating', 10);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_price', 10);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_excerpt', 20);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_meta', 40);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_sharing', 50);
    remove_action('woocommerce_after_single_product_summary', 'woocommerce_output_product_data_tabs', 10);
    remove_action('woocommerce_after_single_product_summary', 'woocommerce_upsell_display', 15);
    remove_action('woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20);

    add_action('woocommerce_before_single_product', 'iapc_render_product_page_designer_wrap', 5);

    add_filter('render_block', 'iapc_filter_product_page_blocks', 10, 2);
    add_filter('body_class', 'iapc_add_product_page_integration_body_class');
    add_filter('the_content', 'iapc_strip_designer_shortcode_from_product_content', 1);
}

function iapc_render_product_page_designer_wrap() {
    static $rendered = false;

    if ($rendered) {
        return;
    }

    $rendered = true;

    echo '<div class="iapc-product-page-designer-root alignwide">';
    iapc_render_product_page_designer();
    echo '</div>';
}

function iapc_filter_product_page_blocks($block_content, $block) {
    if (!iapc_is_product_page_integration() || !is_product()) {
        return $block_content;
    }

    $hidden_blocks = array(
        'core/columns',
        'woocommerce/product-image-gallery',
        'core/post-title',
        'woocommerce/product-price',
        'woocommerce/add-to-cart-form',
        'core/post-excerpt',
        'woocommerce/product-meta',
        'woocommerce/product-details',
        'woocommerce/product-collection',
    );

    if (!empty($block['blockName']) && in_array($block['blockName'], $hidden_blocks, true)) {
        return '';
    }

    if (!empty($block['blockName']) && $block['blockName'] === 'woocommerce/store-notices') {
        ob_start();
        iapc_render_product_page_designer_wrap();
        $designer_html = ob_get_clean();

        if ($designer_html !== '') {
            return $block_content . iapc_kses_designer_html($designer_html);
        }
    }

    return $block_content;
}

function iapc_strip_designer_shortcode_from_product_content($content) {
    if (!function_exists('is_product') || !is_product() || !in_the_loop()) {
        return $content;
    }

    if (!iapc_is_product_page_integration(get_the_ID())) {
        return $content;
    }

    if (strpos($content, '[iapc_designer') === false) {
        return $content;
    }

    return (string) preg_replace('/\[iapc_designer[^\]]*\]/', '', $content);
}

function iapc_add_product_page_integration_body_class($classes) {
    $classes[] = 'iapc-product-page-integration';
    return $classes;
}

function iapc_render_product_page_designer() {
    $product = wc_get_product(get_queried_object_id());

    if (!$product instanceof WC_Product) {
        return;
    }

    // Already sanitized with wp_kses() inside iapc_kses_designer_html().
    echo iapc_kses_designer_html(iapc_get_designer_markup($product)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}

function iapc_hide_default_product_add_to_cart() {
    if (!function_exists('is_product') || !is_product()) {
        return;
    }

    if (iapc_is_product_page_integration()) {
        return;
    }

    $product_id = get_queried_object_id();

    if (!$product_id || !iapc_is_customizer_enabled($product_id)) {
        return;
    }

    if (iapc_get_integration_mode($product_id) !== 'shortcode') {
        return;
    }

    if (!iapc_post_has_designer_shortcode($product_id)) {
        return;
    }

    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30);
}

function iapc_post_has_designer_shortcode($post = null) {
    $content = iapc_get_designer_shortcode_post_content($post);

    return $content !== '' && strpos($content, '[iapc_designer') !== false;
}

function iapc_parse_designer_shortcode_product_id($content) {
    if ($content === '' || strpos($content, '[iapc_designer') === false) {
        return null;
    }

    if (!preg_match_all('/\[iapc_designer([^\]]*)\]/', $content, $matches)) {
        return null;
    }

    $has_shortcode = false;

    foreach ($matches[1] as $atts_string) {
        $has_shortcode = true;
        $atts = shortcode_parse_atts($atts_string);

        if (!empty($atts['product_id'])) {
            return absint($atts['product_id']);
        }
    }

    return $has_shortcode ? 0 : null;
}

function iapc_find_designer_shortcode_product_id_in_post($post = null) {
    return iapc_parse_designer_shortcode_product_id(iapc_get_designer_shortcode_post_content($post));
}

function iapc_parse_designer_shortcode_locale($content) {
    if ($content === '' || strpos($content, '[iapc_designer') === false) {
        return null;
    }

    if (!preg_match_all('/\[iapc_designer([^\]]*)\]/', $content, $matches)) {
        return null;
    }

    $has_shortcode = false;

    foreach ($matches[1] as $atts_string) {
        $has_shortcode = true;

        $regex_locale = iapc_extract_designer_locale_from_text('[iapc_designer' . $atts_string . ']');
        if ($regex_locale !== '') {
            return $regex_locale;
        }

        $atts = shortcode_parse_atts($atts_string);
        $locale = iapc_parse_shortcode_lang_from_atts($atts);

        if ($locale !== '') {
            return $locale;
        }
    }

    return $has_shortcode ? '' : null;
}

function iapc_find_designer_shortcode_locale_in_post($post = null) {
    return iapc_parse_designer_shortcode_locale(iapc_get_designer_shortcode_post_content($post));
}

function iapc_is_customizer_enabled($product_id) {
    return get_post_meta(absint($product_id), '_iapc_customizer_enabled', true) === 'yes';
}

function iapc_product_has_base_price($product) {
    if (!$product instanceof WC_Product) {
        return false;
    }

    $regular = $product->get_regular_price('edit');
    if ($regular !== '' && $regular !== null) {
        return true;
    }

    $sale = $product->get_sale_price('edit');
    return $sale !== '' && $sale !== null;
}

function iapc_product_has_customization_images($product_id) {
    $product_id = absint($product_id);

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

    $product_type = get_post_meta($product_id, '_iapc_product_type', true);

    if (!$product_type) {
        $product_type = 'mug';
    }

    $views = iapc_get_views_for_product($product_id);

    foreach ($views as $view) {
        if (!empty($view['image'])) {
            return true;
        }
    }

    return false;
}

function iapc_product_is_customizable($product_id) {
    $product_id = absint($product_id);

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

    $product = wc_get_product($product_id);

    if (!$product) {
        return false;
    }

    return iapc_product_has_customization_images($product_id) && iapc_product_has_base_price($product);
}

/**
 * Missing store-setup items that block customization (price, product images, etc.).
 *
 * @param int $product_id Product ID.
 * @return string[] Human-readable setup gaps (empty if ready).
 */
function iapc_get_product_customizer_setup_gaps($product_id) {
    $product_id = absint($product_id);
    $gaps       = array();

    if ($product_id <= 0) {
        return $gaps;
    }

    $product = wc_get_product($product_id);

    if ($product && !iapc_product_has_base_price($product)) {
        $gaps[] = __(
            'Configure the regular price in the WooCommerce product data.',
            'idesoftbcn-ai-product-customizer'
        );
    }

    if (!iapc_product_has_customization_images($product_id)) {
        $gaps[] = __(
            'Upload at least Image view 1 / Side A for this product.',
            'idesoftbcn-ai-product-customizer'
        );
    }

    return $gaps;
}

function iapc_allow_customizer_product_purchasable($purchasable, $product) {
    if (!$product instanceof WC_Product) {
        return $purchasable;
    }

    if (!iapc_is_customizer_enabled($product->get_id())) {
        return $purchasable;
    }

    if ($product->get_status() !== 'publish') {
        return $purchasable;
    }

    if (!iapc_product_is_customizable($product->get_id())) {
        return false;
    }

    return true;
}

function iapc_customizer_empty_price_as_zero($price, $product) {
    if ($price !== '' && $price !== null) {
        return $price;
    }

    if (!$product instanceof WC_Product) {
        return $price;
    }

    if (!iapc_is_customizer_enabled($product->get_id())) {
        return $price;
    }

    return 0;
}

function iapc_render_admin_product_config_warnings($product_id) {
    $product_id = absint($product_id);

    if ($product_id <= 0 || !iapc_is_customizer_enabled($product_id)) {
        return;
    }

    $warnings = iapc_get_product_customizer_setup_gaps($product_id);

    if (empty($warnings)) {
        return;
    }

    echo '<div class="iapc-required-setup-notice">';
    echo '<p class="iapc-required-setup-title"><strong>' . esc_html__('Required setup', 'idesoftbcn-ai-product-customizer') . '</strong></p>';

    foreach ($warnings as $warning) {
        echo '<p>' . esc_html($warning) . '</p>';
    }

    echo '</div>';
}

function iapc_get_integration_mode($product_id) {
    $mode = get_post_meta(absint($product_id), '_iapc_integration_mode', true);

    if ($mode === '') {
        return 'shortcode';
    }

    return in_array($mode, array('shortcode', 'product_page'), true) ? $mode : 'shortcode';
}

function iapc_should_load_designer_assets() {
    if (iapc_post_has_designer_shortcode()) {
        return true;
    }

    if (!function_exists('is_product') || !is_product()) {
        return false;
    }

    $product_id = get_queried_object_id();

    return $product_id && iapc_is_customizer_enabled($product_id);
}

function iapc_resolve_designer_product($explicit_product_id = 0) {
    $explicit_product_id = absint($explicit_product_id);

    if ($explicit_product_id > 0) {
        $iapc_product = wc_get_product($explicit_product_id);
        return $iapc_product instanceof WC_Product ? $iapc_product : false;
    }

    $forced_product_id = iapc_get_forced_designer_product_id();

    if ($forced_product_id > 0) {
        $iapc_product = wc_get_product($forced_product_id);
        return $iapc_product instanceof WC_Product ? $iapc_product : false;
    }

    global $product;

    if ($product instanceof WC_Product) {
        return $product;
    }

    if (function_exists('is_product') && is_product()) {
        $iapc_product_id = get_queried_object_id();

        if ($iapc_product_id) {
            $iapc_product = wc_get_product($iapc_product_id);
            return $iapc_product instanceof WC_Product ? $iapc_product : false;
        }
    }

    return false;
}

// Resolves the product used by the designer on the current request.
function iapc_get_current_product() {
    return iapc_resolve_designer_product();
}

function iapc_enqueue_assets() {
    if (!class_exists('WooCommerce')) {
        return;
    }

    if (!iapc_should_load_designer_assets()) {
        return;
    }

    $parsed_product_id = iapc_find_designer_shortcode_product_id_in_post();

    if ($parsed_product_id > 0) {
        iapc_set_forced_designer_product_id($parsed_product_id);
    }

    $parsed_locale = iapc_find_designer_shortcode_locale_in_post();

    if ((!function_exists('is_product') || !is_product()) && $parsed_product_id === 0) {
        return;
    }

    wp_enqueue_style(
        'iapc-designer-css',
        IAPC_PUBLIC_URL . 'css/designer.css',
        array(),
        IAPC_VERSION
    );

    $product = iapc_get_current_product();

    if (!$product) {
        return;
    }

    $product_id = $product->get_id();

    $product_type = get_post_meta($product_id, '_iapc_product_type', true);

    if (!$product_type) {
        $product_type = 'mug';
    }

    $designer_js_deps = array(
        'iapc-toast',
        'iapc-tabs',
        'iapc-cart-bridge',
    );

    wp_enqueue_script(
        'iapc-toast',
        IAPC_PUBLIC_URL . 'js/designer/iapc-toast.js',
        array(),
        IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/designer/iapc-toast.js'),
        true
    );

    wp_enqueue_script(
        'iapc-tabs',
        IAPC_PUBLIC_URL . 'js/designer/iapc-tabs.js',
        array('iapc-toast'),
        IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/designer/iapc-tabs.js'),
        true
    );

    wp_enqueue_script(
        'iapc-cart-bridge',
        IAPC_PUBLIC_URL . 'js/designer/iapc-cart-bridge.js',
        array('iapc-toast'),
        IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/designer/iapc-cart-bridge.js'),
        true
    );

    // Three.js (car products with GLB preview) — bundled r185 + GLTFLoader.
    if ($product_type === 'car') {
        $three_dir = IAPC_PUBLIC_DIR . 'js/vendor/three/';

        wp_enqueue_script(
            'iapc-three',
            IAPC_PUBLIC_URL . 'js/vendor/three/three.bundle.min.js',
            array(),
            IAPC_VERSION . '.' . filemtime($three_dir . 'three.bundle.min.js'),
            true
        );

        // Viewer depends on the Three.js global bundle.
        wp_enqueue_script(
            'iapc-viewer3d',
            IAPC_PUBLIC_URL . 'js/designer/iapc-viewer3d.js',
            array('iapc-toast', 'iapc-three'),
            IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/designer/iapc-viewer3d.js'),
            true
        );
    } else {
        wp_enqueue_script(
            'iapc-viewer3d',
            IAPC_PUBLIC_URL . 'js/designer/iapc-viewer3d.js',
            array('iapc-toast'),
            IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/designer/iapc-viewer3d.js'),
            true
        );
    }

    $designer_js_deps[] = 'iapc-viewer3d';

    wp_enqueue_script(
        'iapc-designer-js',
        IAPC_PUBLIC_URL . 'js/designer.js',
        $designer_js_deps,
        IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/designer.js'),
        true
    );

    // Scope shortcode lang= to JS strings only (restore before the rest of the request).
    $switched = false;
    if ($parsed_locale !== null && $parsed_locale !== '' && function_exists('switch_to_locale')) {
        $switched = switch_to_locale($parsed_locale);
    }

    $js_lang = iapc_get_language();
    $js_i18n = iapc_get_js_i18n();

    if ($switched && function_exists('restore_previous_locale')) {
        restore_previous_locale();
    }

    wp_localize_script(
        'iapc-designer-js',
        'IAPC_DATA',
        array(

            'ajax_url' => admin_url('admin-ajax.php'),
            'nonce'    => wp_create_nonce('iapc_nonce'),
            'lang'     => $js_lang,
            'i18n'     => $js_i18n,

            'productId'   => $product_id,
            'productName' => $product->get_name(),
            'productType' => $product_type,
            'hasBasePrice' => iapc_product_has_base_price($product),
            'pluginVersion' => IAPC_VERSION,

            'fees' => array(
                'text'    => (float) get_post_meta($product_id, '_iapc_fee_text', true),
                'image'   => (float) get_post_meta($product_id, '_iapc_fee_image', true),
                'sticker' => (float) get_post_meta($product_id, '_iapc_fee_sticker', true),
                'graphic' => (float) get_post_meta($product_id, '_iapc_fee_graphic', true),
                'ai'      => (float) get_post_meta($product_id, '_iapc_fee_ai', true),
            ),

            'aiRules' => get_post_meta($product_id, '_iapc_ai_rules', true),

            'aiRemaining' => (
                WC()->session && WC()->session->get('iapc_ai_remaining') !== null
            )
                ? intval(WC()->session->get('iapc_ai_remaining'))
                : IAPC_AI_MAX_IMAGES,

            'aiMax' => IAPC_AI_MAX_IMAGES,

            'aiPremiumActive' => iapc_is_ai_premium_active(),

            // 3D GLB model URL.
            'model3d' => esc_url(get_post_meta($product_id, '_iapc_car_3d_model', true)),
            'allowedBodyNames' => array(
                'Body',
                'CarBody',
                'Carroceria',
                'Carrocería',
                'body',
                'car_body',
            ),
            'bodyMeshNames' => json_decode(
                get_post_meta($product_id, '_iapc_car_body_meshes', true),
                true
            ) ?: array(),

            'frontImage' => iapc_get_product_image_for_product($product_id, $product_type, 'front'),
            'backImage'  => iapc_get_product_image_for_product($product_id, $product_type, 'back'),

            'maxFileSizeMB' => 3,

            'views' => iapc_get_views_for_product($product_id),

            'allowedImageTypes' => array(
                'image/png',
                'image/jpeg',
                'image/webp',
            ),

            'printAreas' => iapc_get_print_areas_for_product($product_id),

            'stickers' => array(
                'Flower'   => IAPC_PLUGIN_URL . 'assets/stickers/1.png',
                'Green'    => IAPC_PLUGIN_URL . 'assets/stickers/2.png',
                'Yellow'   => IAPC_PLUGIN_URL . 'assets/stickers/3.png',
                'Lilac'    => IAPC_PLUGIN_URL . 'assets/stickers/4.png',
                'Blue'     => IAPC_PLUGIN_URL . 'assets/stickers/5.png',
                'Sky Blue' => IAPC_PLUGIN_URL . 'assets/stickers/6.png',
                'Cactus'   => IAPC_PLUGIN_URL . 'assets/stickers/7.png',
                'Clover'   => IAPC_PLUGIN_URL . 'assets/stickers/8.png',
                'Hearts'   => IAPC_PLUGIN_URL . 'assets/stickers/9.png',
            ),

            'graphics' => array(
                'Arrow2'         => IAPC_PLUGIN_URL . 'assets/graphics/Arrow2.svg',
                'Arrow'          => IAPC_PLUGIN_URL . 'assets/graphics/Arrow.svg',
                'Circle'         => IAPC_PLUGIN_URL . 'assets/graphics/Circle.svg',
                'HorizontalLine' => IAPC_PLUGIN_URL . 'assets/graphics/HorizontalLine.svg',
                'VerticalLine'   => IAPC_PLUGIN_URL . 'assets/graphics/VerticalLine.svg',
                'Triangle'       => IAPC_PLUGIN_URL . 'assets/graphics/Triangle.svg',
                'Square'         => IAPC_PLUGIN_URL . 'assets/graphics/Square.svg',
                'Rectangle'      => IAPC_PLUGIN_URL . 'assets/graphics/Rectangle.svg',
            ),
        )
    );

    iapc_enqueue_form_enctype_script();
}

function iapc_enqueue_form_enctype_script() {
    wp_enqueue_script(
        'iapc-form-enctype',
        IAPC_PUBLIC_URL . 'js/iapc-form-enctype.js',
        array(),
        IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/iapc-form-enctype.js'),
        true
    );
}

// Hidden cart form fields for customized designs.

function iapc_set_rendering_designer_cart($active) {
    $GLOBALS['iapc_rendering_designer_cart'] = (bool) $active;
}

function iapc_is_rendering_designer_cart() {
    return !empty($GLOBALS['iapc_rendering_designer_cart']);
}

function iapc_hidden_cart_form_fields() {
    ?>
    <input type="hidden" name="iapc_current_side" value="front">
    <input type="hidden" name="iapc_stickers_data" value="">
    <input type="hidden" name="iapc_has_image" value="no">
    <input type="hidden" name="iapc_has_image_front" value="no">
    <input type="hidden" name="iapc_has_image_back" value="no">
    <input type="hidden" name="iapc_design_image" value="">
    <input type="hidden" name="iapc_design_image_front" value="">
    <input type="hidden" name="iapc_design_image_back" value="">
    <input type="hidden" name="iapc_text" value="">
    <input type="hidden" name="iapc_text_front" value="">
    <input type="hidden" name="iapc_text_back" value="">
    <input type="hidden" name="iapc_use_text" value="">
    <input type="hidden" name="iapc_use_sticker" value="">
    <input type="hidden" name="iapc_customer_image_data" value="">
    <input type="hidden" name="iapc_customer_image_front" value="">
    <input type="hidden" name="iapc_customer_image_back" value="">
    <input type="hidden" name="iapc_used_assets_data" value="">
    <input type="hidden" name="iapc_used_ai" value="no">
    <?php
}

function iapc_add_design_nonce_field() {
    if (!iapc_is_rendering_designer_cart()) {
        return;
    }

    wp_nonce_field('iapc_design_nonce_action', 'iapc_design_nonce');
    iapc_hidden_cart_form_fields();
}
function iapc_get_custom_stickers() {
    return array(
        'Flower'   => array('label' => __('Flower', 'idesoftbcn-ai-product-customizer')),
        'Green'    => array('label' => __('Green', 'idesoftbcn-ai-product-customizer')),
        'Yellow'   => array('label' => __('Yellow', 'idesoftbcn-ai-product-customizer')),
        'Lilac'    => array('label' => __('Lilac', 'idesoftbcn-ai-product-customizer')),
        'Blue'     => array('label' => __('Blue', 'idesoftbcn-ai-product-customizer')),
        'Sky Blue' => array('label' => __('Sky Blue', 'idesoftbcn-ai-product-customizer')),
        'Cactus'   => array('label' => __('Cactus', 'idesoftbcn-ai-product-customizer')),
        'Clover'   => array('label' => __('Clover', 'idesoftbcn-ai-product-customizer')),
        'Hearts'   => array('label' => __('Hearts', 'idesoftbcn-ai-product-customizer')),
    );
}

function iapc_get_sticker_url($key) {
    $stickers = array(
        'Flower'   => IAPC_PLUGIN_URL . 'assets/stickers/1.png',
        'Green'    => IAPC_PLUGIN_URL . 'assets/stickers/2.png',
        'Yellow'   => IAPC_PLUGIN_URL . 'assets/stickers/3.png',
        'Lilac'    => IAPC_PLUGIN_URL . 'assets/stickers/4.png',
        'Blue'     => IAPC_PLUGIN_URL . 'assets/stickers/5.png',
        'Sky Blue' => IAPC_PLUGIN_URL . 'assets/stickers/6.png',
        'Cactus'   => IAPC_PLUGIN_URL . 'assets/stickers/7.png',
        'Clover'   => IAPC_PLUGIN_URL . 'assets/stickers/8.png',
        'Hearts'   => IAPC_PLUGIN_URL . 'assets/stickers/9.png',
    );

    return isset($stickers[$key]) ? $stickers[$key] : '';
}
function iapc_clamp_int($value, $min, $max, $default) {
    if (!isset($value) || $value === '') {
        return $default;
    }

    if (is_string($value)) {
        $value = sanitize_text_field(wp_unslash($value));
    }

    $number = intval($value);

    return max($min, min($max, $number));
}

function iapc_sanitize_layer($value) {
    if (is_string($value)) {
        $value = sanitize_text_field(wp_unslash($value));
    }

    return $value === 'front' ? 'front' : 'behind';
}

function iapc_yes_no_flag($condition) {
    return $condition ? 'yes' : 'no';
}

function iapc_verify_design_nonce() {
    return isset($_POST['iapc_design_nonce'])
        && wp_verify_nonce(
            sanitize_text_field(wp_unslash($_POST['iapc_design_nonce'])),
            'iapc_design_nonce_action'
        );
}

function iapc_clamp_post_int($key, $min, $max, $default) {
    // phpcs:disable WordPress.Security.NonceVerification.Missing -- Only used after iapc_verify_design_nonce().

    if (!isset($_POST[$key]) || $_POST[$key] === '') {
        return $default;
    }

    $value = iapc_clamp_int(sanitize_text_field(wp_unslash($_POST[$key])), $min, $max, $default);

    // phpcs:enable WordPress.Security.NonceVerification.Missing

    return $value;
}

function iapc_get_post_layer($key, $default = 'behind') {
    // phpcs:disable WordPress.Security.NonceVerification.Missing -- Only used after iapc_verify_design_nonce().

    if (!isset($_POST[$key])) {
        return iapc_sanitize_layer($default);
    }

    $layer = iapc_sanitize_layer(sanitize_text_field(wp_unslash($_POST[$key])));

    // phpcs:enable WordPress.Security.NonceVerification.Missing

    return $layer;
}

function iapc_get_post_yes_flag($key) {
    // phpcs:disable WordPress.Security.NonceVerification.Missing -- Only used after iapc_verify_design_nonce().

    if (!isset($_POST[$key])) {
        return false;
    }

    $is_yes = sanitize_text_field(wp_unslash($_POST[$key])) === 'yes';

    // phpcs:enable WordPress.Security.NonceVerification.Missing

    return $is_yes;
}

/**
 * Whether the submitted design includes a user-uploaded sticker (not a bundled asset).
 *
 * @param string|null $raw_json Optional layers JSON; reads POST when omitted.
 * @return bool
 */
function iapc_design_has_custom_sticker_layer($raw_json = null) {
    if ($raw_json === null) {
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Called after nonce check in cart validation.

        if (!isset($_POST['iapc_stickers_data'])) {
            return iapc_get_post_yes_flag('iapc_has_custom_sticker');
        }

        $raw_json = wp_unslash($_POST['iapc_stickers_data']);

        // phpcs:enable WordPress.Security.NonceVerification.Missing
    }

    $layers = iapc_decode_design_layers_json($raw_json);

    foreach ($layers as $layer) {
        if (!empty($layer['type']) && $layer['type'] === 'sticker' && empty($layer['key'])) {
            return true;
        }
    }

    return iapc_get_post_yes_flag('iapc_has_custom_sticker');
}

function iapc_sanitize_design_layer($layer) {
    if (!is_array($layer)) {
        return null;
    }

    $type = isset($layer['type']) ? sanitize_key($layer['type']) : '';

    if (!in_array($type, array('sticker', 'graphic', 'text', 'image', 'paint'), true)) {
        return null;
    }

    $side = isset($layer['side']) ? sanitize_key($layer['side']) : 'front';

    if (!in_array($side, array('front', 'back', 'left', 'right', 'top'), true)) {
        $side = 'front';
    }

    return array(
        'type'     => $type,
        'side'     => $side,
        'key'      => isset($layer['key']) ? sanitize_text_field((string) $layer['key']) : '',
        'src'      => isset($layer['src']) ? esc_url_raw((string) $layer['src']) : '',
        'x'        => isset($layer['x']) ? floatval($layer['x']) : 0,
        'y'        => isset($layer['y']) ? floatval($layer['y']) : 0,
        'size'     => isset($layer['size']) ? floatval($layer['size']) : 0,
        'rotation' => isset($layer['rotation']) ? floatval($layer['rotation']) : 0,
        'layer'    => iapc_sanitize_layer($layer['layer'] ?? 'behind'),
    );
}

/**
 * Decodes a design-layers JSON string into a list of sanitized layer arrays.
 *
 * @param string $raw_json Raw JSON from POST or storage.
 * @param int    $max_layers Maximum layers to accept.
 * @return array<int, array<string, mixed>>
 */
function iapc_decode_design_layers_json($raw_json, $max_layers = 50) {
    if (!is_string($raw_json)) {
        return array();
    }

    $raw_json = trim($raw_json);

    if ($raw_json === '') {
        return array();
    }

    // Reject oversized payloads before decoding.
    if (strlen($raw_json) > 200000) {
        return array();
    }

    // Must be a JSON array, not an object/scalar.
    if ($raw_json[0] !== '[') {
        return array();
    }

    $decoded = json_decode($raw_json, true);

    if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
        return array();
    }

    // Root must be a list of layer objects (reject associative maps).
    if ($decoded !== array_values($decoded)) {
        return array();
    }

    $max_layers = max(1, absint($max_layers));
    $layers     = array();

    foreach ($decoded as $index => $layer) {
        if ($index >= $max_layers) {
            break;
        }

        if (!is_array($layer)) {
            continue;
        }

        $clean = iapc_sanitize_design_layer($layer);

        if ($clean !== null) {
            $layers[] = $clean;
        }
    }

    return $layers;
}

function iapc_get_post_design_layers($post_key) {
    // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Only used after iapc_verify_design_nonce(); JSON sanitized in iapc_decode_design_layers_json().

    if (!isset($_POST[$post_key]) || !is_string($_POST[$post_key]) || $_POST[$post_key] === '') {
        $raw_json = '';
    } else {
        $raw_json = wp_unslash($_POST[$post_key]);
    }

    // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

    if ($raw_json === '') {
        return array();
    }

    return iapc_decode_design_layers_json($raw_json);
}

function iapc_get_post_design_layers_json($post_key) {
    $layers = iapc_get_post_design_layers($post_key);

    return $layers ? wp_json_encode($layers) : '';
}

function iapc_save_png_from_post($post_key) {
    // phpcs:disable WordPress.Security.NonceVerification.Missing -- Only used after iapc_verify_design_nonce().

    if (!isset($_POST[$post_key]) || $_POST[$post_key] === '') {
        return '';
    }

    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 PNG validated (prefix, charset, magic bytes, getimagesize) in iapc_save_png_as_file().
    $raw_png = wp_unslash($_POST[$post_key]);

    // phpcs:enable WordPress.Security.NonceVerification.Missing

    return iapc_save_png_as_file($raw_png);
}

function iapc_get_post_side_text($side) {
    // phpcs:disable WordPress.Security.NonceVerification.Missing -- Only used after iapc_verify_design_nonce().

    $key = $side === 'back' ? 'iapc_text_back' : 'iapc_text_front';

    if (empty($_POST[$key])) {
        return '';
    }

    $text = mb_substr(sanitize_text_field(wp_unslash($_POST[$key])), 0, 80);

    // phpcs:enable WordPress.Security.NonceVerification.Missing

    return $text;
}

function iapc_post_side_has_image($side) {
    $key = $side === 'back' ? 'iapc_has_image_back' : 'iapc_has_image_front';

    return iapc_get_post_yes_flag($key);
}

function iapc_is_yes($value) {
    static $yes_values = null;

    if ($yes_values === null) {
        $yes_values = array('yes', 'Yes', __('Yes', 'idesoftbcn-ai-product-customizer'));
    }

    return in_array($value, $yes_values, true);
}

function iapc_format_yes_no($value) {
    return iapc_is_yes($value) ? __('Yes', 'idesoftbcn-ai-product-customizer') : __('No', 'idesoftbcn-ai-product-customizer');
}

function iapc_side_has_content($text, $has_image, $stickers_per_side = 0) {
    return $text !== '' || $has_image || $stickers_per_side > 0;
}

function iapc_count_stickers_per_side($stickers, $side) {
    if (!is_array($stickers)) {
        return 0;
    }

    $total = 0;

    foreach ($stickers as $sticker) {
        if (!is_array($sticker)) {
            continue;
        }

        $sticker_side = !empty($sticker['side']) ? $sticker['side'] : 'front';

        if ($sticker_side === $side) {
            $total++;
        }
    }

    return $total;
}

function iapc_render_design_link($url) {
    return wp_kses_post(
        '<a href="' . esc_url($url) . '" target="_blank" rel="noopener">' . esc_html__('View final PNG', 'idesoftbcn-ai-product-customizer') . '</a>'
    );
}


function iapc_get_product_description_html($product) {
    if (!$product instanceof WC_Product) {
        return '';
    }

    $description = $product->get_description();

    if ($description === '') {
        $description = $product->get_short_description();
    }

    if ($description === '') {
        return '';
    }

    // Core WordPress content formatting (shortcodes, embeds, wpautop).
    // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
    return apply_filters('the_content', $description);
}

// Side switcher container (buttons are built dynamically in designer.js).
function iapc_shortcode_sides() {
    ob_start();
    ?>
<div class="iapc-sides"></div>
    <?php
    return ob_get_clean();
}


// Preview, clear and reset action buttons.
function iapc_shortcode_actions() {

    ob_start();
    ?>
    

    <section class="iapc-card iapc-card-actions">

        <div class="iapc-actions">

<button type="button" id="iapc_clear_text" class="iapc-btn">
    <?php esc_html_e('Clear text', 'idesoftbcn-ai-product-customizer'); ?>
</button>

<button type="button" id="iapc_clear_image" class="iapc-btn">
    <?php esc_html_e('Clear image', 'idesoftbcn-ai-product-customizer'); ?>
</button>

<button type="button" id="iapc_clear_sticker" class="iapc-btn">
    <?php esc_html_e('Clear stickers/elements', 'idesoftbcn-ai-product-customizer'); ?>
</button>

<button type="button" id="iapc_reset_design" class="iapc-btn">
    <?php esc_html_e('Reset design', 'idesoftbcn-ai-product-customizer'); ?>
</button>
        </div>

    </section>

    <?php
    return ob_get_clean();
}

// Renders the full designer layout HTML for a product.
function iapc_get_designer_markup($product) {
    if (!$product instanceof WC_Product) {
        return '';
    }

    if (!iapc_is_customizer_enabled($product->get_id())) {
        return '';
    }

    $iapc_previous_product = isset($GLOBALS['product']) ? $GLOBALS['product'] : null;
    // WooCommerce templates expect the global $product object.
    // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
    $GLOBALS['product'] = $product;

    $product_name = $product->get_name();
    $ai_premium_active = iapc_is_ai_premium_active();

    ob_start();
    iapc_load_frontend_template('designer-wrap.php', compact('product', 'product_name', 'ai_premium_active'));
    $output = ob_get_clean();

    if ($iapc_previous_product instanceof WC_Product) {
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Restoring WooCommerce global $product.
        $GLOBALS['product'] = $iapc_previous_product;
    } else {
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Clearing WooCommerce global $product.
        unset($GLOBALS['product']);
    }

    return $output;
}

// Main designer layout shortcode.
function iapc_shortcode_designer($atts = array()) {
    if (!class_exists('WooCommerce')) {
        return wp_kses_post(
            '<p class="iapc-designer-notice">' . esc_html__(
                'WooCommerce is required for the product customizer.',
                'idesoftbcn-ai-product-customizer'
            ) . '</p>'
        );
    }

    $atts = shortcode_atts(
        array(
            'product_id' => 0,
            'lang'       => '',
            'locale'     => '',
            'language'   => '',
        ),
        $atts,
        'iapc_designer'
    );

    $locale   = iapc_parse_shortcode_lang_from_atts($atts);
    $switched = false;

    if ($locale !== '' && function_exists('switch_to_locale')) {
        $switched = switch_to_locale($locale);
    }

    $explicit_product_id = absint($atts['product_id']);

    try {
        if (
            $explicit_product_id <= 0
            && function_exists('is_product')
            && is_product()
        ) {
            $current_product_id = get_queried_object_id();

            if ($current_product_id && iapc_is_product_page_integration($current_product_id)) {
                return '';
            }
        }

        if ($explicit_product_id > 0) {
            iapc_set_forced_designer_product_id($explicit_product_id);
        } elseif (!function_exists('is_product') || !is_product()) {
            if (is_cart() || is_checkout() || is_admin()) {
                return '';
            }

            return wp_kses_post(
                '<p class="iapc-designer-notice">' . esc_html__(
                    'A valid WooCommerce product is required. Use [iapc_designer product_id="123"] or place this shortcode on a product page.',
                    'idesoftbcn-ai-product-customizer'
                ) . '</p>'
            );
        }

        $product = iapc_resolve_designer_product($explicit_product_id);

        if (!$product) {
            return wp_kses_post(
                '<p class="iapc-designer-notice">' . esc_html__(
                    'Product not found.',
                    'idesoftbcn-ai-product-customizer'
                ) . '</p>'
            );
        }

        $output = iapc_get_designer_markup($product);

        // Notices: wp_kses_post() is enough (simple paragraph).
        // Full designer markup: MUST use iapc_kses_designer_html() (custom wp_kses
        // allowlist). wp_kses_post() would strip form/input/canvas/data-* and break UI.
        return iapc_kses_designer_html($output);
    } finally {
        if ($explicit_product_id > 0) {
            iapc_set_forced_designer_product_id(0);
        }

        if ($switched && function_exists('restore_previous_locale')) {
            restore_previous_locale();
        }
    }
}

function iapc_render_ai_guided_modal($ai_disabled = '') {
    iapc_load_frontend_template('ai-guided-modal.php', array(
        'ai_disabled' => $ai_disabled,
    ));
}

function iapc_render_customizer() {
    iapc_load_frontend_template('customizer-panels.php', array(
        'ai_premium_active' => iapc_is_ai_premium_active(),
    ));
}

function iapc_preview_overlay() {
    if (!function_exists('is_product') || !is_product()) {
        return;
    }
}
function iapc_validate_customization($passed, $product_id, $quantity) {
    if (!iapc_is_customizer_enabled($product_id)) {
        return $passed;
    }

    if (!iapc_product_is_customizable($product_id)) {
        wc_add_notice(__('This product cannot be customized.', 'idesoftbcn-ai-product-customizer'), 'error');
        return false;
    }

    if (!iapc_verify_design_nonce()) {
        wc_add_notice(__('The design could not be validated. Please reload the page and try again.', 'idesoftbcn-ai-product-customizer'), 'error');
        return false;
    }

    // phpcs:disable WordPress.Security.NonceVerification.Missing -- iapc_design_nonce verified above.

    $usa_sticker = !empty($_POST['iapc_use_sticker']) && sanitize_text_field(wp_unslash($_POST['iapc_use_sticker'])) === 'yes';
    $tiene_sticker_propio = iapc_design_has_custom_sticker_layer();

    if ($usa_sticker && $tiene_sticker_propio && !isset($_POST['iapc_rights_confirm'])) {
        wc_add_notice(__('You must confirm that you own the rights or license to use this sticker or image.', 'idesoftbcn-ai-product-customizer'), 'error');
        // phpcs:enable WordPress.Security.NonceVerification.Missing
        return false;
    }

    // phpcs:enable WordPress.Security.NonceVerification.Missing

    return $passed;
}

/**
 * Validates that binary data is a real PNG image (magic bytes + image headers).
 *
 * @param string $binary Decoded image bytes.
 * @return bool
 */
function iapc_is_valid_png_binary($binary) {
    if (!is_string($binary) || $binary === '') {
        return false;
    }

    // PNG signature: 89 50 4E 47 0D 0A 1A 0A
    if (strncmp($binary, "\x89PNG\r\n\x1a\n", 8) !== 0) {
        return false;
    }

    if (!function_exists('getimagesizefromstring')) {
        return false;
    }

    $info = @getimagesizefromstring($binary);

    if (!is_array($info) || empty($info[0]) || empty($info[1])) {
        return false;
    }

    if (!isset($info[2]) || (int) $info[2] !== IMAGETYPE_PNG) {
        return false;
    }

    $mime = isset($info['mime']) ? $info['mime'] : '';

    return $mime === 'image/png';
}

function iapc_save_png_as_file($base64_png) {
    if (!is_string($base64_png) || $base64_png === '') {
        return '';
    }

    // Only accept a PNG data URI prefix.
    if (strpos($base64_png, 'data:image/png;base64,') !== 0) {
        return '';
    }

    $base64_png = substr($base64_png, strlen('data:image/png;base64,'));
    $base64_png = str_replace(' ', '+', $base64_png);
    // Keep only valid base64 characters.
    $base64_png = preg_replace('/[^A-Za-z0-9+\/=]/', '', $base64_png);

    if ($base64_png === '' || strlen($base64_png) > IAPC_MAX_DESIGN_BYTES * 1.4) {
        return '';
    }

    $imagen_decodificada = base64_decode($base64_png, true);

    if (
        $imagen_decodificada === false
        || $imagen_decodificada === ''
        || strlen($imagen_decodificada) > IAPC_MAX_DESIGN_BYTES
        || !iapc_is_valid_png_binary($imagen_decodificada)
    ) {
        return '';
    }

    $upload_dir = wp_upload_dir();
    $carpeta = trailingslashit($upload_dir['basedir']) . 'iapc-designs/';
    $url_base = trailingslashit($upload_dir['baseurl']) . 'iapc-designs/';

    if (!file_exists($carpeta)) {
        wp_mkdir_p($carpeta);
    }

    $nombre_archivo = 'design-' . time() . '-' . wp_generate_password(8, false) . '.png';
    $ruta_archivo = $carpeta . $nombre_archivo;

    // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Binary PNG write after validation.
    if (!file_put_contents($ruta_archivo, $imagen_decodificada)) {
        return '';
    }

    return $url_base . $nombre_archivo;
}

// Saves a customer-uploaded image file.
function iapc_save_uploaded_customer_image($file) {
    if (empty($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) {
        return '';
    }

    // wp_handle_upload() lives in wp-admin/includes/file.php (not loaded on the storefront by default).
    require_once ABSPATH . 'wp-admin/includes/file.php';

    $checked = wp_check_filetype_and_ext(
        $file['tmp_name'],
        $file['name'],
        array(
            'jpg|jpeg|jpe' => 'image/jpeg',
            'png'          => 'image/png',
            'webp'         => 'image/webp',
        )
    );

    if (empty($checked['type']) || empty($checked['ext'])) {
        return '';
    }

    $file['type'] = $checked['type'];

    $upload = wp_handle_upload(
        $file,
        array(
            'test_form' => false,
            'mimes'     => array(
                'jpg|jpeg|jpe' => 'image/jpeg',
                'png'          => 'image/png',
                'webp'         => 'image/webp',
            ),
        )
    );

    if (!empty($upload['url'])) {
        return esc_url_raw($upload['url']);
    }

    return '';
}

// Persists customization data on the cart item.
function iapc_save_cart_item_data($cart_item_data, $product_id) {
    $product_id = absint($product_id);

    if (!iapc_is_customizer_enabled($product_id)) {
        return $cart_item_data;
    }

    if (!iapc_verify_design_nonce()) {
        return $cart_item_data;
    }

    // phpcs:disable WordPress.Security.NonceVerification.Missing -- iapc_design_nonce verified above.

    $text_front = iapc_get_post_side_text('front');
    $text_back = iapc_get_post_side_text('back');
    $has_image_front = iapc_post_side_has_image('front');
    $has_image_back = iapc_post_side_has_image('back');

    $stickers = iapc_get_post_design_layers('iapc_stickers_data');

    $stickers_front = iapc_count_stickers_per_side($stickers, 'front');
    $stickers_back = iapc_count_stickers_per_side($stickers, 'back');

    $front_designed = iapc_side_has_content($text_front, $has_image_front, $stickers_front);
    $back_designed = iapc_side_has_content($text_back, $has_image_back, $stickers_back);

    if ($front_designed && $back_designed) {
        $cart_item_data['iapc_side'] = __('Both sides', 'idesoftbcn-ai-product-customizer');
    } elseif ($back_designed) {
        $cart_item_data['iapc_side'] = __('Side B', 'idesoftbcn-ai-product-customizer');
    } elseif ($front_designed) {
        $cart_item_data['iapc_side'] = __('Side A', 'idesoftbcn-ai-product-customizer');
    }

    $cart_item_data['iapc_text_front'] = $text_front;
    $cart_item_data['iapc_text_back'] = $text_back;
    $cart_item_data['iapc_has_image_front'] = iapc_yes_no_flag($has_image_front);
    $cart_item_data['iapc_has_image_back'] = iapc_yes_no_flag($has_image_back);

    $cart_item_data['iapc_use_text'] = iapc_yes_no_flag($text_front !== '' || $text_back !== '');

    $cart_item_data['iapc_use_image'] = iapc_yes_no_flag($has_image_front || $has_image_back);

    $stickers_json = iapc_get_post_design_layers_json('iapc_stickers_data');

    if ($stickers_json !== '') {
        $cart_item_data['iapc_stickers_data'] = $stickers_json;
    }

    $cart_item_data['iapc_use_sticker'] = iapc_yes_no_flag(!empty($stickers));

    $cart_item_data['iapc_rights_confirm'] = iapc_yes_no_flag(isset($_POST['iapc_rights_confirm']));

    if ($text_front !== '' || $text_back !== '') {
        $cart_item_data['iapc_text'] = $text_front !== '' ? $text_front : $text_back;
    }

    $allowed_fonts = array(
        'Arial',
        'Georgia',
        'Courier New',
        'Verdana',
        'Trebuchet MS',
        'Tahoma',
        'Times New Roman',
        'Impact',
        'Comic Sans MS',
        'Lucida Sans Unicode',
        'Palatino Linotype',
        'Garamond',
        'Book Antiqua',
        'Arial Black',
        'Century Gothic',
        'Franklin Gothic Medium',
        'Candara',
        'Calibri',
        'Cambria',
        'Consolas'
    );

    $font = isset($_POST['iapc_font']) ? sanitize_text_field(wp_unslash($_POST['iapc_font'])) : 'Arial';
    $cart_item_data['iapc_font'] = in_array($font, $allowed_fonts, true) ? $font : 'Arial';

    $color = isset($_POST['iapc_color']) ? sanitize_hex_color(wp_unslash($_POST['iapc_color'])) : '#000000';
    $cart_item_data['iapc_color'] = $color ?: '#000000';

    $cart_item_data['iapc_font_size'] = iapc_clamp_post_int('iapc_font_size', 14, 45, 26);
    $cart_item_data['iapc_bold'] = iapc_yes_no_flag(isset($_POST['iapc_bold']));
    $cart_item_data['iapc_italic'] = iapc_yes_no_flag(isset($_POST['iapc_italic']));
    $cart_item_data['iapc_pos_x'] = iapc_clamp_post_int('iapc_pos_x', 0, 100, 50);
    $cart_item_data['iapc_pos_y'] = iapc_clamp_post_int('iapc_pos_y', 0, 100, 55);

    $cart_item_data['iapc_image_size'] = iapc_clamp_post_int('iapc_image_size', 40, 220, 100);
    $cart_item_data['iapc_image_rotation'] = iapc_clamp_post_int('iapc_image_rotation', -180, 180, 0);
    $cart_item_data['iapc_image_layer'] = iapc_get_post_layer('iapc_image_layer', 'behind');
    $cart_item_data['iapc_image_x'] = iapc_clamp_post_int('iapc_image_x', 0, 100, 50);
    $cart_item_data['iapc_image_y'] = iapc_clamp_post_int('iapc_image_y', 0, 100, 45);

    if (!empty($_POST['iapc_sticker'])) {
        $stickers_permitidos = array_keys(iapc_get_custom_stickers());
        $sticker_limpio = sanitize_text_field(wp_unslash($_POST['iapc_sticker']));

        if (in_array($sticker_limpio, $stickers_permitidos, true)) {
            $cart_item_data['iapc_sticker'] = $sticker_limpio;
        }
    }

    $cart_item_data['iapc_has_custom_sticker'] = iapc_yes_no_flag(iapc_get_post_yes_flag('iapc_has_custom_sticker'));

    $cart_item_data['iapc_sticker_size'] = iapc_clamp_post_int('iapc_sticker_size', 30, 180, 80);
    $cart_item_data['iapc_sticker_rotation'] = iapc_clamp_post_int('iapc_sticker_rotation', -180, 180, 0);
    $cart_item_data['iapc_sticker_layer'] = iapc_get_post_layer('iapc_sticker_layer', 'behind');
    $cart_item_data['iapc_sticker_x'] = iapc_clamp_post_int('iapc_sticker_x', 0, 100, 50);
    $cart_item_data['iapc_sticker_y'] = iapc_clamp_post_int('iapc_sticker_y', 0, 100, 45);

    if (!empty($_FILES['iapc_image']['tmp_name'])) {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated with is_uploaded_file() in iapc_save_uploaded_customer_image().
        $iapc_image_upload = $_FILES['iapc_image'];

        if (is_uploaded_file($iapc_image_upload['tmp_name'])) {
            $imagen_cliente_url = iapc_save_uploaded_customer_image($iapc_image_upload);

            if (!empty($imagen_cliente_url)) {
                $cart_item_data['iapc_customer_image_url'] = $imagen_cliente_url;
            }
        }
    }

    $url_png_front = iapc_save_png_from_post('iapc_design_image_front');
    if (!empty($url_png_front)) {
        $cart_item_data['iapc_design_image_url_front'] = esc_url_raw($url_png_front);
    }

    $url_png_back = iapc_save_png_from_post('iapc_design_image_back');
    if (!empty($url_png_back)) {
        $cart_item_data['iapc_design_image_url_back'] = esc_url_raw($url_png_back);
    }

    $url_png_legacy = iapc_save_png_from_post('iapc_design_image');
    if (!empty($url_png_legacy)) {
        $cart_item_data['iapc_design_image_url'] = esc_url_raw($url_png_legacy);
    } elseif (!empty($cart_item_data['iapc_design_image_url_front'])) {
        $cart_item_data['iapc_design_image_url'] = $cart_item_data['iapc_design_image_url_front'];
    } elseif (!empty($cart_item_data['iapc_design_image_url_back'])) {
        $cart_item_data['iapc_design_image_url'] = $cart_item_data['iapc_design_image_url_back'];
    }

    $url_cliente_front = iapc_save_png_from_post('iapc_customer_image_front');
    if (!empty($url_cliente_front)) {
        $cart_item_data['iapc_customer_image_url_front'] = esc_url_raw($url_cliente_front);
    }

    $url_cliente_back = iapc_save_png_from_post('iapc_customer_image_back');
    if (!empty($url_cliente_back)) {
        $cart_item_data['iapc_customer_image_url_back'] = esc_url_raw($url_cliente_back);
    }

    $url_cliente_legacy = iapc_save_png_from_post('iapc_customer_image_data');
    if (!empty($url_cliente_legacy)) {
        $cart_item_data['iapc_customer_image_url'] = esc_url_raw($url_cliente_legacy);
    } elseif (!empty($cart_item_data['iapc_customer_image_url_front'])) {
        $cart_item_data['iapc_customer_image_url'] = $cart_item_data['iapc_customer_image_url_front'];
    } elseif (!empty($cart_item_data['iapc_customer_image_url_back'])) {
        $cart_item_data['iapc_customer_image_url'] = $cart_item_data['iapc_customer_image_url_back'];
    }

    $assets_json = iapc_get_post_design_layers_json('iapc_used_assets_data');

    if ($assets_json !== '') {
        $cart_item_data['iapc_used_assets_data'] = $assets_json;
    }

    $cart_item_data['iapc_used_ai'] = iapc_yes_no_flag(iapc_get_post_yes_flag('iapc_used_ai'));

    $cart_item_data['iapc_unique_key'] = md5(microtime() . wp_rand());

    // phpcs:enable WordPress.Security.NonceVerification.Missing

    return $cart_item_data;
}

function iapc_get_cart_customization_allowed_html() {
    return array(
        'br'     => array('class' => true),
        'span'   => array('class' => true),
        'strong' => array(),
        'a'      => array(
            'href'   => true,
            'target' => true,
            'rel'    => true,
        ),
    );
}

function iapc_add_cart_customization_line(array &$lines, $label, $value) {
    if ($value === null || $value === '') {
        return;
    }

    $lines[] = sprintf(
        '<span class="iapc-cart-customization-line"><strong>%s:</strong> %s</span>',
        esc_html($label),
        $value
    );
}

function iapc_build_cart_customization_display_html($cart_item) {
    $lines = array();

    iapc_add_cart_customization_line(
        $lines,
        __('Designed sides', 'idesoftbcn-ai-product-customizer'),
        !empty($cart_item['iapc_side']) ? esc_html($cart_item['iapc_side']) : ''
    );

    if (!empty($cart_item['iapc_text_front'])) {
        iapc_add_cart_customization_line(
            $lines,
            __('Text (Side A)', 'idesoftbcn-ai-product-customizer'),
            esc_html($cart_item['iapc_text_front'])
        );
    }

    if (!empty($cart_item['iapc_text_back'])) {
        iapc_add_cart_customization_line(
            $lines,
            __('Text (Side B)', 'idesoftbcn-ai-product-customizer'),
            esc_html($cart_item['iapc_text_back'])
        );
    }

    if (
        empty($cart_item['iapc_text_front']) &&
        empty($cart_item['iapc_text_back']) &&
        !empty($cart_item['iapc_text'])
    ) {
        iapc_add_cart_customization_line(
            $lines,
            __('Text', 'idesoftbcn-ai-product-customizer'),
            esc_html($cart_item['iapc_text'])
        );
    }

    if (!empty($cart_item['iapc_use_image'])) {
        iapc_add_cart_customization_line(
            $lines,
            __('Custom image', 'idesoftbcn-ai-product-customizer'),
            esc_html(iapc_format_yes_no($cart_item['iapc_use_image']))
        );
    }

    if (!empty($cart_item['iapc_use_sticker'])) {
        iapc_add_cart_customization_line(
            $lines,
            __('Custom sticker', 'idesoftbcn-ai-product-customizer'),
            esc_html(iapc_format_yes_no($cart_item['iapc_use_sticker']))
        );
    }

    if (!empty($cart_item['iapc_used_ai']) && iapc_is_yes($cart_item['iapc_used_ai'])) {
        iapc_add_cart_customization_line(
            $lines,
            __('AI image', 'idesoftbcn-ai-product-customizer'),
            esc_html(iapc_format_yes_no('yes'))
        );
    }

    if (isset($cart_item['iapc_sticker'])) {
        $stickers = iapc_get_custom_stickers();
        $sticker_label = $stickers[$cart_item['iapc_sticker']]['label'] ?? $cart_item['iapc_sticker'];

        iapc_add_cart_customization_line(
            $lines,
            __('Sticker', 'idesoftbcn-ai-product-customizer'),
            esc_html(is_string($sticker_label) ? $sticker_label : $cart_item['iapc_sticker'])
        );
    }

    $design_sides = array(
        'iapc_design_image_url_front' => __('Final design (Side A)', 'idesoftbcn-ai-product-customizer'),
        'iapc_design_image_url_back'  => __('Final design (Side B)', 'idesoftbcn-ai-product-customizer'),
    );

    foreach ($design_sides as $key => $label) {
        if (!empty($cart_item[$key])) {
            iapc_add_cart_customization_line(
                $lines,
                $label,
                iapc_render_design_link($cart_item[$key])
            );
        }
    }

    if (
        empty($cart_item['iapc_design_image_url_front']) &&
        empty($cart_item['iapc_design_image_url_back']) &&
        !empty($cart_item['iapc_design_image_url'])
    ) {
        iapc_add_cart_customization_line(
            $lines,
            __('Final design', 'idesoftbcn-ai-product-customizer'),
            iapc_render_design_link($cart_item['iapc_design_image_url'])
        );
    }

    if (empty($lines)) {
        return '';
    }

    return '<span class="iapc-cart-customization-details">' . implode('', $lines) . '</span>';
}

// Displays customization data in the cart.
function iapc_display_cart_item_data($item_data, $cart_item) {
    $html = iapc_build_cart_customization_display_html($cart_item);

    if ($html === '') {
        return $item_data;
    }

    $safe_html = wp_kses($html, iapc_get_cart_customization_allowed_html());

    $item_data[] = array(
        'key'     => __('Customization details', 'idesoftbcn-ai-product-customizer'),
        'value'   => $safe_html,
        'display' => $safe_html,
    );

    return $item_data;
}

// Persists customization data on the order line item.
function iapc_save_order_line_item_data($item, $cart_item_key, $values, $order) {

    if (!empty($values['iapc_side'])) {
        $item->add_meta_data(__('Designed sides', 'idesoftbcn-ai-product-customizer'), $values['iapc_side'], true);
    }

    if (!empty($values['iapc_text_front'])) {
        $item->add_meta_data(__('Text (Side A)', 'idesoftbcn-ai-product-customizer'), $values['iapc_text_front'], true);
    }

    if (!empty($values['iapc_text_back'])) {
        $item->add_meta_data(__('Text (Side B)', 'idesoftbcn-ai-product-customizer'), $values['iapc_text_back'], true);
    }

    if (empty($values['iapc_text_front']) && empty($values['iapc_text_back']) && !empty($values['iapc_text'])) {
        $item->add_meta_data(__('Text', 'idesoftbcn-ai-product-customizer'), $values['iapc_text'], true);
    }

    if (!empty($values['iapc_use_image'])) {
        $item->add_meta_data(
            __('Custom image', 'idesoftbcn-ai-product-customizer'),
            iapc_format_yes_no($values['iapc_use_image']),
            true
        );
    }

    if (!empty($values['iapc_use_sticker'])) {
        $item->add_meta_data(
            __('Custom sticker', 'idesoftbcn-ai-product-customizer'),
            iapc_format_yes_no($values['iapc_use_sticker']),
            true
        );
    }

    $design_sides = array(
        'iapc_design_image_url_front' => __('Final PNG Design (Side A)', 'idesoftbcn-ai-product-customizer'),
        'iapc_design_image_url_back'  => __('Final PNG Design (Side B)', 'idesoftbcn-ai-product-customizer'),
    );

    foreach ($design_sides as $key => $label) {
        if (empty($values[$key])) {
            continue;
        }

        $url = esc_url($values[$key]);
        $html = iapc_render_design_link($url) . '<br>';
        $html .= '<img src="' . $url . '" style="max-width:250px;height:auto;border:1px solid #ddd;border-radius:8px;margin-top:8px;">';
        $item->add_meta_data($label, wp_kses_post($html), true);
    }

    if (
        empty($values['iapc_design_image_url_front']) &&
        empty($values['iapc_design_image_url_back']) &&
        !empty($values['iapc_design_image_url'])
    ) {
        $url = esc_url($values['iapc_design_image_url']);
        $html = iapc_render_design_link($url) . '<br>';
        $html .= '<img src="' . $url . '" style="max-width:250px;height:auto;border:1px solid #ddd;border-radius:8px;margin-top:8px;">';
        $item->add_meta_data(__('Final PNG Design', 'idesoftbcn-ai-product-customizer'), wp_kses_post($html), true);
    }

    if (!empty($values['iapc_customer_image_url_front'])) {
        $url_cliente = esc_url_raw($values['iapc_customer_image_url_front']);
        $html_cliente = '<a href="' . esc_url($url_cliente) . '" target="_blank" rel="noopener">' . esc_html__('View uploaded customer image (Side A)', 'idesoftbcn-ai-product-customizer') . '</a><br>';
        $html_cliente .= '<img src="' . esc_url($url_cliente) . '" style="max-width:180px;height:auto;border:1px solid #ddd;border-radius:8px;margin-top:8px;">';
        $item->add_meta_data('_iapc_customer_image_html_front', wp_kses_post($html_cliente), true);
    }

    if (!empty($values['iapc_customer_image_url_back'])) {
        $url_cliente = esc_url_raw($values['iapc_customer_image_url_back']);
        $html_cliente = '<a href="' . esc_url($url_cliente) . '" target="_blank" rel="noopener">' . esc_html__('View uploaded customer image (Side B)', 'idesoftbcn-ai-product-customizer') . '</a><br>';
        $html_cliente .= '<img src="' . esc_url($url_cliente) . '" style="max-width:180px;height:auto;border:1px solid #ddd;border-radius:8px;margin-top:8px;">';
        $item->add_meta_data('_iapc_customer_image_html_back', wp_kses_post($html_cliente), true);
    }

    if (!empty($values['iapc_customer_image_url'])) {
        $url_cliente = esc_url_raw($values['iapc_customer_image_url']);
    
        $html_cliente = '<a href="' . esc_url($url_cliente) . '" target="_blank" rel="noopener">' . esc_html__('View uploaded customer image', 'idesoftbcn-ai-product-customizer') . '</a><br>';
        $html_cliente .= '<img src="' . esc_url($url_cliente) . '" style="max-width:180px;height:auto;border:1px solid #ddd;border-radius:8px;margin-top:8px;">';
    
        $item->add_meta_data('_iapc_customer_image_html', wp_kses_post($html_cliente), true);
    
    }
    
    if (!empty($values['iapc_used_assets_data'])) {
        $assets_json = wp_unslash($values['iapc_used_assets_data']);
        $item->add_meta_data('_iapc_used_assets_data', wp_kses_post($assets_json), true);

        $assets_array = iapc_decode_design_layers_json((string) $assets_json);

        if (!empty($assets_array)) {
            $html_assets = '';

            foreach ($assets_array as $asset) {
                if (empty($asset['src'])) {
                    continue;
                }

                $tipo = !empty($asset['type']) ? $asset['type'] : 'asset';
                $nombre = !empty($asset['key']) ? $asset['key'] : $tipo;

                $html_assets .= '<div style="display:inline-block;margin:6px;text-align:center;">';
                $html_assets .= '<a href="' . esc_url($asset['src']) . '" target="_blank" rel="noopener">';
                $html_assets .= '<img src="' . esc_url($asset['src']) . '" style="max-width:90px;height:auto;border:1px solid #ddd;border-radius:6px;">';
                $html_assets .= '</a><br>';
                $html_assets .= '<small>' . esc_html($nombre) . '</small>';
                $html_assets .= '</div>';
            }

            if ($html_assets) {
                $item->add_meta_data('_iapc_assets_html', wp_kses_post($html_assets), true);
            }
        }
    }

    $tecnicos = array(
        'iapc_use_text' => __('Add text', 'idesoftbcn-ai-product-customizer'),
        'iapc_font' => __('Font', 'idesoftbcn-ai-product-customizer'),
        'iapc_color' => __('Color', 'idesoftbcn-ai-product-customizer'),
        'iapc_font_size' => __('Text size', 'idesoftbcn-ai-product-customizer'),
        'iapc_bold' => __('Bold', 'idesoftbcn-ai-product-customizer'),
        'iapc_italic' => __('Italic', 'idesoftbcn-ai-product-customizer'),
        'iapc_image_size' => __('Image size', 'idesoftbcn-ai-product-customizer'),
        'iapc_image_rotation' => __('Image rotation', 'idesoftbcn-ai-product-customizer'),
        'iapc_rights_confirm' => __('Rights confirmation', 'idesoftbcn-ai-product-customizer'),
        'iapc_has_custom_sticker' => __('Custom sticker', 'idesoftbcn-ai-product-customizer'),
        'iapc_sticker_size' => __('Sticker size', 'idesoftbcn-ai-product-customizer'),
        'iapc_sticker_rotation' => __('Sticker rotation', 'idesoftbcn-ai-product-customizer'),
    );

    $yes_no_meta_keys = array(
        'iapc_use_text',
        'iapc_bold',
        'iapc_italic',
        'iapc_rights_confirm',
        'iapc_has_custom_sticker',
    );

    foreach ($tecnicos as $key => $label) {
        if (isset($values[$key])) {
            $value = $values[$key];

            if (in_array($key, $yes_no_meta_keys, true)) {
                $value = iapc_format_yes_no($value);
            }

            if ($key === 'iapc_font_size') {
                $value .= 'px';
            }

            if (in_array($key, array('iapc_image_rotation', 'iapc_sticker_rotation'), true)) {
                $value .= 'Â°';
            }

            $item->add_meta_data('_' . $label, $value, true);
        }
    }

    if (isset($values['iapc_pos_x'], $values['iapc_pos_y'])) {
        $item->add_meta_data(
            '_' . __('Text position', 'idesoftbcn-ai-product-customizer'),
            'X: ' . $values['iapc_pos_x'] . '%, Y: ' . $values['iapc_pos_y'] . '%',
            true
        );
    }

    if (isset($values['iapc_image_x'], $values['iapc_image_y'])) {
        $item->add_meta_data(
            '_' . __('Image position', 'idesoftbcn-ai-product-customizer'),
            'X: ' . $values['iapc_image_x'] . '%, Y: ' . $values['iapc_image_y'] . '%',
            true
        );
    }

    if (isset($values['iapc_sticker_x'], $values['iapc_sticker_y'])) {
        $item->add_meta_data(
            '_' . __('Sticker position', 'idesoftbcn-ai-product-customizer'),
            'X: ' . $values['iapc_sticker_x'] . '%, Y: ' . $values['iapc_sticker_y'] . '%',
            true
        );
    }

    if (isset($values['iapc_image_layer'])) {
        $item->add_meta_data(
            '_' . __('Image position relative to text', 'idesoftbcn-ai-product-customizer'),
            $values['iapc_image_layer'] === 'front'
                ? __('In front of text', 'idesoftbcn-ai-product-customizer')
                : __('Behind text', 'idesoftbcn-ai-product-customizer'),
            true
        );
    }

    if (isset($values['iapc_sticker_layer'])) {
        $item->add_meta_data(
            '_' . __('Sticker position relative to text', 'idesoftbcn-ai-product-customizer'),
            $values['iapc_sticker_layer'] === 'front'
                ? __('In front of text', 'idesoftbcn-ai-product-customizer')
                : __('Behind text', 'idesoftbcn-ai-product-customizer'),
            true
        );
    }

}
// AI Premium license settings in admin.
function iapc_is_ai_premium_active() {
    $status  = get_option('iapc_ai_license_status', 'inactive');
    $token   = get_option('iapc_ai_license_token', '');
    $expires = get_option('iapc_ai_license_expiry', '');

    if (!in_array($status, array('active', 'trial'), true)) {
        return false;
    }

    if (empty($token)) {
        return false;
    }

    if (!empty($expires) && strtotime($expires) < strtotime(gmdate('Y-m-d'))) {
        return false;
    }

    return true;
}
function iapc_get_ai_license_state() {
    $status  = get_option('iapc_ai_license_status', 'inactive');
    $token   = get_option('iapc_ai_license_token', '');
    $expires = get_option('iapc_ai_license_expiry', '');

  if (!in_array($status, array('active', 'trial'), true) || empty($token)) {
        return 'inactive';
    }

    if (!empty($expires) && strtotime($expires) < strtotime(gmdate('Y-m-d'))) {
        return 'expired';
    }

    return 'active';
}
function iapc_get_ai_license_expiry() {
    return get_option('iapc_ai_license_expiry', '');
}

function iapc_is_ai_license_locked() {
    return iapc_get_ai_license_state() === 'active';
}

function iapc_clear_ai_license() {
    delete_option('iapc_ai_license_key');
    update_option('iapc_ai_license_status', 'inactive');
    delete_option('iapc_ai_license_token');
    delete_option('iapc_ai_license_expiry');
}

function iapc_set_license_admin_notice($type, $message) {
    $user_id = get_current_user_id();

    if (!$user_id || $message === '') {
        return;
    }

    $allowed_types = array('success', 'error', 'warning');

    set_transient(
        'iapc_license_notice_' . $user_id,
        array(
            'type'    => in_array($type, $allowed_types, true) ? $type : 'error',
            'message' => (string) $message,
        ),
        60
    );
}

function iapc_consume_license_admin_notice() {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return null;
    }

    $key    = 'iapc_license_notice_' . $user_id;
    $notice = get_transient($key);

    if ($notice) {
        delete_transient($key);
    }

    return is_array($notice) ? $notice : null;
}

function iapc_extract_license_api_error_message($data) {
    if (!is_array($data)) {
        return '';
    }

    foreach (array('message', 'error') as $field) {
        if (!empty($data[$field]) && is_string($data[$field])) {
            return sanitize_text_field($data[$field]);
        }
    }

    return '';
}

function iapc_render_license_notice_markup($notice) {
    $type = $notice['type'] ?? 'error';

    if ($type === 'success') {
        $class = 'notice-success';
    } elseif ($type === 'warning') {
        $class = 'notice-warning';
    } else {
        $class = 'notice-error';
    }

    echo '<div class="notice ' . esc_attr($class) . ' is-dismissible iapc-admin-license-notice"><p>';
    echo '<strong>' . esc_html__('Idesoftbcn AI Product Customizer:', 'idesoftbcn-ai-product-customizer') . '</strong> ';
    echo esc_html($notice['message']);
    echo '</p></div>';
}

function iapc_render_license_save_feedback_notice() {
    if (!iapc_should_show_admin_plugin_notice()) {
        return;
    }

    $screen = function_exists('get_current_screen') ? get_current_screen() : null;

    if (!$screen || $screen->post_type !== 'product') {
        return;
    }

    $notice = iapc_consume_license_admin_notice();

    if (!$notice || empty($notice['message'])) {
        return;
    }

    iapc_render_license_notice_markup($notice);
}

function iapc_activate_ai_license_key($license_key) {
    $license_key = sanitize_text_field((string) $license_key);

    if ($license_key === '') {
        iapc_clear_ai_license();
        return array(
            'success' => false,
            'message' => __('Enter a license key.', 'idesoftbcn-ai-product-customizer'),
            'type'    => 'warning',
        );
    }

    update_option('iapc_ai_license_key', $license_key);

    $response = wp_remote_post(IAPC_API_BASE_URL . '/license/activate', array(
        'timeout' => 15,
        'headers' => array(
            'Accept'         => 'application/json',
            'Content-Type'   => 'application/json',
            'X-Plugin-Key'   => 'APC_PLUGIN_V1',
            'X-Plugin-Version' => IAPC_VERSION,
        ),
        'body' => wp_json_encode(array(
            'license_key'       => $license_key,
            'installation_uuid' => iapc_get_installation_uuid(),
            'domain'            => wp_parse_url(home_url(), PHP_URL_HOST),
            'plugin_version'    => IAPC_VERSION,
            'wp_version'        => get_bloginfo('version'),
            'php_version'       => PHP_VERSION,
            'site_url'          => site_url(),
            'home_url'          => home_url(),
            'locale'            => get_locale(),
        )),
    ));

    if (is_wp_error($response)) {
        update_option('iapc_ai_license_status', 'inactive');
        delete_option('iapc_ai_license_token');
        delete_option('iapc_ai_license_expiry');

        return array(
            'success' => false,
            'message' => sprintf(
                /* translators: %s: connection error message */
                __('Could not connect to the license server: %s', 'idesoftbcn-ai-product-customizer'),
                $response->get_error_message()
            ),
            'type' => 'error',
        );
    }

    $body = wp_remote_retrieve_body($response);

    $data = json_decode($body, true);

    if (!empty($data['success']) && !empty($data['valid']) && !empty($data['ai_enabled'])) {
        update_option('iapc_ai_license_status', sanitize_text_field($data['status']));
        update_option('iapc_ai_license_token', 'valid');
        update_option('iapc_ai_license_expiry', sanitize_text_field($data['expires_at'] ?? ''));

        $expires = sanitize_text_field($data['expires_at'] ?? '');
        $message = $expires
            ? sprintf(
                /* translators: %s: license expiry date */
                __('License activated successfully. Expires on %s.', 'idesoftbcn-ai-product-customizer'),
                $expires
            )
            : __('License activated successfully.', 'idesoftbcn-ai-product-customizer');

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

    update_option('iapc_ai_license_status', 'inactive');
    delete_option('iapc_ai_license_token');
    delete_option('iapc_ai_license_expiry');

    $api_message = iapc_extract_license_api_error_message($data);

    if ($api_message === '') {
        $api_message = __('The license key is invalid or already in use on another site.', 'idesoftbcn-ai-product-customizer');
    }

    return array(
        'success' => false,
        'message' => $api_message,
        'type'    => 'error',
    );
}

function iapc_render_admin_ai_license_notice() {
    if (!iapc_should_show_admin_plugin_notice()) {
        return;
    }

    $screen = function_exists('get_current_screen') ? get_current_screen() : null;

    // Guide page already covers license setup; avoid duplicate nag there.
    if ($screen && $screen->id === 'woocommerce_page_idesoftbcn-ai-product-customizer') {
        return;
    }

    $license_state = iapc_get_ai_license_state();

    if ($license_state === 'active') {
        return;
    }

    $expires = get_option('iapc_ai_license_expiry', '');

    if ($license_state === 'expired') {
        echo '<div class="notice notice-error is-dismissible iapc-admin-license-notice"><p>';
        echo '<strong>' . esc_html__('Idesoftbcn AI Product Customizer:', 'idesoftbcn-ai-product-customizer') . '</strong> ';
        echo esc_html(
            sprintf(
                /* translators: %s: license expiry date */
                __('Your AI Premium license expired on %s. Enter a valid key in the AI Premium section.', 'idesoftbcn-ai-product-customizer'),
                $expires ? $expires : '—'
            )
        );
        echo '</p></div>';
        return;
    }

    echo '<div class="notice notice-warning is-dismissible iapc-admin-license-notice"><p>';
    echo '<strong>' . esc_html__('Idesoftbcn AI Product Customizer:', 'idesoftbcn-ai-product-customizer') . '</strong> ';
    esc_html_e('AI Premium is not active. Enter a valid key in the AI Premium section.', 'idesoftbcn-ai-product-customizer');
    echo '</p></div>';
}


function iapc_render_admin_product_image_field($field_id, $label, $extra_class = '') {
    $value = get_post_meta(get_the_ID(), $field_id, true);
    $preview_id = $field_id . '_preview';
    $field_class = 'form-field iapc-product-image-field';

    if ($extra_class !== '') {
        $field_class .= ' ' . sanitize_html_class($extra_class);
    }

    echo '<p class="' . esc_attr($field_class) . '">';
    echo '<label for="' . esc_attr($field_id) . '">' . esc_html($label) . '</label>';

    echo '<div class="iapc-product-image-actions">';

    echo '<input
            type="text"
            id="' . esc_attr($field_id) . '"
            name="' . esc_attr($field_id) . '"
            value="' . esc_url($value) . '"
            class="iapc-product-image-input" />';

    echo '<button
            type="button"
            class="button iapc-upload-product-image"
            data-target="' . esc_attr($field_id) . '">'
            . esc_html__('Upload / select', 'idesoftbcn-ai-product-customizer') .
        '</button>';

    echo '<button
            type="button"
            class="button iapc-remove-product-image"'
            . ($value ? '' : ' style="display:none;"')
            . ' data-target="' . esc_attr($field_id) . '"
            data-preview="' . esc_attr($preview_id) . '">'
            . esc_html__('Remove image', 'idesoftbcn-ai-product-customizer') .
        '</button>';

    echo '</div>';

    echo '<img
            id="' . esc_attr($preview_id) . '"
            class="iapc-product-image-preview"
            src="' . esc_url($value) . '"'
            . ($value ? '' : ' style="display:none;"')
            . ' alt="">';

    echo '</p>';
}

function iapc_add_product_type_field() {
    echo '<div class="options_group">';

$customizer_enabled = get_post_meta(get_the_ID(), '_iapc_customizer_enabled', true);

if ($customizer_enabled === '') {
    $customizer_enabled = 'no';
}

$integration_mode = iapc_get_integration_mode(get_the_ID());

echo '<details class="iapc-admin-customizer-box" open>';

echo '<summary class="iapc-customizer-summary">';

echo '<span class="iapc-customizer-left">';
echo '<span class="iapc-customizer-arrow">▼</span>';
echo '<span class="iapc-customizer-title">' . esc_html__('Idesoftbcn AI Product Customizer', 'idesoftbcn-ai-product-customizer') . '</span>';
echo '</span>';

echo '<span class="iapc-customizer-switch" onclick="event.stopPropagation();">';
echo '<input type="checkbox" id="_iapc_customizer_enabled" name="_iapc_customizer_enabled" value="yes" ' . checked($customizer_enabled, 'yes', false) . '>';
echo '<label for="_iapc_customizer_enabled">' . esc_html__('Enable customizer', 'idesoftbcn-ai-product-customizer') . '</label>';
echo '</span>';

echo '</summary>';

echo '<div class="iapc-admin-customizer-content">';

if ($customizer_enabled === 'yes') {
    iapc_render_admin_product_config_warnings(get_the_ID());
}

echo '<div class="iapc-admin-ai-promo-content">';

echo '<h3 class="iapc-section-title">✨ ' . esc_html__('AI Premium', 'idesoftbcn-ai-product-customizer') . '</h3>';
echo '<p class="iapc-section-text"><strong>' . esc_html__('Power your store with unique designs created by Artificial Intelligence.', 'idesoftbcn-ai-product-customizer') . '</strong></p>';
echo '<p class="iapc-section-text">' . esc_html__('Offer customers an advanced customization experience and increase your sales.', 'idesoftbcn-ai-product-customizer') . '</p>';

echo '<div class="iapc-admin-ai-promo-bottom">';
echo '<p class="iapc-admin-ai-subtitle">';
echo wp_kses_post(__('Create unique designs in seconds with AI.<br><strong>Start today with a 14-day free trial.</strong>', 'idesoftbcn-ai-product-customizer'));
echo '</p>';

echo '<a href="https://aiproductcustomizer.com/" target="_blank" rel="noopener noreferrer" class="iapc-admin-ai-premium-btn">';
echo '✨ ' . esc_html__('Discover AI Premium', 'idesoftbcn-ai-product-customizer');
echo '</a>';

echo '</div>'; // cierra bottom
echo '</div>'; // cierra promo content

echo '<div class="iapc-integration-mode-box iapc-admin-section">';
echo '<h2 class="iapc-section-title">' . esc_html__('Storefront integration', 'idesoftbcn-ai-product-customizer') . '</h2>';
echo '<p class="iapc-section-text">' . esc_html__('Choose how the customizer appears on the storefront for this product.', 'idesoftbcn-ai-product-customizer') . '</p>';

echo '<fieldset class="iapc-integration-mode-fieldset">';

echo '<label class="iapc-integration-mode-option">';
echo '<input type="radio" name="_iapc_integration_mode" value="shortcode" ' . checked($integration_mode, 'shortcode', false) . '>';
echo '<span class="iapc-integration-mode-option-text">';
echo '<strong>' . esc_html__('Shortcode', 'idesoftbcn-ai-product-customizer') . '</strong> — ';
echo wp_kses_post(
    sprintf(
        /* translators: 1: product ID for the shortcode example */
        __('Place <code>[iapc_designer]</code> on the product page, a tab, or any page. Use <code>[iapc_designer product_id="%1$s"]</code> on other pages.', 'idesoftbcn-ai-product-customizer'),
        (string) get_the_ID()
    )
);
echo '</span>';
echo '</label>';

echo '<label class="iapc-integration-mode-option">';
echo '<input type="radio" name="_iapc_integration_mode" value="product_page" ' . checked($integration_mode, 'product_page', false) . '>';
echo '<span class="iapc-integration-mode-option-text">';
echo '<strong>' . esc_html__('Product page', 'idesoftbcn-ai-product-customizer') . '</strong> — ';
echo wp_kses_post(
    __('Shows only the customizer on the product page. WooCommerce gallery, tabs, description, and default add-to-cart are hidden. Do not add <code>[iapc_designer]</code> to the description.', 'idesoftbcn-ai-product-customizer')
);
echo '</span>';
echo '</label>';

echo '</fieldset>';

echo '</div>';

echo '<div class="iapc-admin-section">';
echo '<h2 class="iapc-section-title">' . esc_html__('Customization surcharges', 'idesoftbcn-ai-product-customizer') . '</h2>';

$iapc_fees = array(
    '_iapc_fee_text'     => __('Text surcharge (€)', 'idesoftbcn-ai-product-customizer'),
    '_iapc_fee_image'    => __('Uploaded image surcharge (€)', 'idesoftbcn-ai-product-customizer'),
    '_iapc_fee_sticker'  => __('Sticker surcharge (€)', 'idesoftbcn-ai-product-customizer'),
    '_iapc_fee_graphic'  => __('Graphic element surcharge (€)', 'idesoftbcn-ai-product-customizer'),
    '_iapc_fee_ai'       => __('AI surcharge (€)', 'idesoftbcn-ai-product-customizer'),
);

echo '<div class="iapc-fees-grid">';

foreach ($iapc_fees as $field_id => $label) {

    echo '<div class="iapc-fee-item">';

    woocommerce_wp_text_input(array(
        'id'                => $field_id,
        'label'             => $label,
        'type'              => 'number',
        'value'             => get_post_meta(get_the_ID(), $field_id, true),
        'custom_attributes' => array(
            'step' => '0.01',
            'min'  => '0'
        )
    ));

    echo '</div>';
}

echo '</div>';

echo '</div>';

/*
    woocommerce_wp_select(array(
        'id' => '_iapc_product_type',
        'label' => 'Product type',
        'options' => array(
            'mug'      => 'Mug',
            'tshirt'   => 'T-shirt',
            'notebook' => 'Notebook',
            'car'      => 'Car'
        ),
        'desc_tip' => true,
        'description' => 'Choose which template this product will use.'
    ));

*/
    /*
    // 3D GLB test fields (disabled).
    $model_3d = get_post_meta(get_the_ID(), '_iapc_car_3d_model', true);

    echo '<h2 style="padding-left:12px;">' . esc_html__('Car 3D model', 'idesoftbcn-ai-product-customizer') . '</h2>';
    
    echo '<p class="form-field">';
    echo '<label for="_iapc_car_3d_model">' . esc_html__('GLB / GLTF file', 'idesoftbcn-ai-product-customizer') . '</label>';
    echo '<input type="text" id="_iapc_car_3d_model" name="_iapc_car_3d_model" value="' . esc_url($model_3d) . '" style="width:60%;" />';
    echo '<button type="button" class="button iapc-upload-product-image" data-target="_iapc_car_3d_model">' . esc_html__('Upload / select', 'idesoftbcn-ai-product-customizer') . '</button>';
    echo '<span class="description" style="display:block;margin-left:150px;margin-top:6px;">';
    echo esc_html__('Important: the model must have the car body separated and named Body, CarBody or Carroceria. Otherwise, the normal 2D view will be used.', 'idesoftbcn-ai-product-customizer');
    echo '</span>';
    echo '</p>';
    
    $body_meshes = get_post_meta(get_the_ID(), '_iapc_car_body_meshes', true);
    
    echo '<p class="form-field">';
    echo '<label for="_iapc_car_body_meshes">' . esc_html__('Body meshes', 'idesoftbcn-ai-product-customizer') . '</label>';
    echo '<textarea id="_iapc_car_body_meshes" name="_iapc_car_body_meshes" style="width:60%;height:90px;">' . esc_textarea($body_meshes) . '</textarea>';
    echo '<span class="description" style="display:block;margin-left:150px;margin-top:6px;">';
    echo esc_html__('Paste here the names of the body parts in JSON format. Example: ["SportsCar2_Cube006-Mesh_1","SportsCar2_Cube006-Mesh_2"]', 'idesoftbcn-ai-product-customizer');
    echo '</span>';
    echo '</p>';
    */

    echo '<div class="iapc-admin-section">';
    echo '<h2 class="iapc-section-title">' . esc_html__('Add your own product images', 'idesoftbcn-ai-product-customizer') . '</h2>';
    echo '<p class="iapc-section-text">';
    esc_html_e('Upload one image per product view. Customers will see these images in the designer while customizing the product.', 'idesoftbcn-ai-product-customizer');
    echo '</p>';

echo '<p class="iapc-section-text">';
echo wp_kses_post(
    __('Images for <strong>Side A</strong> and <strong>Side B</strong> are the main ones. If your product has more sides (for example a car, box, or multi-view object), add them under <strong>Additional product images</strong>.', 'idesoftbcn-ai-product-customizer')
);
echo '</p>';
    echo '<p class="iapc-section-text">';
    esc_html_e('These images are shown in the product preview and in the designer.', 'idesoftbcn-ai-product-customizer');
    echo '</p>';
    
$iapc_images_main = array(
    '_iapc_custom_front_image' => __('Image view 1 / Side A', 'idesoftbcn-ai-product-customizer'),
    '_iapc_custom_back_image'  => __('Image view 2 / Side B', 'idesoftbcn-ai-product-customizer'),
);

$iapc_images_extra = array(
    '_iapc_custom_left_image'  => __('Image view 3', 'idesoftbcn-ai-product-customizer'),
    '_iapc_custom_right_image' => __('Image view 4', 'idesoftbcn-ai-product-customizer'),
    '_iapc_custom_top_image'   => __('Image view 5', 'idesoftbcn-ai-product-customizer'),
);

foreach ($iapc_images_main as $field_id => $label) {
    iapc_render_admin_product_image_field($field_id, $label, 'iapc-product-image-field-main');
}

echo '<details class="iapc-admin-extra-images" style="margin:12px 0;">';
echo '<summary>' . esc_html__('Additional product images', 'idesoftbcn-ai-product-customizer') . '</summary>';

foreach ($iapc_images_extra as $field_id => $label) {
    iapc_render_admin_product_image_field($field_id, $label);
}

echo '</details>';

echo '</div>';

echo '<div class="iapc-admin-section">';
echo '<h2 class="iapc-section-title">' . esc_html__('Main print areas', 'idesoftbcn-ai-product-customizer') . '</h2>';
echo '<p class="iapc-section-text">';
esc_html_e('Drag and resize the rectangle on each product image to define where customers can place designs.', 'idesoftbcn-ai-product-customizer');
echo '</p>';
echo '<p class="iapc-section-text iapc-print-area-default-notice">';
esc_html_e('Default area applied. Adjust if your image requires it.', 'idesoftbcn-ai-product-customizer');
echo '</p>';

$views_main = array(
    'front' => __('View 1 / Side A', 'idesoftbcn-ai-product-customizer'),
    'back'  => __('View 2 / Side B', 'idesoftbcn-ai-product-customizer'),
);

$views_extra = array(
    'left'  => __('View 3', 'idesoftbcn-ai-product-customizer'),
    'right' => __('View 4', 'idesoftbcn-ai-product-customizer'),
    'top'   => __('View 5', 'idesoftbcn-ai-product-customizer'),
);

$view_image_fields = array(
    'front' => '_iapc_custom_front_image',
    'back'  => '_iapc_custom_back_image',
    'left'  => '_iapc_custom_left_image',
    'right' => '_iapc_custom_right_image',
    'top'   => '_iapc_custom_top_image',
);

$iapc_render_print_area_fields = function ($view_key, $view_label, $with_visual = false) use ($view_image_fields) {
    $product_id = get_the_ID();
    $defaults = iapc_get_default_print_area();
    $image_field = isset($view_image_fields[$view_key]) ? $view_image_fields[$view_key] : '';
    $image_url = $image_field !== '' ? get_post_meta($product_id, $image_field, true) : '';

    echo '<div class="iapc-print-area-block" data-view="' . esc_attr($view_key) . '">';

    echo '<h3 class="iapc-subsection-title">' . esc_html(sprintf(
        /* translators: %s: product view label */
        __('Print area - %s', 'idesoftbcn-ai-product-customizer'),
        $view_label
    )) . '</h3>';

    if ($with_visual) {
        echo '<div class="iapc-print-area-editor"';
        echo ' data-view="' . esc_attr($view_key) . '"';
        echo ' data-image-field="' . esc_attr($image_field) . '"';
        echo ' data-field-x="_iapc_print_' . esc_attr($view_key) . '_x"';
        echo ' data-field-y="_iapc_print_' . esc_attr($view_key) . '_y"';
        echo ' data-field-w="_iapc_print_' . esc_attr($view_key) . '_w"';
        echo ' data-field-h="_iapc_print_' . esc_attr($view_key) . '_h"';
        echo ' data-field-rotation="_iapc_print_' . esc_attr($view_key) . '_rotation"';
        echo '>';

        echo '<p class="iapc-print-area-editor-empty"' . ($image_url ? ' style="display:none;"' : '') . '>';
        esc_html_e('Upload a product image for this view to set the print area visually.', 'idesoftbcn-ai-product-customizer');
        echo '</p>';

        echo '<div class="iapc-print-area-stage"' . ($image_url ? '' : ' style="display:none;"') . '>';
        echo '<img class="iapc-print-area-image" src="' . esc_url($image_url) . '" alt="">';
        echo '<div class="iapc-print-area-rect" tabindex="0">';
        echo '<span class="iapc-print-area-handle iapc-print-area-handle-nw" data-handle="nw"></span>';
        echo '<span class="iapc-print-area-handle iapc-print-area-handle-ne" data-handle="ne"></span>';
        echo '<span class="iapc-print-area-handle iapc-print-area-handle-sw" data-handle="sw"></span>';
        echo '<span class="iapc-print-area-handle iapc-print-area-handle-se" data-handle="se"></span>';
        echo '</div>';
        echo '</div>';

        echo '</div>';
    }

    echo '<details class="iapc-print-area-advanced">';
    echo '<summary>' . esc_html__('Advanced values', 'idesoftbcn-ai-product-customizer') . '</summary>';
    echo '<div class="iapc-print-grid">';

    foreach (array(
        'x' => 'X',
        'y' => 'Y',
        'w' => __('Width', 'idesoftbcn-ai-product-customizer'),
        'h' => __('Height', 'idesoftbcn-ai-product-customizer'),
    ) as $coord => $label) {
        $saved = get_post_meta($product_id, '_iapc_print_' . $view_key . '_' . $coord, true);
        $value = ($saved !== '') ? $saved : $defaults[$coord];

        woocommerce_wp_text_input(array(
            'id' => '_iapc_print_' . $view_key . '_' . $coord,
            'label' => $label . ' ' . $view_label,
            'type' => 'number',
            'value' => $value,
            'custom_attributes' => array(
                'step' => '0.01',
                'min'  => '0',
                'max'  => '1',
            ),
            'description' => __('Example: 0.25', 'idesoftbcn-ai-product-customizer'),
        ));
    }

    $saved_rotation = get_post_meta($product_id, '_iapc_print_' . $view_key . '_rotation', true);
    $rotation_value = ($saved_rotation !== '') ? $saved_rotation : $defaults['rotation'];

    woocommerce_wp_text_input(array(
        'id' => '_iapc_print_' . $view_key . '_rotation',
        'label' => __('Rotation', 'idesoftbcn-ai-product-customizer') . ' ' . $view_label,
        'type' => 'number',
        'value' => $rotation_value,
        'custom_attributes' => array(
            'step' => '1',
            'min'  => '-180',
            'max'  => '180',
        ),
        'description' => __('Example: 0, 15, -15, 90', 'idesoftbcn-ai-product-customizer'),
    ));

    echo '</div>';
    echo '</details>';
    echo '</div>';
};

foreach ($views_main as $view_key => $view_label) {
    $iapc_render_print_area_fields($view_key, $view_label, true);
}

echo '<details class="iapc-admin-extra-printareas" style="margin:12px 0;">';
echo '<summary>' . esc_html__('Additional print areas', 'idesoftbcn-ai-product-customizer') . '</summary>';

foreach ($views_extra as $view_key => $view_label) {
    $iapc_render_print_area_fields($view_key, $view_label, true);
}

echo '</details>';

echo '</div>';

$license = get_option('iapc_ai_license_key', '');
$status  = get_option('iapc_ai_license_status', 'inactive');
$expires = get_option('iapc_ai_license_expiry', '');
$token   = get_option('iapc_ai_license_token', '');
$license_state = iapc_get_ai_license_state();
$license_locked = iapc_is_ai_license_locked();

echo '<div class="iapc-admin-ai-premium-box">';
echo '<h3 class="iapc-section-title">✨ ' . esc_html__('AI Premium', 'idesoftbcn-ai-product-customizer') . '</h3>';

echo '<div class="iapc-ai-status-grid">';

echo '<div class="iapc-ai-status-item">';
echo '<span>' . esc_html__('Status', 'idesoftbcn-ai-product-customizer') . '</span>';
if ($license_state === 'active') {
    if ($status === 'trial') {
        echo '<strong class="iapc-ai-status-active">' . esc_html__('Trial active', 'idesoftbcn-ai-product-customizer') . '</strong>';
    } else {
        echo '<strong class="iapc-ai-status-active">' . esc_html__('Premium active', 'idesoftbcn-ai-product-customizer') . '</strong>';
    }
} elseif ($license_state === 'expired') {
    echo '<strong class="iapc-ai-status-expired">' . esc_html__('License expired', 'idesoftbcn-ai-product-customizer') . '</strong>';
} else {
    echo '<strong>' . esc_html__('No license', 'idesoftbcn-ai-product-customizer') . '</strong>';
}
echo '</div>';

echo '<div class="iapc-ai-status-item">';
echo '<span>' . esc_html__('Expiry date', 'idesoftbcn-ai-product-customizer') . '</span>';
echo '<strong>' . ($expires ? esc_html($expires) : '—') . '</strong>';
echo '</div>';

echo '</div>';

if ($license_state === 'expired') {
    echo '<div class="notice notice-error inline"><p>';
    echo esc_html(
        sprintf(
            /* translators: %s: license expiry date */
            __('Your license has expired on %s. Enter a valid key and save to reactivate AI.', 'idesoftbcn-ai-product-customizer'),
            $expires ? $expires : '—'
        )
    );
    echo '</p></div>';
} elseif ($license_state === 'inactive') {
    echo '<div class="notice notice-warning inline"><p>';
    esc_html_e('AI Premium is not active. Enter and verify your license to use AI image generation.', 'idesoftbcn-ai-product-customizer');
    echo '</p></div>';
}

echo '<div class="iapc-ai-field iapc-ai-license-field">';
echo '<label for="iapc_ai_license_key">' . esc_html__('License', 'idesoftbcn-ai-product-customizer') . '</label>';

if ($license_locked) {
    echo '<input type="text" id="iapc_ai_license_key" value="' . esc_attr($license) . '" readonly disabled class="iapc-ai-license-input is-locked" />';
    echo '<p class="iapc-section-text iapc-ai-license-note">' . esc_html__('This license is active. Disconnect it to enter a different one.', 'idesoftbcn-ai-product-customizer') . '</p>';
} else {
    echo '<input type="text" id="iapc_ai_license_key" name="iapc_ai_license_key" value="' . esc_attr($license) . '" placeholder="XXXX-XXXX-XXXX-XXXX" class="iapc-ai-license-input" />';
}

echo '</div>';

echo '<div class="iapc-ai-license-actions">';

if ($license_locked) {
    echo '<button type="submit" name="iapc_license_action" value="disconnect" class="button iapc-admin-ai-disconnect-btn">';
    echo esc_html__('Disconnect license', 'idesoftbcn-ai-product-customizer');
    echo '</button>';
} else {
    echo '<button type="submit" name="iapc_license_action" value="save" class="button button-primary iapc-admin-ai-license-btn">';
    echo esc_html__('Save license', 'idesoftbcn-ai-product-customizer');
    echo '</button>';
}

echo '</div>';

$ai_rules = get_post_meta(get_the_ID(), '_iapc_ai_rules', true);

echo '<div class="iapc-ai-field">';
echo '<label for="_iapc_ai_rules">' . esc_html__('AI limitations', 'idesoftbcn-ai-product-customizer') . '</label>';
echo '<p class="iapc-ai-help">' . esc_html__('These instructions are automatically appended to the AI prompt for this product.', 'idesoftbcn-ai-product-customizer') . '</p>';
echo '<textarea id="_iapc_ai_rules" name="_iapc_ai_rules" placeholder="' . esc_attr__('Example: no green color allowed', 'idesoftbcn-ai-product-customizer') . '">' . esc_textarea($ai_rules) . '</textarea>';
echo '</div>';

echo '</div>'; // cierra iapc-admin-ai-premium-box

echo '<div style="clear:both;"></div>';

echo '</div>'; // cierra iapc-admin-customizer-content
echo '</details>'; // cierra desplegable Customizer

echo '</div>'; // cierra options_group

} // cierra iapc_add_product_type_field


function iapc_get_installation_uuid() {
    $uuid = get_option('iapc_installation_uuid');

    if (!$uuid) {
        $uuid = wp_generate_uuid4();
        update_option('iapc_installation_uuid', $uuid, false);
    }

    return $uuid;
}

// Saves the license key and remote validation response.

function iapc_save_product_type_field($post_id) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (!current_user_can('edit_product', $post_id)) {
        return;
    }

    if (
        !isset($_POST['woocommerce_meta_nonce'])
        || !wp_verify_nonce(
            sanitize_text_field(wp_unslash($_POST['woocommerce_meta_nonce'])),
            'woocommerce_save_data'
        )
    ) {
        return;
    }

    if (isset($_POST['_iapc_customizer_enabled'])) {
        update_post_meta($post_id, '_iapc_customizer_enabled', 'yes');
    } else {
        update_post_meta($post_id, '_iapc_customizer_enabled', 'no');
    }

    if (isset($_POST['_iapc_integration_mode'])) {
        $integration_mode = sanitize_text_field(wp_unslash($_POST['_iapc_integration_mode']));

        if (!in_array($integration_mode, array('shortcode', 'product_page'), true)) {
            $integration_mode = 'shortcode';
        }

        update_post_meta($post_id, '_iapc_integration_mode', $integration_mode);
    }

    $iapc_fee_fields = array(
        '_iapc_fee_text',
        '_iapc_fee_image',
        '_iapc_fee_sticker',
        '_iapc_fee_graphic',
        '_iapc_fee_ai',
    );
    
    foreach ($iapc_fee_fields as $field) {
        $value = isset($_POST[$field])
            ? floatval(str_replace(',', '.', sanitize_text_field(wp_unslash($_POST[$field]))))
            : 0;

        update_post_meta($post_id, $field, max(0, $value));
    }

    $license_action = isset($_POST['iapc_license_action'])
        ? sanitize_text_field(wp_unslash($_POST['iapc_license_action']))
        : '';

    if ($license_action !== '') {
        if (current_user_can('manage_options') || current_user_can('manage_woocommerce')) {
            if ($license_action === 'disconnect') {
                iapc_clear_ai_license();
                iapc_set_license_admin_notice('success', __('License disconnected.', 'idesoftbcn-ai-product-customizer'));
            } elseif ($license_action === 'save' && isset($_POST['iapc_ai_license_key'])) {
                $result = iapc_activate_ai_license_key(sanitize_text_field(wp_unslash($_POST['iapc_ai_license_key'])));

                if (!empty($result['message'])) {
                    iapc_set_license_admin_notice($result['type'] ?? ($result['success'] ? 'success' : 'error'), $result['message']);
                }
            }
        }
    }

    if (isset($_POST['_iapc_ai_rules'])) {
        $ai_rules = sanitize_textarea_field(wp_unslash($_POST['_iapc_ai_rules']));

        if ($ai_rules !== '') {
            update_post_meta($post_id, '_iapc_ai_rules', $ai_rules);
        } else {
            delete_post_meta($post_id, '_iapc_ai_rules');
        }
    }

    // Save optional 3D car GLB model meta.
    $model_3d = isset($_POST['_iapc_car_3d_model'])
        ? esc_url_raw(sanitize_text_field(wp_unslash($_POST['_iapc_car_3d_model'])))
        : '';

    if ($model_3d !== '') {
        update_post_meta(
            $post_id,
            '_iapc_car_3d_model',
            $model_3d
        );
    } else {
        delete_post_meta($post_id, '_iapc_car_3d_model');
    }

    // Save product type meta.
    if (isset($_POST['_iapc_product_type'])) {
        update_post_meta(
            $post_id,
            '_iapc_product_type',
            sanitize_text_field(wp_unslash($_POST['_iapc_product_type']))
        );
    }

    $iapc_image_fields = array(
        '_iapc_custom_front_image',
        '_iapc_custom_back_image',
        '_iapc_custom_left_image',
        '_iapc_custom_right_image',
        '_iapc_custom_top_image',
    );

    if (isset($_POST['_iapc_car_body_meshes'])) {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON structure validated in iapc_sanitize_car_body_meshes().
        $body_meshes_raw = trim(wp_unslash($_POST['_iapc_car_body_meshes']));

        if ($body_meshes_raw !== '') {
            $body_meshes_json = iapc_sanitize_car_body_meshes($body_meshes_raw);

            if ($body_meshes_json !== '') {
                update_post_meta($post_id, '_iapc_car_body_meshes', $body_meshes_json);
            } else {
                delete_post_meta($post_id, '_iapc_car_body_meshes');
            }
        } else {
            delete_post_meta($post_id, '_iapc_car_body_meshes');
        }
    }

    foreach ($iapc_image_fields as $field) {
        if (isset($_POST[$field])) {
            update_post_meta(
                $post_id,
                $field,
                esc_url_raw(wp_unslash($_POST[$field]))
            );
        }
    }

    $campos_area = array(
        '_iapc_print_front_x',
        '_iapc_print_front_y',
        '_iapc_print_front_w',
        '_iapc_print_front_h',

        '_iapc_print_back_x',
        '_iapc_print_back_y',
        '_iapc_print_back_w',
        '_iapc_print_back_h',

        '_iapc_print_left_x',
        '_iapc_print_left_y',
        '_iapc_print_left_w',
        '_iapc_print_left_h',

        '_iapc_print_right_x',
        '_iapc_print_right_y',
        '_iapc_print_right_w',
        '_iapc_print_right_h',

        '_iapc_print_top_x',
        '_iapc_print_top_y',
        '_iapc_print_top_w',
        '_iapc_print_top_h',
    );

    foreach ($campos_area as $campo) {

        if (!isset($_POST[$campo])) {
            continue;
        }

        $valor = trim(sanitize_text_field(wp_unslash($_POST[$campo])));

        if ($valor === '') {
            delete_post_meta($post_id, $campo);
            continue;
        }

        update_post_meta(
            $post_id,
            $campo,
            floatval(str_replace(',', '.', $valor))
        );
    }

    $rotaciones = array(
        '_iapc_print_front_rotation',
        '_iapc_print_back_rotation',
        '_iapc_print_left_rotation',
        '_iapc_print_right_rotation',
        '_iapc_print_top_rotation',
    );

    foreach ($rotaciones as $campo) {
        if (!isset($_POST[$campo])) {
            continue;
        }

        $valor = trim(sanitize_text_field(wp_unslash($_POST[$campo])));

        if ($valor === '') {
            delete_post_meta($post_id, $campo);
            continue;
        }

        update_post_meta(
            $post_id,
            $campo,
            max(-180, min(180, intval($valor)))
        );
    }
}

// Default printable area (normalized 0–1 coordinates).

function iapc_get_default_print_area() {
    return array(
        'x' => 0.25,
        'y' => 0.25,
        'w' => 0.5,
        'h' => 0.5,
        'rotation' => 0,
    );
}

function iapc_get_default_print_areas() {
    $default = iapc_get_default_print_area();
    $areas = array();

    foreach (array('front', 'back', 'left', 'right', 'top') as $view) {
        $areas[$view] = $default;
    }

    return $areas;
}

function iapc_get_print_areas_for_product($product_id) {
    $areas = iapc_get_default_print_areas();

    $views = array('front', 'back', 'left', 'right', 'top');

    foreach ($views as $view) {
        $x = get_post_meta($product_id, '_iapc_print_' . $view . '_x', true);
        $y = get_post_meta($product_id, '_iapc_print_' . $view . '_y', true);
        $w = get_post_meta($product_id, '_iapc_print_' . $view . '_w', true);
        $h = get_post_meta($product_id, '_iapc_print_' . $view . '_h', true);

        if ($x !== '' && $y !== '' && $w !== '' && $h !== '' && floatval($w) > 0 && floatval($h) > 0) {
            $areas[$view] = array(
                'x' => floatval(str_replace(',', '.', $x)),
                'y' => floatval(str_replace(',', '.', $y)),
                'w' => floatval(str_replace(',', '.', $w)),
                'h' => floatval(str_replace(',', '.', $h)),
            );
        }
    }

    foreach ($areas as $view => &$config) {
        $rotation = get_post_meta($product_id, '_iapc_print_' . $view . '_rotation', true);
        $config['rotation'] = ($rotation !== '')
            ? max(-180, min(180, intval($rotation)))
            : 0;
    }
    unset($config);

    return $areas;
}

function iapc_get_custom_product_image($product_id, $view) {
    $meta_keys = array(
        'front' => '_iapc_custom_front_image',
        'back'  => '_iapc_custom_back_image',
        'left'  => '_iapc_custom_left_image',
        'right' => '_iapc_custom_right_image',
        'top'   => '_iapc_custom_top_image',
    );

    if (!isset($meta_keys[$view])) {
        return '';
    }

    return get_post_meta($product_id, $meta_keys[$view], true);
}

function iapc_get_product_image_for_product($product_id, $product_type, $image_type) {
    $custom_image = iapc_get_custom_product_image($product_id, $image_type);

    if (!empty($custom_image)) {
        return esc_url($custom_image);
    }

    return '';
}

function iapc_get_views_for_product($product_id) {
    $view_labels = array(
        'front' => __('Side A', 'idesoftbcn-ai-product-customizer'),
        'back'  => __('Side B', 'idesoftbcn-ai-product-customizer'),
        'left'  => __('View 3', 'idesoftbcn-ai-product-customizer'),
        'right' => __('View 4', 'idesoftbcn-ai-product-customizer'),
        'top'   => __('View 5', 'idesoftbcn-ai-product-customizer'),
    );

    $views = array();

    foreach (array('front', 'back') as $view_key) {
        $custom_image = iapc_get_custom_product_image($product_id, $view_key);

        $views[$view_key] = array(
            'label' => $view_labels[$view_key],
            'image' => !empty($custom_image) ? esc_url($custom_image) : '',
            'hasCustomImage' => !empty($custom_image),
            'isAdditional' => false,
        );
    }

    foreach (array('left', 'right', 'top') as $view_key) {
        $custom_image = iapc_get_custom_product_image($product_id, $view_key);

        if (empty($custom_image)) {
            continue;
        }

        $views[$view_key] = array(
            'label' => $view_labels[$view_key],
            'image' => esc_url($custom_image),
            'hasCustomImage' => true,
            'isAdditional' => true,
        );
    }

    return $views;
}

// Customization surcharge pricing.
function iapc_calculate_customization_surcharge($product_id, $cart_item) {
    $product_id = absint($product_id);
    $extra = 0.0;

    if (!empty($cart_item['iapc_text_front']) || !empty($cart_item['iapc_text_back'])) {
        $extra += (float) get_post_meta($product_id, '_iapc_fee_text', true);
    }

    if (
        (!empty($cart_item['iapc_has_image_front']) && iapc_is_yes($cart_item['iapc_has_image_front'])) ||
        (!empty($cart_item['iapc_has_image_back']) && iapc_is_yes($cart_item['iapc_has_image_back']))
    ) {
        $extra += (float) get_post_meta($product_id, '_iapc_fee_image', true);
    }

    if (!empty($cart_item['iapc_stickers_data'])) {
        $layers = iapc_decode_design_layers_json((string) $cart_item['iapc_stickers_data']);

        foreach ($layers as $layer) {
            if ($layer['type'] === 'sticker') {
                $extra += (float) get_post_meta($product_id, '_iapc_fee_sticker', true);
            }

            if ($layer['type'] === 'graphic') {
                $extra += (float) get_post_meta($product_id, '_iapc_fee_graphic', true);
            }
        }
    }

    if (!empty($cart_item['iapc_used_ai']) && iapc_is_yes($cart_item['iapc_used_ai'])) {
        $extra += (float) get_post_meta($product_id, '_iapc_fee_ai', true);
    }

    return $extra;
}

function iapc_apply_customization_pricing($cart) {

    if (is_admin() && !defined('DOING_AJAX')) {
        return;
    }

    foreach ($cart->get_cart() as &$cart_item) {

        $product_id = $cart_item['product_id'];
        $precio_extra = iapc_calculate_customization_surcharge($product_id, $cart_item);

        if ($precio_extra <= 0) {
            continue;
        }

        if (!isset($cart_item['iapc_precio_original'])) {
            $cart_item['iapc_precio_original'] = $cart_item['data']->get_price();
        }

        $cart_item['data']->set_price($cart_item['iapc_precio_original'] + $precio_extra);
    }
}

function iapc_get_cart_design_thumbnail_url($cart_item) {
    if (!empty($cart_item['iapc_design_image_url_front'])) {
        return $cart_item['iapc_design_image_url_front'];
    }

    if (!empty($cart_item['iapc_design_image_url_back'])) {
        return $cart_item['iapc_design_image_url_back'];
    }

    if (!empty($cart_item['iapc_design_image_url'])) {
        return $cart_item['iapc_design_image_url'];
    }

    return '';
}

function iapc_filter_cart_item_thumbnail($image, $cart_item, $cart_item_key) {
    $url = iapc_get_cart_design_thumbnail_url($cart_item);

    if ($url === '') {
        return $image;
    }

    return wp_kses_post(
        sprintf(
            '<img src="%s" class="iapc-cart-custom-thumb" alt="%s" />',
            esc_url($url),
            esc_attr__('Custom design', 'idesoftbcn-ai-product-customizer')
        )
    );
}

function iapc_filter_store_api_cart_item_images($product_images, $cart_item, $cart_item_key) {
    $url = iapc_get_cart_design_thumbnail_url($cart_item);

    if ($url === '') {
        return $product_images;
    }

    $safe_url = esc_url_raw($url);

    return array(
        (object) array(
            'id'        => 0,
            'src'       => $safe_url,
            'thumbnail' => $safe_url,
            'srcset'    => '',
            'sizes'     => '',
            'name'      => '',
            'alt'       => __('Custom design', 'idesoftbcn-ai-product-customizer'),
        ),
    );
}

function iapc_enqueue_cart_assets() {
    if (!iapc_should_load_cart_assets()) {
        return;
    }

    wp_enqueue_style(
        'iapc-cart-css',
        IAPC_PUBLIC_URL . 'css/cart.css',
        array(),
        IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'css/cart.css')
    );

    wp_enqueue_script(
        'iapc-cart-thumbnail',
        IAPC_PUBLIC_URL . 'js/iapc-cart-thumbnail.js',
        array(),
        IAPC_VERSION . '.' . filemtime(IAPC_PUBLIC_DIR . 'js/iapc-cart-thumbnail.js'),
        true
    );
}

function iapc_admin_enqueue_product_scripts($hook) {
    if (!iapc_is_product_edit_screen($hook)) {
        return;
    }

    wp_enqueue_media();

    wp_enqueue_script(
        'iapc-admin-product-uploader',
        IAPC_ADMIN_URL . 'js/admin-product-uploader.js',
        array('jquery'),
        IAPC_VERSION . '.' . filemtime(IAPC_ADMIN_DIR . 'js/admin-product-uploader.js'),
        true
    );

    wp_enqueue_script(
        'iapc-admin-print-area-editor',
        IAPC_ADMIN_URL . 'js/admin-print-area-editor.js',
        array('jquery', 'iapc-admin-product-uploader'),
        IAPC_VERSION . '.' . filemtime(IAPC_ADMIN_DIR . 'js/admin-print-area-editor.js'),
        true
    );

    wp_localize_script(
        'iapc-admin-product-uploader',
        'IAPC_ADMIN',
        array(
            'selectProductImage' => __('Select product image', 'idesoftbcn-ai-product-customizer'),
            'useThisImage'       => __('Use this image', 'idesoftbcn-ai-product-customizer'),
        )
    );
}



// Allow GLB uploads in the media library.

function iapc_allow_3d_uploads($mimes) {
    $mimes['glb'] = 'model/gltf-binary';
    $mimes['gltf'] = 'model/gltf+json';
    return $mimes;
}

function iapc_fix_glb_filetype($data, $file, $filename, $mimes) {

    $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

    if ($ext === 'glb') {
        return array(
            'ext' => 'glb',
            'type' => 'model/gltf-binary',
            'proper_filename' => $filename,
        );
    }

    if ($ext === 'gltf') {
        return array(
            'ext' => 'gltf',
            'type' => 'model/gltf+json',
            'proper_filename' => $filename,
        );
    }

    return $data;
}

// Show customer uploads in the order admin view only.

function iapc_display_order_images_admin_only($item_id, $item, $product) {
    if (!is_admin()) {
        return;
    }

    $customer_image_front = $item->get_meta('_iapc_customer_image_html_front');
    $customer_image_back = $item->get_meta('_iapc_customer_image_html_back');
    $customer_image = $item->get_meta('_iapc_customer_image_html');
    $assets_html = $item->get_meta('_iapc_assets_html');

    echo '<div style="margin-top:12px;padding:10px;border:1px solid #ddd;background:#fafafa;">';
    echo '<strong>' . esc_html__('Production files', 'idesoftbcn-ai-product-customizer') . '</strong>';

    if ($customer_image_front) {
        echo '<p><strong>' . esc_html__('Customer uploaded image (Side A):', 'idesoftbcn-ai-product-customizer') . '</strong><br>';
        echo wp_kses_post($customer_image_front);
        echo '</p>';
    }

    if ($customer_image_back) {
        echo '<p><strong>' . esc_html__('Customer uploaded image (Side B):', 'idesoftbcn-ai-product-customizer') . '</strong><br>';
        echo wp_kses_post($customer_image_back);
        echo '</p>';
    }

    if ($customer_image && !$customer_image_front && !$customer_image_back) {
        echo '<p><strong>' . esc_html__('Customer uploaded image:', 'idesoftbcn-ai-product-customizer') . '</strong><br>';
        echo wp_kses_post($customer_image);
        echo '</p>';
    }

    if ($assets_html) {
        echo '<p><strong>' . esc_html__('Used stickers/elements:', 'idesoftbcn-ai-product-customizer') . '</strong><br>';
        echo wp_kses_post($assets_html);
        echo '</p>';
    }

    echo '</div>';
}

function iapc_hide_order_itemmeta($hidden) {
    $hidden[] = '_iapc_customer_image_html';
    $hidden[] = '_iapc_customer_image_html_front';
    $hidden[] = '_iapc_customer_image_html_back';
    $hidden[] = '_iapc_used_assets_data';
    $hidden[] = '_iapc_assets_html';
    return $hidden;
}

function iapc_admin_enqueue_license_notice_styles($hook) {
    if (!iapc_should_enqueue_license_notice_styles($hook)) {
        return;
    }

    $css = '
.notice.iapc-admin-license-notice {
    border: none;
    border-left: 4px solid #c3c4c7;
    border-radius: 0;
    box-shadow: none;
    padding: 1px 12px;
}
.notice.iapc-admin-license-notice.notice-warning {
    background-color: #fcf9e8;
    border-left-color: #dba617;
}
.notice.iapc-admin-license-notice.notice-error {
    background-color: #fcf0f1;
    border-left-color: #d63638;
}
.notice.iapc-admin-license-notice.notice-success {
    background-color: #edfaef;
    border-left-color: #00a32a;
}
.notice.iapc-admin-license-notice p {
    margin: 0.6em 0;
    color: #1d2327;
}
';

    wp_register_style('iapc-admin-license-notice', false, array(), IAPC_VERSION);
    wp_enqueue_style('iapc-admin-license-notice');
    wp_add_inline_style('iapc-admin-license-notice', $css);
}

// Admin product edit screen styles.
function iapc_admin_enqueue_styles($hook) {

    global $post;

    if (
        $hook !== 'post.php' &&
        $hook !== 'post-new.php'
    ) {
        return;
    }

    if (!$post || $post->post_type !== 'product') {
        return;
    }

    wp_enqueue_style(
        'iapc-admin-css',
        IAPC_ADMIN_URL . 'css/admin.css',
        array(),
        IAPC_VERSION . '.' . filemtime(IAPC_ADMIN_DIR . 'css/admin.css')
    );
}

// AI image generation AJAX handler.

function iapc_sanitize_car_body_meshes($raw) {
    $raw = trim((string) $raw);

    if ($raw === '') {
        return '';
    }

    $decoded = json_decode($raw, true);

    if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
        return '';
    }

    return wp_json_encode($decoded);
}

function iapc_is_allowed_ai_image_host($host) {
    if (empty($host)) {
        return false;
    }

    $allowed_hosts = apply_filters(
        'iapc_allowed_ai_image_hosts',
        array('aiproductcustomizer.com')
    );

    foreach ($allowed_hosts as $allowed_host) {
        $allowed_host = (string) $allowed_host;
        $suffix       = '.' . $allowed_host;
        $is_subdomain = (strlen($host) > strlen($suffix)
            && substr($host, -strlen($suffix)) === $suffix);

        if ($host === $allowed_host || $is_subdomain) {
            return true;
        }
    }

    return false;
}

function iapc_extract_api_image_url_from_body($body) {
    if (!is_array($body)) {
        return '';
    }

    $candidates = array();

    if (!empty($body['image_url'])) {
        $candidates[] = $body['image_url'];
    }

    if (!empty($body['url'])) {
        $candidates[] = $body['url'];
    }

    if (!empty($body['data']) && is_array($body['data'])) {
        if (!empty($body['data']['image_url'])) {
            $candidates[] = $body['data']['image_url'];
        }

        if (!empty($body['data']['url'])) {
            $candidates[] = $body['data']['url'];
        }
    }

    foreach ($candidates as $candidate) {
        $url = esc_url_raw((string) $candidate);

        if ($url !== '') {
            return $url;
        }
    }

    return '';
}

function iapc_is_safe_remote_ai_image_url($url) {
    $url = esc_url_raw((string) $url);

    if ($url === '' || !wp_http_validate_url($url)) {
        return false;
    }

    $scheme = wp_parse_url($url, PHP_URL_SCHEME);

    if (!in_array($scheme, array('https'), true)) {
        return false;
    }

    $host = wp_parse_url($url, PHP_URL_HOST);

    if (empty($host)) {
        return false;
    }

    if (in_array(strtolower($host), array('localhost', '127.0.0.1', '::1'), true)) {
        return false;
    }

    if (filter_var($host, FILTER_VALIDATE_IP)) {
        // Only allow public IPs (reject private / reserved ranges).
        return (bool) filter_var(
            $host,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
        );
    }

    return true;
}

function iapc_save_ai_image_binary($binary) {
    if (!is_string($binary) || $binary === '' || strlen($binary) > IAPC_MAX_DESIGN_BYTES) {
        return '';
    }

    $upload_dir = wp_upload_dir();
    $folder     = trailingslashit($upload_dir['basedir']) . 'iapc-designs/';
    $url_base   = trailingslashit($upload_dir['baseurl']) . 'iapc-designs/';

    if (!file_exists($folder)) {
        wp_mkdir_p($folder);
    }

    $filename = 'ai-' . time() . '-' . wp_generate_password(8, false) . '.png';
    $path     = $folder . $filename;

    if (!file_put_contents($path, $binary)) {
        return '';
    }

    return $url_base . $filename;
}

function iapc_import_remote_ai_image_file($url) {
    if (!iapc_is_safe_remote_ai_image_url($url)) {
        return '';
    }

    $response = wp_remote_get(
        $url,
        array(
            'timeout' => 30,
            'redirection' => 3,
        )
    );

    if (is_wp_error($response)) {
        return '';
    }

    $code = (int) wp_remote_retrieve_response_code($response);

    if ($code < 200 || $code >= 300) {
        return '';
    }

    $binary = wp_remote_retrieve_body($response);

    if ($binary === '') {
        return '';
    }

    $content_type = (string) wp_remote_retrieve_header($response, 'content-type');

    if ($content_type !== '' && strpos($content_type, 'image/') !== 0) {
        return '';
    }

    return iapc_save_ai_image_binary($binary);
}

function iapc_resolve_ai_image_from_api_body($body) {
    $raw_url = iapc_extract_api_image_url_from_body($body);

    if ($raw_url === '') {
        return '';
    }

    $trusted_url = iapc_sanitize_api_image_url($raw_url);

    if ($trusted_url !== '') {
        return $trusted_url;
    }

    return iapc_import_remote_ai_image_file($raw_url);
}

function iapc_sanitize_api_image_url($url) {
    $url = esc_url_raw((string) $url);

    if ($url === '') {
        return '';
    }

    $host = wp_parse_url($url, PHP_URL_HOST);

    if (!iapc_is_allowed_ai_image_host($host)) {
        return '';
    }

    return $url;
}

function iapc_can_request_storefront_ai($product_id) {
    if (!class_exists('WooCommerce') || !function_exists('WC') || !WC()->session) {
        return false;
    }

    $product_id = absint($product_id);

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

    return true;
}

function iapc_generate_ai_image() {
    check_ajax_referer('iapc_nonce', 'nonce');

    if (!iapc_is_ai_premium_active()) {
        wp_send_json_error(__('AI Premium is not activated.', 'idesoftbcn-ai-product-customizer'));
    }

    $product_id = absint($_POST['product_id'] ?? 0);

    if (!iapc_can_request_storefront_ai($product_id)) {
        wp_send_json_error(__('This product cannot use AI image generation.', 'idesoftbcn-ai-product-customizer'));
    }

    $prompt = sanitize_textarea_field(wp_unslash($_POST['prompt'] ?? ''));
    $rules = sanitize_textarea_field(wp_unslash($_POST['rules'] ?? ''));

    if (empty($prompt)) {
        wp_send_json_error(__('Prompt is empty.', 'idesoftbcn-ai-product-customizer'));
    }

    $remaining = WC()->session ? WC()->session->get('iapc_ai_remaining', IAPC_AI_MAX_IMAGES) : IAPC_AI_MAX_IMAGES;

    if ($remaining <= 0) {
        wp_send_json_error(array(
            'message' => __('You have reached the limit of 15 images.', 'idesoftbcn-ai-product-customizer'),
            'remaining' => 0,
        ));
    }

    $license_key = get_option('iapc_ai_license_key', '');
    $domain = wp_parse_url(home_url(), PHP_URL_HOST);

    if (empty($license_key)) {
        wp_send_json_error(__('No active license.', 'idesoftbcn-ai-product-customizer'));
    }

    $response = wp_remote_post(IAPC_API_BASE_URL . '/ai/image', array(
        'timeout' => 30,
        'headers' => array(
            'Content-Type' => 'application/json',
            'X-Plugin-Key' => 'APC_PLUGIN_V1',
            'X-Plugin-Version' => IAPC_VERSION,
        ),
        'body' => wp_json_encode(array(
            'license_key' => $license_key,
            'domain' => $domain,
            'installation_uuid' => iapc_get_installation_uuid(),
            'plugin_version' => IAPC_VERSION,
            'wp_version' => get_bloginfo('version'),
            'php_version' => PHP_VERSION,
            'site_url' => site_url(),
            'home_url' => home_url(),
            'locale' => get_locale(),
            'prompt' => $prompt,
            'rules' => $rules,
        )),
    ));

    if (is_wp_error($response)) {
        wp_send_json_error($response->get_error_message());
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);

    if (empty($body['success'])) {
        wp_send_json_error($body['message'] ?? __('Error generating AI image.', 'idesoftbcn-ai-product-customizer'));
    }

    $image_url = iapc_resolve_ai_image_from_api_body($body);

    if ($image_url === '') {
        wp_send_json_error(__('The AI service returned an invalid image URL.', 'idesoftbcn-ai-product-customizer'));
    }

    $remaining--;
    if (WC()->session) {
        WC()->session->set('iapc_ai_remaining', $remaining);
    }

    wp_send_json_success(array(
        'image_url' => $image_url,
        'prompt' => sanitize_textarea_field((string) ($body['prompt'] ?? '')),
        'remaining' => $remaining,
        'request_id' => $body['request_id'] ?? null,
    ));
}
    
    
