<?php
declare(strict_types=1);

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

/**
 * Product Bundle element renderer (shared).
 *
 * Phase 1, step 27. Renders a product bundle card — list of products,
 * total + discounted price, "Add Bundle to Cart" CTA. Supports two
 * bundle types from ProductBundleService:
 *
 *   - 'fixed'         — the bundle is the listed products, take-it-or-leave-it.
 *   - 'build_your_own' — checkboxes per product; user picks ≥ min_products
 *                       and the chosen subset is added to cart.
 *
 * Pre-extraction differences resolved:
 *
 *   - DB driver: frontend used `\Instinctor\Core\DB` (standalone-aware),
 *     editor used `$wpdb`. Unified — prefer DB class if available, fall
 *     back to $wpdb when running in pure-WP context.
 *
 *   - Currency formatting: frontend used CurrencyService::formatPrice()
 *     for $X.XX with locale-aware symbols. Editor used a literal "$" +
 *     number_format. Unified to CurrencyService when class_exists, else
 *     "$" + number_format fallback (covers raw-WP installs without the
 *     full service stack loaded).
 *
 *   - Radius model: frontend had separate $fieldRadius + $buttonRadius
 *     (cards vs CTA). Editor had a single $borderRadius for both.
 *     Adopted frontend's split model; falls back to legacy 'border_radius'
 *     when the new keys aren't set in saved data.
 *
 *   - Colors: frontend had three separate colors — accent (price text),
 *     button (CTA bg), savings (badge bg). Editor folded accent+button
 *     together. Adopted frontend's three-color split.
 *
 *   - Build-your-own selection: frontend rendered hidden checkboxes,
 *     an outline on .impulse-bundle-selectable items, a custom check
 *     icon, and shipped AJAX JS to POST to admin-ajax. Editor renders
 *     a static preview — no interactivity, no JS.
 *
 *   - Empty state: editor copy is more diagnostic
 *     ("Bundle #N — no products found." / "No bundle selected. Pick one
 *     in the panel."). Frontend is curt ("No bundle selected."). Kept
 *     per-context.
 *
 *   - Title visibility: frontend honoured `show_title` boolean.
 *     Editor always shows the title. Frontend canonical.
 *
 *   - AJAX/JS: emitted only on frontend (editor preview doesn't POST).
 *     Singleton via $GLOBALS['impulse_bundle_ui_printed'] so multiple
 *     bundle blocks on the page share one handler block.
 */
