<?php

defined('ABSPATH') || exit;

/**
 * BundlePress_Bundle_Upsell
 *
 * Injects a "Complete the Bundle" upsell section on:
 *  - Single product pages (below the Add-to-Cart button)
 *  - Cart page (above the cart totals / below the cart items)
 *
 * product and renders a React root which BundleFrontend.jsx picks up.
 */
class BundlePress_Bundle_Upsell
{
    public function __construct()
    {
        // REST route so React components can look up bundles by product ID
        add_action('rest_api_init', [$this, 'register_routes']);

        // Single-product page hook (after add-to-cart button)
        add_action('woocommerce_single_product_summary', [$this, 'render_product_upsell'], 35);
    }

    public function public_permission_check()
    {
        return true;
    }

    /* ------------------------------------------------------------------ */
    /*  REST API                                                            */
    /* ------------------------------------------------------------------ */

    public function register_routes()
    {
        register_rest_route('bundlepress/v1', '/bundles-by-product/(?P<product_id>\d+)', [
            'methods' => 'GET',
            'callback' => [$this, 'get_bundles_containing_product'],
            'permission_callback' => [$this, 'public_permission_check'],
            'args' => [
                'product_id' => [
                    'validate_callback' => function ($param) {
                        return is_numeric($param); },
                ],
            ],
        ]);

        register_rest_route('bundlepress/v1', '/recommendations/(?P<product_id>\d+)', [
            'methods' => 'GET',
            'callback' => [$this, 'get_recommendations_api'],
            'permission_callback' => [$this, 'public_permission_check'],
            'args' => [
                'product_id' => [
                    'validate_callback' => function ($param) {
                        return is_numeric($param); },
                ],
                'limit' => [
                    'validate_callback' => function ($param) {
                        return is_numeric($param); },
                ],
            ],
        ]);
    }

    public function get_recommendations_api($request)
    {
        $product_id = absint($request['product_id']);
        $limit = isset($request['limit']) ? absint($request['limit']) : 4;

        $items = $this->get_product_recommendations($product_id, $limit);

        return rest_ensure_response([
            'success' => true,
            'data' => $items
        ]);
    }

