<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
 * REST API query handler for Navi AI Product Chat Bot.
 *
 * @package NAVIAIPR
 */

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

/**
 * Handles REST API product queries.
 */
class NAVIAIPR_Query_Handler
{

    /**
     * Plugin options.
     *
     * @var array
     */
    private $naviaipr_opts;

    /**
     * Constructor.
     *
     * @param array $opts_arr Plugin options.
     */
    public function __construct($opts_arr)
    {
        $this->naviaipr_opts = $opts_arr;
        add_action('rest_api_init', array($this, 'register_routes'));
    }


    /**
     * Register REST routes.
     */
    public function register_routes()
    {
        register_rest_route(
            'naviaipr/v1',
            '/query',
            array(
                'methods' => 'POST',
                'callback' => array($this, 'handle_query'),
                'permission_callback' => '__return_true',
            )
        );
    }

    /**
     * Parse user message for intent and price filters.
     *
     * @param string $message User input.
     * @return array
     */
    private function parse_user_message($message)
    {
        $message_lower = mb_strtolower(trim($message));
        $result = array(
            'price_operator' => null,
            'price_value' => null,
            'keywords' => null,
            'is_greeting' => false,
            'is_price_only' => false,
            'is_keyword_with_price' => false,
            'is_sort' => false,
            'sort_opt' => null,
        );

        $greetings = array('hi', 'hello', 'hey', 'hola', 'hlo', 'good morning', 'good afternoon', 'good evening', 'how are you', 'what\'s up', 'sup', 'yo', 'namaste', 'namaskar', 'hello ji');
        $greetings_text = implode(
            '|',
            array_map(
                function ($word) {
                    $quoted = preg_quote($word, '/');
                    return preg_replace('/([a-zA-Z])/', '$1+', $quoted);
                },
                $greetings
            )
        );
        $pattern = '/\b(' . $greetings_text . ')\b/i';

        if (preg_match($pattern, $message_lower)) {
            $result['is_greeting'] = true;
            return $result;
        }

        $less = array('under', 'below', 'less than', 'less', 'upto', 'up to', 'within', 'maximum', 'max', 'budget', 'cheap', 'up-to', 'kam', 'kamm', 'kumm', 'se kam', 'sekm', 'ke niche', 'ke niche', 'niche', 'neeche', 'nichu', 'ochhu');
        $greater = array('above', 'over', 'more than', 'greater than', 'greater', 'more', 'minimum', 'min', 'at least', 'atleast', 'se zyada', 'zyada', 'se upar', 'upar');
        $equal = array('exactly', 'exact', 'equal to', 'equals', 'for', 'equal', 'at', 'around', 'approximately', 'approx', 'barabar', 'ke');

        $price_operator = null;
        $price_value = null;

        $pattern_less = implode('|', array_map(fn($p) => preg_quote($p, '/'), $less));
        $pattern_greater = implode('|', array_map(fn($p) => preg_quote($p, '/'), $greater));
        $pattern_equal = implode('|', array_map(fn($p) => preg_quote($p, '/'), $equal));

        if (preg_match('/^\s*\$?\s*(\d{1,6})\s*(\$|rs)??\s*$/u', $message_lower, $matches)) {
            $result['price_operator'] = '=';
            $result['price_value'] = intval($matches[1]);
            $result['is_price_only'] = true;
            return $result;
        }
        $has_price_pattern = false;

        if (preg_match('/\b(high\s*to\s*low|low\s*to\s*high)\b/i', $message_lower, $matches)) {
            $result['is_sort'] = true;
            $result['sort_opt'] = ('high to low' === strtolower($matches[0])) ? 'high' : 'low';
            $has_price_pattern = true;
        } elseif (preg_match('/(' . $pattern_less . ')\s*\$?\s*(\d{1,6})/u', $message_lower, $matches)) {
            $price_operator = '<';
            $price_value = intval($matches[2]);
            $has_price_pattern = true;
        } elseif (preg_match('/(\d{1,6})\s*(rs|\$)?\s*(' . $pattern_less . ')/u', $message_lower, $matches)) {
            $price_operator = '<';
            $price_value = intval($matches[1]);
            $has_price_pattern = true;
        } elseif (preg_match('/(' . $pattern_greater . ')\s*\$?\s*(\d{1,6})/u', $message_lower, $matches)) {
            $price_operator = '>';
            $price_value = intval($matches[2]);
            $has_price_pattern = true;
        } elseif (preg_match('/(\d{1,6})\s*(rs|\$)?\s*(' . $pattern_greater . ')/u', $message_lower, $matches)) {
            $price_operator = '>';
            $price_value = intval($matches[1]);
            $has_price_pattern = true;
        } elseif (preg_match('/(' . $pattern_equal . ')\s*\$?\s*(\d{1,6})/u', $message_lower, $matches)) {
            $price_operator = '=';
            $price_value = intval($matches[2]);
            $has_price_pattern = true;
        } elseif (preg_match('/\$\s*(\d{1,6})|\b(\d{1,6})\s*\$/u', $message_lower, $matches)) {
            $price_operator = '=';
            $price_value = intval($matches[1] ? $matches[1] : $matches[2]);
            $has_price_pattern = true;
        } elseif (preg_match('/(\d{1,6})\s*(rs|\$)?\s*(' . $pattern_equal . ')/u', $message_lower, $matches)) {
            $price_operator = '=';
            $price_value = intval($matches[1]);
            $has_price_pattern = true;
        } elseif (!$has_price_pattern && preg_match('/\b(\d{1,6})\b/u', $message_lower, $matches)) {
            $price_operator = '=';
            $price_value = intval($matches[1]);
            $has_price_pattern = true;
        }

        $clean = $message_lower;
        if ($has_price_pattern) {
            $all = $pattern_less . '|' . $pattern_greater . '|' . $pattern_equal;
            $clean = preg_replace('/(' . $all . ')\s*\$?\s*\d{1,6}/u', '', $clean);
            $clean = preg_replace('/\d{1,6}\s*(rs|\$)?\s*(' . $all . ')/u', '', $clean);
            $clean = trim(preg_replace('/(?:\$\s*\d+(?:\.\d+)?|\d+(?:\.\d+)?\s*(?:\$|rs\b))/u', '', $clean));
            $clean = trim(preg_replace('/(?:^|\s)\$(?:\s|$)/u', ' ', $clean));
            $clean = preg_replace('/\b(high\s*to\s*low|low\s*to\s*high)\b/i', '', $clean);
        }

        $clean = preg_replace('/\b(muje|mujhe|please|plz|chahiye|chaiye|product|products|chahie| ya | or | aur )\b/u', '', $clean);
        $clean = trim(preg_replace('/\s+/u', ' ', $clean));
        $clean = preg_replace('/\b(at|for|around|approximately|approx|barabar|ke|exactly|exact|equal to|equals|equal)\s*\$?\s*\d{1,6}/u', '', $clean);
        $clean = preg_replace('/\d{1,6}\s*(rs|\$)?\s*\b(at|for|around|approximately|approx|barabar|ke|exactly|exact|equal to|equals|equal)\b/u', '', $clean);

        $result['price_operator'] = $price_operator;
        $result['price_value'] = $price_value;
        $result['keywords'] = ('' !== $clean) ? $clean : null;

        if (!empty($result['keywords']) && !empty($result['price_value'])) {
            $result['is_keyword_with_price'] = true;
        }
        return $result;
    }