function impulse_element_render_bundle($el, array $settings, string $context): string {
    if (is_object($el)) $el = (array) $el;
    $isEditor = ($context === 'editor');

    // ── Resolve element id ──────────────────────────────────────────
    $rawId = (string) ($el['id'] ?? '');
    if ($rawId === '') $rawId = uniqid();
    $elId  = 'pb-' . preg_replace('/[^a-z0-9_-]/i', '', $rawId);

    // ── Settings ────────────────────────────────────────────────────
    $bundleId     = intval($settings['bundle_id'] ?? 0);
    $layout       = 'stack';
    $showSavings  = !isset($settings['show_savings']) || filter_var($settings['show_savings'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== false;
    $showImages   = !isset($settings['show_images'])  || filter_var($settings['show_images'],  FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== false;
    $showPrices   = !isset($settings['show_prices'])  || filter_var($settings['show_prices'],  FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== false;
    $showTitle    = !isset($settings['show_title'])   || filter_var($settings['show_title'],   FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== false;

    $btnLabel     = esc_html((string) ($settings['btn_label'] ?? (function_exists('instinctor__') ? instinctor__('Add Bundle to Cart') : 'Add Bundle to Cart')));
    $accentColor  = esc_attr((string) ($settings['accent_color']  ?? '#1d2327'));
    // Frontend split: button color falls back to accent for legacy data.
    $buttonColor  = esc_attr((string) ($settings['button_color']  ?? ($settings['accent_color'] ?? '#1d2327')));
    $savingsColor = esc_attr((string) ($settings['savings_color'] ?? '#999999'));
    $fieldColor   = esc_attr((string) ($settings['field_color']   ?? '#f5f5f5'));

    // Two-radius model with legacy single-radius fallback.
    $legacyRadius = intval($settings['border_radius'] ?? 6);
    $fieldRadius  = intval($settings['field_radius']  ?? $legacyRadius);
    $buttonRadius = intval($settings['button_radius'] ?? $legacyRadius);

    $bundleWidthPx = intval($settings['bundle_width'] ?? 400);
    if ($bundleWidthPx < 340) $bundleWidthPx = 340;
    if ($bundleWidthPx > 600) $bundleWidthPx = 600;

    $fieldText    = function_exists('impulse_contrast_color') ? impulse_contrast_color($fieldColor) : '#000000';
    $fieldTextSub = ($fieldText === '#000000') ? 'rgba(0,0,0,0.5)' : 'rgba(255,255,255,0.5)';
    $accentText   = function_exists('impulse_contrast_color') ? impulse_contrast_color($buttonColor) : '#ffffff';

    // ── Load bundle data ────────────────────────────────────────────
    $bundle   = null;
    $products = [];
    $pricing  = ['original' => 0, 'discounted' => 0, 'savings' => 0];

    if ($bundleId && class_exists('\\Instinctor\\Services\\ProductBundleService')) {
        try {
            $bundle  = \Instinctor\Services\ProductBundleService::getBundle($bundleId);
            $pricing = \Instinctor\Services\ProductBundleService::calculateBundlePrice($bundleId) ?: $pricing;
        } catch (\Throwable $e) {
            $bundle = null;
        }

        if ($bundle && !empty($bundle['products'])) {
            // Prefer the standalone DB class. Fall back to $wpdb when running on raw WP.
            $useInstinctor = class_exists('\\Instinctor\\Core\\DB');
            if ($useInstinctor) {
                $pt = \Instinctor\Core\DB::prefix() . 'instinctor_products';
            } else {
                global $wpdb;
                $pt = isset($wpdb) ? ($wpdb->prefix . 'instinctor_products') : '';
            }

            if ($pt !== '') {
                foreach (array_map('intval', $bundle['products']) as $pid) {
                    if ($useInstinctor) {
                        $p = \Instinctor\Core\DB::get_row(
                            \Instinctor\Core\DB::prepare("SELECT id, name, price, sale_price, images, gallery_images, featured_image FROM {$pt} WHERE id = %d", $pid),
                            ARRAY_A
                        );
                    } else {
                        global $wpdb;
                        $p = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery,PluginCheck.Security.DirectDB.UnescapedDBParameter -- plugin's own custom table ($pt from DB::prefix()/wpdb->prefix, not user input); query is prepared; per-request render value, not object-cacheable
                            $wpdb->prepare("SELECT id, name, price, sale_price, images, gallery_images, featured_image FROM {$pt} WHERE id = %d", $pid), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pt is the plugin's own prefixed table from DB::prefix() (110/113); id bound via %d placeholder.
                            ARRAY_A
                        );
                    }
                    if ($p) {
                        $imgs = function_exists('impulse_get_product_images') ? impulse_get_product_images((object) $p) : [];
                        $p['image_url']       = $imgs[0] ?? '';
                        $reg                  = floatval($p['price']);
                        $sale                 = floatval($p['sale_price']);
                        $p['effective_price'] = $reg > 0 ? $reg : ($sale > 0 ? $sale : 0);
                        $products[]           = $p;
                    }
                }
            }
        }
    }

    $isBuildOwn = $bundle && (($bundle['bundle_type'] ?? '') === 'build_your_own');

    // Currency formatter — service when available, else "$" + number_format.
    $fmt = static function ($amount): string {
        if (class_exists('\\Instinctor\\Services\\CurrencyService')) {
            return (string) \Instinctor\Services\CurrencyService::formatPrice((float) $amount);
        }
        return '$' . number_format((float) $amount, 2);
    };

    // ── Layout / wrapper ────────────────────────────────────────────
    $wrapStyle = 'display:flex;flex-direction:column;gap:8px;';
    $bundleWidthStyle = 'max-width:' . $bundleWidthPx . 'px;width:100%;margin:0 auto;';

    ob_start();

    echo '<div id="' . esc_attr($elId) . '" class="impulse-product-bundle" style="display:block;width:100%;box-sizing:border-box;font-family:inherit;padding:12px">';

    // ── Empty state ─────────────────────────────────────────────────
    if (!$bundle || empty($products)) {
        if ($isEditor) {
            // Editor — diagnostic copy so the builder knows what's wrong.
            echo '<div style="padding:24px;text-align:center;color:#999;font-size:13px;background:#f5f5f5;border-radius:6px">';
            echo $bundleId
                ? 'Bundle #' . intval($bundleId) . ' — no products found.'
                : 'No bundle selected. Pick one in the panel.';
            echo '</div>';
        } else {
            echo '<div style="padding:24px;text-align:center;color:#999;font-size:13px">';
            echo esc_html(function_exists('instinctor__') ? instinctor__('No bundle selected.') : 'No bundle selected.');
            echo '</div>';
        }
        echo '</div>';
        return ob_get_clean();
    }

    // Inner constrained container
    echo '<div style="' . esc_attr($bundleWidthStyle) . '">';

    // ── Bundle name + savings badge ────────────────────────────────
    echo '<div style="display:flex;align-items:center;gap:10px;margin-bottom:14px;flex-wrap:wrap">';
    if ($showTitle) {
        echo '<div class="impulse-bundle-title" style="font-size:17px;font-weight:600;color:inherit">' . esc_html((string) ($bundle['name'] ?? '')) . '</div>';
    }
    if ($showSavings && $pricing['savings'] > 0) {
        $discType = (string) ($bundle['discount_type'] ?? '');
        $discVal  = floatval($bundle['discount_value'] ?? 0);
        if ($discType === 'percent') {
            $savingsLabel = function_exists('instinctor__')
                ? sprintf(instinctor__('Save %s%%'), $discVal)
                : sprintf('Save %s%%', $discVal);
        } else {
            $savingsLabel = function_exists('instinctor__')
                ? sprintf(instinctor__('Save %s'), $fmt($pricing['savings']))
                : 'Save ' . $fmt($pricing['savings']);
        }
        echo '<span class="impulse-bundle-savings-badge" style="' . esc_attr('background:' . $savingsColor . ';color:#fff;font-size:11px;font-weight:600;padding:3px 8px;border-radius:20px;white-space:nowrap') . '">' . esc_html($savingsLabel) . '</span>';
    }
    echo '</div>';

    // ── Products list ──────────────────────────────────────────────
    echo '<div class="impulse-bundle-products" style="' . esc_attr($wrapStyle) . 'margin-bottom:16px">';
    foreach ($products as $p) {
        $frStyle   = 'border-radius:' . $fieldRadius . 'px;';
        $itemStyle = 'display:flex;align-items:center;gap:10px;padding:8px;background:' . $fieldColor . ';color:' . $fieldText . ';' . $frStyle;
        $selectableCls = (!$isEditor && $isBuildOwn) ? ' impulse-bundle-selectable' : '';
        $checked = (!$isEditor && $isBuildOwn) ? ' checked' : '';

        echo '<div class="' . esc_attr('impulse-bundle-item' . $selectableCls) . '" data-id="' . esc_attr((string) intval($p['id'])) . '" style="' . esc_attr($itemStyle) . '">';

        // Hidden checkbox (frontend build-your-own only).
        if (!$isEditor && $isBuildOwn) {
            echo '<input type="checkbox" class="impulse-bundle-check" data-id="' . esc_attr((string) intval($p['id'])) . '"' . esc_attr($checked) . ' style="display:none">';
        }

        // Stack layout: name+price on left, thumb on right, optional check on far right.
        echo '<div style="flex:1;min-width:0">';
        echo '<div style="font-size:13px;font-weight:500;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' . esc_html((string) ($p['name'] ?? '')) . '</div>';
        if ($showPrices && $p['effective_price'] > 0) {
            echo '<div style="' . esc_attr('font-size:12px;color:' . $fieldTextSub . ';margin-top:2px') . '">' . esc_html($fmt($p['effective_price'])) . '</div>';
        }
        echo '</div>';

        if ($showImages && !empty($p['image_url'])) {
            echo '<img src="' . esc_url((string) $p['image_url']) . '" alt="' . esc_attr((string) ($p['name'] ?? '')) . '" loading="lazy" style="width:52px;height:52px;object-fit:cover;border-radius:4px;flex-shrink:0">';
        }

        if (!$isEditor && $isBuildOwn) {
            echo '<div class="impulse-bundle-check-icon" style="width:20px;height:20px;border-radius:50%;border:2px solid #ccc;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background 0.15s,border-color 0.15s">';
            echo '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>';
            echo '</div>';
        }

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

    // ── Price + CTA footer ─────────────────────────────────────────
    $minSel = intval($bundle['min_products'] ?? 0);
    echo '<div class="impulse-bundle-footer" style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">';
    if ($pricing['original'] > 0) {
        echo '<div style="display:flex;align-items:baseline;gap:8px;flex:1;min-width:0">';
        if ($pricing['savings'] > 0) {
            echo '<span class="impulse-bundle-price" style="' . esc_attr('font-size:22px;font-weight:700;color:' . $accentColor) . '">' . esc_html($fmt($pricing['discounted'])) . '</span>';
            echo '<span class="impulse-bundle-original" style="font-size:14px;opacity:0.45;text-decoration:line-through">' . esc_html($fmt($pricing['original'])) . '</span>';
        } else {
            echo '<span class="impulse-bundle-price" style="' . esc_attr('font-size:22px;font-weight:700;color:' . $accentColor) . '">' . esc_html($fmt($pricing['original'])) . '</span>';
        }
        echo '</div>';
    } else {
        echo '<div style="flex:1"></div>';
    }

    $btnCursor = $isEditor ? 'default' : 'pointer';
    echo '<button type="button" class="impulse-bundle-cta impulse-bundle-add-btn" data-bundle-id="' . esc_attr((string) intval($bundleId)) . '" data-build-own="' . esc_attr($isBuildOwn ? '1' : '0') . '" data-min="' . esc_attr((string) $minSel) . '" style="' . esc_attr('display:inline-flex;align-items:center;justify-content:center;padding:0 20px;height:40px;background:' . $buttonColor . ';color:' . $accentText . ';border:none;border-radius:' . $buttonRadius . 'px;font-size:14px;font-weight:600;cursor:' . $btnCursor . ';white-space:nowrap;flex-shrink:0;transition:opacity 0.15s') . '">';
    echo esc_html($btnLabel);
    echo '</button>';
    echo '</div>';

    // Status message slot (frontend only — but harmless on editor; keep markup uniform).
    if (!$isEditor) {
        echo '<div class="impulse-bundle-msg" style="display:none;margin-top:10px;font-size:12px;text-align:center"></div>';
    }

    echo '</div>'; // inner constrained container

    // ── Inline AJAX handler (frontend only, singleton per page) ────
    if (!$isEditor && empty($GLOBALS['impulse_bundle_ui_printed'])) {
        $GLOBALS['impulse_bundle_ui_printed'] = true;

        $frontendNonce = function_exists('wp_create_nonce') ? wp_create_nonce('instinctor_frontend') : '';
        $ajaxUrl = function_exists('admin_url') ? admin_url('admin-ajax.php') : '/admin/admin-ajax.php';
        $cartUrl = class_exists('\\Instinctor\\Services\\PageService')
            ? esc_js(\Instinctor\Services\PageService::resolveUrl('cart'))
            : '/cart';

        ob_start();
        echo '(function(){';
        echo 'function initBundleUI(){';
        // Select/deselect for build-your-own
        echo 'document.addEventListener("click",function(e){';
        echo '  var item=e.target.closest(".impulse-bundle-selectable");';
        echo '  if(!item)return;';
        echo '  var cb=item.querySelector(".impulse-bundle-check");';
        echo '  if(!cb)return;';
        echo '  cb.checked=!cb.checked;';
        echo '  var icon=item.querySelector(".impulse-bundle-check-icon");';
        echo '  if(icon){';
        echo '    icon.style.background=cb.checked?"' . esc_js($accentColor) . '":"";';
        echo '    icon.style.borderColor=cb.checked?"' . esc_js($accentColor) . '":"#ccc";';
        echo '  }';
        echo '  item.style.outline=cb.checked?"2px solid ' . esc_js($accentColor) . '":"";';
        echo '});';
        // Add bundle to cart
        echo 'document.addEventListener("click",function(e){';
        echo '  var btn=e.target.closest(".impulse-bundle-cta");';
        echo '  if(!btn)return;';
        echo '  var bundleId=btn.dataset.bundleId;';
        echo '  var isBuildOwn=btn.dataset.buildOwn==="1";';
        echo '  var minSel=parseInt(btn.dataset.min)||0;';
        echo '  var wrap=btn.closest(".impulse-product-bundle");';
        echo '  var msg=wrap?wrap.querySelector(".impulse-bundle-msg"):null;';
        echo '  var svgIcon=btn.querySelector("svg")?btn.querySelector("svg").outerHTML:"";';
        echo '  var origText=btn.textContent.trim();';
        echo '  var selectedProducts=[];';
        echo '  if(isBuildOwn){';
        echo '    wrap.querySelectorAll(".impulse-bundle-check:checked").forEach(function(cb){selectedProducts.push(parseInt(cb.dataset.id));});';
        echo '    if(minSel>0&&selectedProducts.length<minSel){';
        echo '      if(msg){msg.textContent="Please select at least "+minSel+" product"+(minSel!==1?"s":"")+".";msg.style.display="block";msg.style.color="#c00";}';
        echo '      return;';
        echo '    }';
        echo '  }';
        echo '  btn.disabled=true;btn.style.opacity="0.6";';
        echo '  btn.style.width=btn.offsetWidth+"px";';
        echo '  var fd=new FormData();';
        echo '  fd.append("action","instinctor_add_bundle_to_cart");';
        echo '  fd.append("nonce","' . esc_js($frontendNonce) . '");';
        echo '  fd.append("bundle_id",bundleId);';
        echo '  selectedProducts.forEach(function(id){fd.append("selected_products[]",id);});';
        echo '  fetch("' . esc_js($ajaxUrl) . '",{method:"POST",body:fd})';
        echo '    .then(function(r){return r.json();})';
        echo '    .then(function(d){';
        echo '      if(d.success){';
        echo '        btn.textContent="Added!";';
        echo '        if(msg){msg.textContent=(d.data&&d.data.message)||"Bundle added to cart!";msg.style.display="block";msg.style.color="' . esc_js($savingsColor) . '";}';
        echo '        var badge=document.querySelector(".impulse-cart-count,.instinctor-cart-count");';
        echo '        if(badge&&d.data.cart_count!==undefined)badge.textContent=d.data.cart_count;';
        echo '        setTimeout(function(){btn.disabled=false;btn.style.opacity="";btn.style.width="";btn.innerHTML=svgIcon+" "+origText;},2000);';
        echo '      }else{';
        echo '        btn.disabled=false;btn.style.opacity="";btn.style.width="";';
        echo '        if(msg){msg.textContent=(d.data&&d.data.message)||"Failed to add bundle.";msg.style.display="block";msg.style.color="#c00";}';
        echo '      }';
        echo '    })';
        echo '    .catch(function(){';
        echo '      btn.disabled=false;btn.style.opacity="";btn.style.width="";';
        echo '      if(msg){msg.textContent="Network error. Please try again.";msg.style.display="block";msg.style.color="#c00";}';
        echo '    });';
        echo '});';
        echo '}';
        echo 'if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",initBundleUI);}else{initBundleUI();}';
        echo '})();';
        $_inlineJs = ob_get_clean();
        wp_add_inline_script('instinctor-frontend-js', $_inlineJs, 'after');
    }

    echo '</div>'; // .impulse-product-bundle

    return (string) ob_get_clean();
}