    /**
     * Return all published bundles that include $product_id as an item.
     * Also matches the *parent* product if a variation ID is passed.
     */
    public function get_bundles_containing_product($request)
    {
        $product_id = absint($request['product_id']);

        // If it is a variation, also match the parent product ID
        $ids_to_check = [$product_id];
        $product = wc_get_product($product_id);
        if ($product && $product->get_type() === 'variation') {
            $ids_to_check[] = $product->get_parent_id();
        }

        global $wpdb;
        $items_table = $wpdb->prefix . 'bundlepress_bundle_items';
        $posts_table = $wpdb->prefix . 'posts';
        $meta_table = $wpdb->prefix . 'postmeta';

        $placeholders = implode(',', array_fill(0, count($ids_to_check), '%d'));

        global $wpdb;
        $bundles_table = $wpdb->prefix . 'bundlepress_bundles';
        $items_table   = $wpdb->prefix . 'bundlepress_bundle_items';
        $fixed_table   = $wpdb->prefix . 'bundlepress_fixed_settings';
        $volume_table  = $wpdb->prefix . 'bundlepress_volume_settings';
        $fbt_table     = $wpdb->prefix . 'bundlepress_fbt_settings';
        $bxgy_table    = $wpdb->prefix . 'bundlepress_bxgy_settings';
        $mixmatch_table = $wpdb->prefix . 'bundlepress_mixmatch_settings';

        $placeholders = implode(',', array_fill(0, count($ids_to_check), '%d'));

        // 1. Get bundles where product is specifically listed as an item
        $specific_bundle_ids = $wpdb->get_col($wpdb->prepare(
            "SELECT DISTINCT bundle_id FROM $items_table WHERE product_id IN ($placeholders)",
            ...$ids_to_check
        ));

        // 2. Get all promotion bundles (volume, fbt, bxgy, mixmatch) that might be store-wide or triggered
        $promo_bundles = $wpdb->get_results("SELECT id, type, product_id FROM $bundles_table WHERE type IN ('volume', 'fbt', 'bxgy', 'mixmatch', 'fixed') AND status = 'publish'", ARRAY_A);
        
        $bundle_ids = $specific_bundle_ids;

        foreach ($promo_bundles as $pb) {
            $pb_id = (int) $pb['id'];
            $data = BundlePress_DB::get_bundle_data($pb_id);
            if (!$data) continue;

            $settings = $data['settings'] ?? [];
            $target = $settings['target_type'] ?? 'all';

            if ($target === 'all') {
                $bundle_ids[] = $pb_id;
            } elseif ($target === 'specific' && !empty($settings['trigger_products'])) {
                $trigger_ids = wp_list_pluck($settings['trigger_products'], 'product_id');
                foreach ($ids_to_check as $check_id) {
                    if (in_array((int)$check_id, array_map('intval', $trigger_ids))) {
                        $bundle_ids[] = $pb_id;
                        break;
                    }
                }
            }
        }

        $bundle_ids = array_unique(array_map('intval', $bundle_ids));

        if (empty($bundle_ids)) {
            return rest_ensure_response(['success' => true, 'data' => []]);
        }

        $bundles = [];
        foreach ($bundle_ids as $bid) {
            $data = BundlePress_DB::get_bundle_data($bid);
            if (!$data || $data['status'] !== 'publish') {
                continue;
            }

            $b_type         = $data['type'];
            $regular_price  = (float) ($data['price'] ?? 0);
            $sale_price     = (float) ($data['sale_price'] ?? 0);
            $discount_type  = $data['discount_type'] ?? '';
            $discount_value = (float) ($data['discount_value'] ?? 0);

            // For volume/mixmatch bundles, if regular price is 0, use the current product's price
            if ($regular_price <= 0 && in_array($b_type, ['volume', 'mixmatch'])) {
                $regular_price = (float) $product->get_regular_price();
            }

            $settings = $data['settings'] ?? [];

            // If it has specific trigger products, ensure the current product is one of them
            if (isset($settings['target_type']) && $settings['target_type'] === 'specific' && !empty($settings['trigger_products'])) {
                $trigger_ids = wp_list_pluck($settings['trigger_products'], 'product_id');
                $is_triggered = false;
                foreach ($ids_to_check as $check_id) {
                    if (in_array($check_id, $trigger_ids)) {
                        $is_triggered = true;
                        break;
                    }
                }
                if (!$is_triggered)
                    continue;
            }

            $display_price = $sale_price ?: $regular_price;
            if (!$sale_price && $discount_type) {
                if ($discount_type === 'percentage') {
                    $display_price = $regular_price - ($regular_price * $discount_value / 100);
                } elseif ($discount_type === 'fixed') {
                    $display_price = max(0, $regular_price - $discount_value);
                }
            }

            // Gather items for preview
            $raw_items = $wpdb->get_results($wpdb->prepare(
                "SELECT product_id, variation_id, quantity FROM {$wpdb->prefix}bundlepress_bundle_items WHERE bundle_id = %d ORDER BY position ASC LIMIT 10",
                $bid
            ), ARRAY_A);

            // If it's a store-wide volume/fbt bundle and raw_items is empty, inject the current product
            if (empty($raw_items) && in_array($b_type, ['volume', 'fbt'])) {
                $raw_items[] = [
                    'product_id' => $product_id,
                    'quantity' => 1
                ];
            }

            $bundle_items = [];

            // For FBT (Frequently Bought Together), we usually want to include the CURRENT product as the first item
            if ($b_type === 'fbt') {
                $main_p = wc_get_product($product_id);
                if ($main_p) {
                    $bundle_items[] = [
                        'product_id' => (int) $product_id,
                        'quantity' => 1,
                        'name' => $main_p->get_name() . ' (This Item)',
                        'price' => $main_p->get_price(),
                        'image' => wp_get_attachment_thumb_url($main_p->get_image_id()) ?: '',
                        'type' => $main_p->get_type(),
                        'variations' => ($main_p->get_type() === 'variable') ? array_map(function($v) {
                            return [
                                'id' => $v['variation_id'], 
                                'name' => implode(', ', array_filter($v['attributes'])), 
                                'price' => $v['display_price'],
                                'attributes' => $v['attributes']
                            ];
                        }, $main_p->get_available_variations()) : [],
                    ];
                }

                if (isset($settings['fbt_source']) && $settings['fbt_source'] === 'auto') {
                    $limit = isset($settings['fbt_limit']) ? absint($settings['fbt_limit']) : 4;
                    $recommendations = $this->get_product_recommendations($product_id, $limit);
                    $bundle_items = array_merge($bundle_items, $recommendations);
                } else {
                    foreach ($raw_items as $ri) {
                        // Avoid duplicating the current product if it's also in the manual list
                        if ((int) $ri['product_id'] === $product_id)
                            continue;

                        $rp = wc_get_product($ri['product_id']);
                        if ($rp) {
                            $v_id = isset($ri['variation_id']) ? (int) $ri['variation_id'] : null;
                            $bundle_items[] = [
                                'product_id'   => (int) $ri['product_id'],
                                'variation_id' => $v_id,
                                'quantity'     => (int) ($ri['quantity'] ?? 1),
                                'name'         => $rp->get_name(),
                                'price'        => $v_id ? (float)wc_get_product($v_id)->get_price() : $rp->get_price(),
                                'image'        => wp_get_attachment_thumb_url($rp->get_image_id()) ?: '',
                                'type'         => $rp->get_type(),
                                'variations'   => ($rp->get_type() === 'variable') ? array_map(function($v) use($rp) {
                                    return [
                                        'id' => $v['variation_id'], 
                                        'name' => implode(', ', array_filter($v['attributes'])), 
                                        'price' => $v['display_price'],
                                        'attributes' => $v['attributes']
                                    ];
                                }, $rp->get_available_variations()) : [],
                            ];
                        }
                    }
                }
            } else {
                // Non-FBT logic (Volume, MixMatch, etc)
                foreach ($raw_items as $ri) {
                    $rp = wc_get_product($ri['product_id']);
                    if ($rp) {
                        $v_id = isset($ri['variation_id']) ? (int) $ri['variation_id'] : null;
                        $bundle_items[] = [
                            'product_id'   => (int) $ri['product_id'],
                            'variation_id' => $v_id,
                            'quantity'     => (int) ($ri['quantity'] ?? 1),
                            'name'         => $rp->get_name(),
                            'price'        => $v_id ? (float)wc_get_product($v_id)->get_price() : $rp->get_price(),
                            'image'        => wp_get_attachment_thumb_url($rp->get_image_id()) ?: '',
                            'type'         => $rp->get_type(),
                            'variations'   => ($rp->get_type() === 'variable') ? array_map(function($v) use($rp) {
                                return [
                                    'id' => $v['variation_id'], 
                                    'name' => implode(', ', array_filter($v['attributes'])), 
                                    'price' => $v['display_price'],
                                    'attributes' => $v['attributes']
                                ];
                            }, $rp->get_available_variations()) : [],
                        ];
                    }
                }
            }

            // For BXGY: resolve offer_product (free gift) into full product details
            $bxgy_offer_items = [];
            if ($b_type === 'bxgy' && !empty($settings['offer_product'])) {
                $offer_products = $settings['offer_product'];
                // Normalize: can be a single product object or array of product objects
                if (isset($offer_products['product_id'])) {
                    $offer_products = [$offer_products];
                } elseif (!is_array($offer_products)) {
                    $offer_products = [];
                }

                foreach ($offer_products as $op) {
                    $op_id = isset($op['product_id']) ? (int) $op['product_id'] : 0;
                    if (!$op_id && isset($op['id']))
                        $op_id = (int) $op['id'];
                    if (!$op_id)
                        continue;

                    $op_product = wc_get_product($op_id);
                    if ($op_product) {
                        $v_id = isset($op['variation_id']) ? (int) $op['variation_id'] : null;
                        $bxgy_offer_items[] = [
                            'product_id'   => $op_id,
                            'variation_id' => $v_id,
                            'name'         => $op_product->get_name(),
                            'price'        => $v_id ? (float)wc_get_product($v_id)->get_price() : (float)$op_product->get_price(),
                            'image'        => wp_get_attachment_thumb_url($op_product->get_image_id()) ?: '',
                            'type'         => $op_product->get_type(),
                            'variations'   => ($op_product->get_type() === 'variable') ? array_map(function($v) {
                                return [
                                    'id' => $v['variation_id'], 
                                    'name' => implode(', ', array_filter($v['attributes'])), 
                                    'price' => $v['display_price'],
                                    'attributes' => $v['attributes']
                                ];
                            }, $op_product->get_available_variations()) : [],
                        ];
                    }
                }
            }

            $bundles[] = [
                'id'               => (int) $bid,
                'product_id'       => $data['product_id'] ? (int) $data['product_id'] : null,
                'name'             => $data['name'],
                'type'             => $b_type ?: 'fixed',
                'settings'         => $settings,
                'url'              => ($data['product_id'] ? get_permalink($data['product_id']) : '#'),
                'image_url'        => $data['image_url'] ?: '',
                'short_description'=> isset($data['short_description']) ? wp_kses_post($data['short_description']) : '',
                'gallery'          => isset($data['gallery']) ? $data['gallery'] : [],
                'regular_price'    => $regular_price,
                'display_price'    => $display_price,
                'discount_type'    => $discount_type,
                'discount_value'   => $discount_value,
                'sale_price'       => $sale_price ?: null,
                'item_names'       => array_column($bundle_items, 'name'),
                'item_count'       => count($bundle_items),
                'items'            => $bundle_items,
                'bxgy_offer_items' => $bxgy_offer_items,
            ];
        }

        return rest_ensure_response(['success' => true, 'data' => $bundles]);
    }