    /**
     * Handle incoming REST API query.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response
     */
    public function handle_query($request)
    {
        $message = sanitize_text_field($request->get_param('message') ?? '');
        if (empty($message)) {
            return rest_ensure_response(array('error' => 'Empty message'));
        }

        $parsed = $this->parse_user_message($message);
        $keywords = $parsed['keywords'];

        $opts = $this->naviaipr_opts;

        if ($parsed['is_greeting']) {
            $key_words_arr = explode(',', $opts['greeting_msg']);
            $reply = sprintf(
                'Hi there! 👋<br>I can help you find products. Try:<br>• "<span>%1$s</span>" - Search by name<br>• "<span>%2$s</span>" or "<span>$%2$s</span>" - Products at exact price<br>• "<span>%3$s</span>" - Protein at 60<br>• "<span>%4$s</span>" - Price range filter<br>What are you looking for?',
                esc_html(trim($key_words_arr[0] ?? '')),
                esc_html(trim($key_words_arr[1] ?? '')),
                esc_html(trim($key_words_arr[2] ?? '')),
                esc_html(trim($key_words_arr[3] ?? ''))
            );
            return rest_ensure_response(
                array(
                    'reply' => $reply,
                    'reply_html' => wp_kses_post($reply),
                    'products' => array(),
                )
            );
        }

        if (!class_exists('WC_Product_Query')) {
            return rest_ensure_response(array('error' => 'WooCommerce not active'));
        }

        $limit = intval($opts['max_results']);
        $args = array(
            'limit' => -1,
            'status' => 'publish',
            'stock_status' => 'instock',
            'return' => 'ids',
            'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
                array(
                    'taxonomy' => 'product_cat',
                    'field' => 'slug',
                    'terms' => array('free-samples', 'do-not-use'),
                    'operator' => 'NOT IN',
                ),
            ),
        );