    /**
     * Get WooCommerce recommendations (Upsells -> Cross-sells -> Related)
     */
    private function get_product_recommendations($product_id, $limit = 4)
    {
        $product = wc_get_product($product_id);
        if (!$product)
            return [];

        $recommended_ids = [];

        // 1. Try Upsells
        $recommended_ids = array_merge($recommended_ids, $product->get_upsell_ids());

        // 2. Try Cross-sells if needed
        if (count($recommended_ids) < $limit) {
            $recommended_ids = array_unique(array_merge($recommended_ids, $product->get_cross_sell_ids()));
        }

        // 3. Try Related products if still needed
        if (count($recommended_ids) < $limit) {
            $related = wc_get_related_products($product_id, $limit * 2);
            $recommended_ids = array_unique(array_merge($recommended_ids, $related));
        }

        // 4. Fallback: Products in same category
        if (count($recommended_ids) < $limit) {
            $cat_ids = $product->get_category_ids();
            if (!empty($cat_ids)) {
                $args = [
                    'post_type' => 'product',
                    'post_status' => 'publish',
                    'posts_per_page' => $limit,
                    'post__not_in' => array_merge([$product_id], $recommended_ids),
                    'tax_query' => [
                        [
                            'taxonomy' => 'product_cat',
                            'field' => 'term_id',
                            'terms' => $cat_ids,
                        ],
                    ],
                    'fields' => 'ids'
                ];
                $cat_products = get_posts($args);
                $recommended_ids = array_unique(array_merge($recommended_ids, $cat_products));
            }
        }

        // 5. Final Fallback: Recent Products
        if (count($recommended_ids) < $limit) {
            $recent = get_posts([
                'post_type' => 'product',
                'post_status' => 'publish',
                'posts_per_page' => $limit,
                'post__not_in' => array_merge([$product_id], $recommended_ids),
                'fields' => 'ids'
            ]);
            $recommended_ids = array_unique(array_merge($recommended_ids, $recent));
        }

        // Clean up: remove current product and slice to limit
        $recommended_ids = array_diff($recommended_ids, [$product_id]);
        $recommended_ids = array_slice($recommended_ids, 0, $limit);

        $items = [];
        foreach ($recommended_ids as $pid) {
            $rp = wc_get_product($pid);
            if ($rp && $rp->is_visible()) {
                $items[] = [
                    'product_id' => (int) $pid,
                    'quantity' => 1,
                    'name' => $rp->get_name(),
                    'price' => $rp->get_price(),
                    'image' => wp_get_attachment_thumb_url($rp->get_image_id()) ?: '',
                ];
            }
        }

        return $items;
    }