        $categories = get_terms(
            array(
                'taxonomy' => 'product_cat',
                'hide_empty' => false,
                'orderby' => 'name',
                'order' => 'asc',
            )
        );

        $cat = '';
        $best_score = 0;

        foreach ($categories as $category) {
            $cat_name = strtolower(str_replace('-', ' ', $category->slug));
            $keyword_lower = strtolower($parsed['keywords'] ?? '');

            // Score 1: Category is the ENTIRE query (or near entire).
            // e.g. "plant based" or "plant based products".
            $stripped = preg_replace('/\b(show|products?|items?|list|me|all)\b/i', '', $keyword_lower);
            $stripped = trim(preg_replace('/\s+/', ' ', $stripped));

            if ($cat_name === $stripped) {
                $score = 100; // Exact match after stripping filler words.
            } elseif (strpos($keyword_lower, $cat_name . ' ') === 0) {
                // Score 2: Category appears at START of query.
                $score = 70;
            } else {
                // Score 3: Category covers more than 50% of the query word count.
                $cat_words = str_word_count($cat_name);
                $query_words = str_word_count($keyword_lower);
                $ratio = $query_words > 0 ? $cat_words / $query_words : 0;

                // Only count it if regex actually matches.
                if (preg_match('/\b' . preg_quote($cat_name, '/') . '\b/iu', $keyword_lower) && $ratio >= 0.5) {
                    $score = 40;
                } else {
                    $score = 0;
                }
            }

            if ($best_score < $score) {
                $best_score = $score;
                $cat = $category->slug;
            }
        }

        // Only trust the category match if confidence is high enough.
        if ($best_score < 70) {
            $cat = '';
        }

        if (!empty($keywords)) {
            if (!empty($cat)) {
                $args['category'] = array($cat);
            } else {
                $args['s'] = $keywords;
            }
        }

        $args['meta_query'] = array('relation' => 'AND'); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
        $args['meta_query'][] = array(
            'key' => '_price',
            'value' => 0,
            'compare' => '>',
            'type' => 'NUMERIC',
        );

        $query = new WC_Product_Query($args);
        $product_ids = $query->get_products();
        $items = array();

        foreach ($product_ids as $pid) {
            $product = wc_get_product($pid);
            if (!$product) {
                continue;
            }
            $current_price = floatval($product->get_price());
            if ($current_price <= 0) {
                continue;
            }

            if (!empty($parsed['price_value']) && !empty($parsed['price_operator'])) {
                $pv = floatval($parsed['price_value']);
                switch ($parsed['price_operator']) {
                    case '<':
                        if ($current_price > $pv) {
                            continue 2;
                        }
                        break;
                    case '>':
                        if ($current_price < $pv) {
                            continue 2;
                        }
                        break;
                    case '=':
                        if ((float) $current_price !== (float) $pv) {
                            continue 2;
                        }
                        break;
                }
            }

            $excerpt = wp_strip_all_tags($product->get_short_description());
            if (mb_strlen($excerpt) > 80) {
                $excerpt = mb_substr($excerpt, 0, 80) . '...';
            }
            $items[] = array(
                'id' => $product->get_id(),
                'name' => $product->get_name(),
                'price' => $current_price,
                'url' => get_permalink($product->get_id()),
                'image' => wp_get_attachment_image_url($product->get_image_id(), 'thumbnail') ? wp_get_attachment_image_url($product->get_image_id(), 'thumbnail') : '',
                'excerpt' => $excerpt,
            );
            if (count($items) >= $limit) {
                break;
            }
        }

        $no_exact_match = false;
        if (empty($items)) {
            $no_exact_match = true;
            if ($parsed['is_keyword_with_price']) {
                $fm = sprintf(
                    'No products found for "%1$s" at $%2$s. Try adjusting your search.',
                    esc_html($parsed['keywords']),
                    esc_html($parsed['price_value'])
                );
                return rest_ensure_response(
                    array(
                        'reply' => $fm,
                        'reply_html' => '<div style="padding:20px;text-align:center;color:#fbaeb0;">' . $fm . '</div>',
                        'products' => array(),
                        'no_exact_match' => true,
                    )
                );
            }
            $fallback_query = new WC_Product_Query(
                array(
                    'limit' => $limit,
                    'status' => 'publish',
                    'stock_status' => 'instock',
                    'orderby' => 'rand',
                    'order' => 'DESC',
                )
            );
            foreach ($fallback_query->get_products() as $random_product) {
                $excerpt = wp_strip_all_tags($random_product->get_short_description());
                if (mb_strlen($excerpt) > 80) {
                    $excerpt = mb_substr($excerpt, 0, 80) . '...';
                }
                $items[] = array(
                    'id' => $random_product->get_id(),
                    'name' => $random_product->get_name(),
                    'price' => floatval($random_product->get_price()),
                    'url' => get_permalink($random_product->get_id()),
                    'image' => wp_get_attachment_image_url($random_product->get_image_id(), 'thumbnail') ? wp_get_attachment_image_url($random_product->get_image_id(), 'thumbnail') : '',
                    'excerpt' => $excerpt,
                );
            }
        } elseif ($parsed['is_sort']) {
            'high' === $parsed['sort_opt'] ? usort(
                $items,
                function ($a, $b) {
                    return $b['price'] <=> $a['price'];
                }
            ) : usort(
                $items,
                function ($a, $b) {
                    return $a['price'] <=> $b['price'];
                }
            );
        } else {
            usort(
                $items,
                function ($a, $b) {
                    return $a['price'] <=> $b['price'];
                }
            );
        }

        return rest_ensure_response(
            array(
                'reply' => wp_strip_all_tags($this->build_text_from_items($items, $message, $no_exact_match)),
                'reply_html' => wp_kses_post($this->build_html_from_items($items, $no_exact_match, $parsed)),
                'products' => $items,
                'no_exact_match' => $no_exact_match,
            )
        );
    }

    /**
     * Build plain-text reply from product items.
     *
     * @param array  $items          Product list.
     * @param string $message        Original query.
     * @param bool   $no_exact_match Whether exact match was found.
     * @return string
     */
    private function build_text_from_items($items, $message = '', $no_exact_match = false)
    {
        if (empty($items)) {
            return 'Sorry — I could not find matching products.';
        }
        if ($no_exact_match) {
            return 'Sorry, no exact matches. Here are some popular alternatives:';
        }
        $top = $items[0];
        return sprintf('Try %1$s — now $%2$s. Buy: %3$s', $top['name'], $top['price'], $top['url']);
    }

    /**
     * Build HTML reply from product items.
     *
     * @param array      $items          Product list.
     * @param bool       $no_exact_match Whether exact match was found.
     * @param array|null $parsed         Parsed message data.
     * @return string
     */
    private function build_html_from_items($items, $no_exact_match = false, $parsed = null)
    {
        if (empty($items)) {
            return '<div>No matching products found.</div>';
        }
        $html = '';
        if ($no_exact_match) {
            $html .= '<div style="margin-bottom:8px;font-weight:600">No exact matches found.<br>Here are some popular items:</div>';
        } elseif (!empty($parsed['is_keyword_with_price'])) {
            $html .= '<div style="margin-bottom:8px;font-weight:600">' . sprintf(
                'Found %1$d "%2$s" products at $%3$s:',
                count($items),
                $parsed['keywords'],
                $parsed['price_value']
            ) . '</div>';
        } elseif (!empty($parsed['is_price_only'])) {
            $html .= '<div style="margin-bottom:8px;font-weight:600">' . sprintf(
                'Products at $%1$s:',
                $parsed['price_value']
            ) . '</div>';
        } elseif (!empty($parsed['keywords']) && empty($parsed['price_value'])) {
            $html .= '<div style="margin-bottom:8px;font-weight:600">' . sprintf(
                'Found %1$d products matching "%2$s":',
                count($items),
                $parsed['keywords']
            ) . '</div>';
        }

        $chat_layout = esc_attr($this->naviaipr_opts['chat_layout']);

        foreach ($items as $item) {
            $img = esc_url($item['image'] ? $item['image'] : wc_placeholder_img_src());
            $html .= '<div class="pb-product ' . $chat_layout . '-layout"><img src="' . $img . '" alt="' . esc_attr($item['name']) . '"><div style="flex:1"><div class="title">' . esc_html($item['name']) . '</div><div class="price">$' . esc_html($item['price']) . '</div>';
            if (!empty($item['excerpt'])) {
                $html .= '<div class="excerpt">' . esc_html($item['excerpt']) . '</div>';
            }
            $html .= '</div><div><a class="pb-buy" href="' . esc_url($item['url']) . '" target="_blank" rel="noopener">Buy Now</a></div></div>';
        }
        return $html;
    }
}