    /* ------------------------------------------------------------------ */
    /*  Template helpers                                                    */
    /* ------------------------------------------------------------------ */

    public function render_product_upsell()
    {
        global $product;

        if (!$product || $product->get_type() === 'bundlepress') {
            return; // don't show on bundle product pages themselves
        }

        $product_id = $product->get_id();

        // Check if this product is actually a fixed bundle (handles cases where term is cached as 'simple')
        global $wpdb;
        $is_fixed_bundle = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}bundlepress_bundles WHERE product_id = %d AND type = 'fixed'",
            $product_id
        ));
        
        if ($is_fixed_bundle) {
            return;
        }

        // Quick DB check – only render if at least one bundle contains this product

        if (!$this->product_has_bundles($product_id)) {
            echo '<!-- BundlePress DEBUG: Product ' . esc_html($product_id) . ' has no associated bundles for upsell -->';
            return;
        }

        echo '<!-- BundlePress Upsell Root Injection -->';
        echo '<div id="bundlepress-upsell-root-product-' . esc_attr($product_id) . '" '
            . 'class="bundlepress-upsell-root" '
            . 'data-product-id="' . esc_attr($product_id) . '" '
            . 'data-context="product" '
            . 'style="margin: 24px 0;"></div>';
    }



    /**
     * Cheap check: does any published bundle contain this product?
     */
    private function product_has_bundles(int $product_id): bool
    {
        global $wpdb;
        $items_table = $wpdb->prefix . 'bundlepress_bundle_items';
        $bundles_table = $wpdb->prefix . 'bundlepress_bundles';

        $ids = [$product_id];
        $p = wc_get_product($product_id);
        if ($p && $p->get_type() === 'variation') {
            $ids[] = $p->get_parent_id();
        }

        $placeholders = implode(',', array_fill(0, count($ids), '%d'));

        // 1. Check if product is explicitly in bundle items
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $has_direct = (bool) $wpdb->get_var($wpdb->prepare(
            "SELECT 1 FROM $items_table as items 
             JOIN $bundles_table as bundles ON items.bundle_id = bundles.id 
             WHERE items.product_id IN ($placeholders) AND bundles.status = 'publish' LIMIT 1",
            ...$ids
        ));

        if ($has_direct) return true;

        // 2. Check store-wide or triggered bundles
        $promo_bundles = $wpdb->get_results("SELECT id, type FROM $bundles_table WHERE status = 'publish' AND type IN ('volume', 'fbt', 'bxgy')", ARRAY_A);
        foreach ($promo_bundles as $pb) {
            $data = BundlePress_DB::get_bundle_data($pb['id']);
            if (!$data) continue;
            
            $settings = $data['settings'] ?? [];
            $target = $settings['target_type'] ?? 'all';
            if ($target === 'all') return true;

            if ($target === 'specific' && !empty($settings['trigger_products'])) {
                $trigger_ids = wp_list_pluck($settings['trigger_products'], 'product_id');
                foreach ($ids as $check_id) {
                    if (in_array((int)$check_id, array_map('intval', $trigger_ids))) return true;
                }
            }
        }

        return false;
    }
}
