<?php
/**
 * Core search functionality
 *
 * Main search function and mock mode detection.
 */

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

/**
 * Check if developer-only mock mode is enabled.
 *
 * Keeping this in one helper makes it easier to adjust the detection logic
 * in the future without touching all call sites.
 *
 * @return bool
 */
function psfa_is_mock_mode_enabled() {
    return defined('PSFA_MOCK_MODE') && PSFA_MOCK_MODE;
}

/**
 * Search Amazon products using PA-API v5
 *
 * NOTE: For development and demos, you can enable a private "mock mode"
 * by defining PSFA_MOCK_MODE in wp-config.php:
 *
 *   define( 'PSFA_MOCK_MODE', true );
 *
 * When enabled, the function will load products from mock.json instead
 * of calling Amazon PA-API. This path is intentionally hidden from UI so
 * end users of the plugin do not see or use it by default.
 */
function psfa_amazon_search_products($search_term, $category, $price_min, $price_max, $page = 1, $sort = '', $brand = '', $condition = '', $merchant = '', $has_deal = '', $min_rating = 0, $min_saving_percent = 0, $availability = '') {
    // Private developer-only mock mode (no API call, no credentials required).
    if (psfa_is_mock_mode_enabled()) {
        $mock_path = PSFA_PLUGIN_DIR . 'mock.json';

        if (!file_exists($mock_path)) {
            return ['error' => 'Mock data file not found. Expected at: ' . esc_html($mock_path)];
        }

        $mock_raw = file_get_contents($mock_path);
        if ($mock_raw === false) {
            return ['error' => 'Unable to read mock data file.'];
        }

        $mock_data = json_decode($mock_raw, true);
        if (!is_array($mock_data)) {
            return ['error' => 'Invalid mock data format. Expected a JSON array of products.'];
        }

        // Build items in the same shape as PA-API response.
        $items = [];
        foreach ($mock_data as $product) {
            // Build DealDetails for OffersV2 if deal data exists
            $deal_details = null;
            if (!empty($product['dealCountdown']) || !empty($product['dealBadge']) || !empty($product['isPrimeExclusive'])) {
                $deal_details = [];
                if (!empty($product['dealBadge'])) {
                    $deal_details['Badge'] = $product['dealBadge'];
                }
                if (!empty($product['dealCountdown']['end_time'])) {
                    $deal_details['EndTime'] = $product['dealCountdown']['end_time'];
                }
                if (!empty($product['isPrimeExclusive'])) {
                    $deal_details['AccessType'] = 'PRIME_EXCLUSIVE';
                }
            }

            // Build OffersV2 listing
            $v2_listing = [
                'Price' => [
                    'Money' => [
                        'DisplayAmount' => $product['price'] ?? '',
                    ],
                    'Savings' => [
                        'Percentage' => $product['savingPercent'] ?? 0,
                    ],
                    'SavingBasis' => [
                        'Money' => [
                            'DisplayAmount' => $product['savingBasis'] ?? '',
                        ],
                    ],
                ],
                'DeliveryInfo' => [
                    'IsPrimeEligible'  => $product['isPrime'] ?? false,
                    'IsFreeShipping'   => $product['isFreeShipping'] ?? false,
                ],
                'Condition' => [
                    'Value' => $product['condition'] ?? 'New',
                ],
                'MerchantInfo' => [
                    'Name' => $product['merchant'] ?? '',
                ],
                'Availability' => [
                    'Type' => $product['availabilityType'] ?? 'IN_STOCK',
                    'Message' => $product['availabilityMessage'] ?? 'In Stock',
                ],
            ];

            if ($deal_details) {
                $v2_listing['DealDetails'] = $deal_details;
            }

            $items[] = [
                'ASIN'          => $product['asin'] ?? '',
                'DetailPageURL' => $product['url'] ?? '#',
                'ItemInfo'      => [
                    'Title'     => [
                        'DisplayValue' => $product['title'] ?? '(No Title)',
                    ],
                    'ByLineInfo' => [
                        'Brand' => [
                            'DisplayValue' => $product['brand'] ?? '',
                        ],
                    ],
                ],
                'Images'        => [
                    'Primary' => [
                        'Large' => [
                            'URL' => $product['img'] ?? '',
                        ],
                    ],
                ],
                'Offers'        => [
                    'Listings' => [
                        [
                            'Price' => [
                                'DisplayAmount' => $product['price'] ?? '',
                            ],
                            'SavingBasis' => [
                                'DisplayAmount' => $product['savingBasis'] ?? '',
                            ],
                            'DeliveryInfo' => [
                                'IsPrimeEligible'  => $product['isPrime'] ?? false,
                                'IsFreeShipping'   => $product['isFreeShipping'] ?? false,
                            ],
                            'Condition' => [
                                'Value' => $product['condition'] ?? 'New',
                            ],
                            'MerchantInfo' => [
                                'Name' => $product['merchant'] ?? '',
                            ],
                            'Availability' => [
                                'Type' => $product['availabilityType'] ?? 'IN_STOCK',
                                'Message' => $product['availabilityMessage'] ?? 'In Stock',
                            ],
                        ],
                    ],
                ],
                'OffersV2' => [
                    'Listings' => [$v2_listing],
                ],
                'BrowseNodeInfo' => [
                    'WebsiteSalesRank' => [
                        'SalesRank' => $product['salesRank'] ?? 0,
                    ],
                ],
                'CustomerReviews' => [
                    'StarRating' => [
                        'Value' => $product['rating'] ?? 0,
                    ],
                    'Count' => $product['reviewCount'] ?? 0,
                ],
                // Mock-only fields
                '_mockCategory' => $product['category'] ?? 'Electronics',
                'hasDeal' => $product['hasDeal'] ?? false,
            ];
        }

        // Determine effective search term: function argument or URL query (?q or ?query).
        // Note: These are read-only GET parameters for search filtering, not form submissions.
        // Nonce verification is not required for read-only GET parameters that don't modify data.
        $effective_term = trim((string) $search_term);
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET parameter for search query.
        if ($effective_term === '' && isset($_GET['q'])) {
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET parameter for search query.
            $effective_term = sanitize_text_field(wp_unslash($_GET['q']));
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET parameter for search query.
        } elseif ($effective_term === '' && isset($_GET['query'])) {
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET parameter for search query.
            $effective_term = sanitize_text_field(wp_unslash($_GET['query']));
        }

        // If a search term is provided, filter mock items by title or brand (case-insensitive).
        if ($effective_term !== '') {
            $needle = function_exists('mb_strtolower')
                ? mb_strtolower($effective_term)
                : strtolower($effective_term);

            $items = array_values(array_filter($items, function ($item) use ($needle) {
                $title = $item['ItemInfo']['Title']['DisplayValue'] ?? '';
                $brand = $item['ItemInfo']['ByLineInfo']['Brand']['DisplayValue'] ?? '';

                $haystack_title = function_exists('mb_strtolower')
                    ? mb_strtolower($title)
                    : strtolower($title);
                $haystack_brand = function_exists('mb_strtolower')
                    ? mb_strtolower($brand)
                    : strtolower($brand);

                return (false !== strpos($haystack_title, $needle)) ||
                       (false !== strpos($haystack_brand, $needle));
            }));
        }

        // Apply brand filter in mock mode
        if (!empty($brand)) {
            $normalize_brand = function($b) {
                $b = trim($b);
                $b = preg_replace('/[®™©]/', '', $b);
                $b = preg_replace('/\s+/', ' ', $b);
                return strtolower($b);
            };
            $normalized_brand = $normalize_brand($brand);
            $items = array_filter($items, function($item) use ($normalized_brand, $normalize_brand) {
                $item_brand = $item['ItemInfo']['ByLineInfo']['Brand']['DisplayValue'] ?? '';
                if (empty($item_brand)) {
                    return false;
                }
                $normalized_item_brand = $normalize_brand($item_brand);
                return $normalized_item_brand === $normalized_brand;
            });
            $items = array_values($items);
        }
        
        // Apply price filter in mock mode
        if ($price_min > 0 || $price_max > 0) {
            $items = array_filter($items, function($item) use ($price_min, $price_max) {
                $price_str = psfa_get_item_price_display($item);
                if (empty($price_str)) {
                    // If no price and min is set, exclude item
                    return $price_min == 0;
                }
                $price = (float) psfa_extract_price($price_str);
                if ($price == 0.0 && $price_min > 0.0) {
                    return false;
                }
                $pass_min = ($price_min == 0 || $price >= $price_min);
                $pass_max = ($price_max == 0 || $price <= $price_max);
                return $pass_min && $pass_max;
            });
            $items = array_values($items);
        }
        
        // Apply Category filter in mock mode
        if (!empty($category) && $category !== 'All') {
            $items = array_filter($items, function($item) use ($category) {
                $item_category = $item['_mockCategory'] ?? '';
                return strcasecmp($item_category, $category) === 0;
            });
            $items = array_values($items);
        }

        // Apply Condition filter in mock mode (New, Used, Refurbished, Collectible)
        if (!empty($condition) && in_array($condition, ['New', 'Used', 'Refurbished', 'Collectible'], true)) {
            $items = array_filter($items, function($item) use ($condition) {
                $item_condition = psfa_get_item_condition($item);
                if (empty($item_condition)) {
                    $item_condition = 'New'; // Default to New
                }
                return strcasecmp($item_condition, $condition) === 0;
            });
            $items = array_values($items);
        }

        // Apply Merchant filter in mock mode (Amazon only)
        if (!empty($merchant) && $merchant === 'Amazon') {
            $items = array_filter($items, function($item) {
                $item_merchant = psfa_get_item_merchant($item);
                return strcasecmp($item_merchant, 'Amazon') === 0 || strcasecmp($item_merchant, 'Amazon.com') === 0;
            });
            $items = array_values($items);
        }

        // Apply Has Deal filter in mock mode (V2: DealDetails exists)
        if (!empty($has_deal)) {
            $items = array_filter($items, function($item) {
                // V2: Check if DealDetails exists
                $deal = $item['OffersV2']['Listings'][0]['DealDetails'] ?? null;
                if ($deal) {
                    return true;
                }
                // Fallback: check hasDeal flag in mock data
                return !empty($item['hasDeal']);
            });
            $items = array_values($items);
        }

        // Apply minimum rating filter in mock mode (1-5 stars)
        if ($min_rating > 0 && $min_rating <= 5) {
            $items = array_filter($items, function($item) use ($min_rating) {
                $rating = (float) ($item['CustomerReviews']['StarRating']['Value'] ?? 0);
                return $rating >= $min_rating;
            });
            $items = array_values($items);
        }

        // Apply minimum savings filter in mock mode
        if ($min_saving_percent > 0) {
            $items = array_filter($items, function($item) use ($min_saving_percent) {
                $saving = psfa_get_item_saving_basis($item);
                $price = psfa_get_item_price_display($item);
                if (empty($saving) || empty($price)) {
                    return false;
                }
                // Calculate saving percent
                $orig = (float) preg_replace('/[^0-9.]/', '', $saving);
                $curr = (float) preg_replace('/[^0-9.]/', '', $price);
                if ($orig <= 0) {
                    return false;
                }
                $percent = round((($orig - $curr) / $orig) * 100);
                return $percent >= $min_saving_percent;
            });
            $items = array_values($items);
        }

        // Apply sort in mock mode to mirror PA-API behaviour where possible.
        if (!empty($sort)) {
            usort($items, function ($a, $b) use ($sort) {
                // Extract numeric price (treat empty or invalid as very large so they sort last for LowToHigh)
                $priceA = psfa_get_item_price_display($a);
                $priceB = psfa_get_item_price_display($b);

                $toNumber = function ($price) {
                    if (!is_string($price) || $price === '') {
                        return null;
                    }
                    // Extract FIRST price only (handles "$22.99 ($11.50 / count)" format)
                    // Stop at opening parenthesis or slash to avoid per-unit prices
                    $price = preg_replace('/\s*[\(\/].*$/', '', $price);
                    // Strip currency symbols and commas, keep only digits and decimal
                    $clean = preg_replace('/[^0-9.]/', '', $price);
                    if ($clean === '' || !is_numeric($clean)) {
                        return null;
                    }
                    return (float) $clean;
                };

                $numA = $toNumber($priceA);
                $numB = $toNumber($priceB);

                // Handle cases where one or both prices are missing
                if ($numA === null && $numB === null) {
                    return 0;
                }
                if ($numA === null) {
                    return 1; // A goes after B
                }
                if ($numB === null) {
                    return -1; // B goes after A
                }

                if ($sort === 'Price:LowToHigh') {
                    return $numA <=> $numB;
                }
                if ($sort === 'Price:HighToLow') {
                    return $numB <=> $numA;
                }

                // For other sort modes (Relevance, NewestArrivals, etc.), keep original order.
                return 0;
            });
        }

        // Simulate API pagination: 10 items per page, just like live PA-API.
        $per_page      = 10;
        $total_results = count($items);
        $offset        = max(0, ($page - 1) * $per_page);

        $paged_items = array_slice($items, $offset, $per_page);

        return [
            'SearchResult' => [
                'Items'            => $paged_items,
                'TotalResultCount' => $total_results,
            ],
        ];
    }
    
    // @codeCoverageIgnoreStart
    // Live mode – requires valid PA-API credentials.
    require_once PSFA_PLUGIN_DIR . 'src/core/api/class-api-factory.php';
    
    $client = PSFA_API_Factory::get_client();
    $is_configured = $client->is_configured();
    
    if (is_wp_error($is_configured)) {
        return array('error' => $is_configured->get_error_message());
    }

    // Build SearchItems payload from structured parameters.
    $search_params = array(
        'search_term'        => $search_term,
        'category'           => $category,
        'price_min'          => $price_min,
        'price_max'          => $price_max,
        'page'               => $page,
        'sort'               => $sort,
        'brand'              => $brand,
        'condition'          => $condition,
        'merchant'           => $merchant,
        'has_deal'           => $has_deal,
        'min_rating'         => $min_rating,
        'min_saving_percent' => $min_saving_percent,
        'availability'       => $availability,
    );
    $payload = psfa_build_search_payload( $search_params, $client->get_associate_tag() );

    if (isset($payload['error'])) {
        // Preserve previous behaviour when keywords/category are missing.
        return array('error' => $payload['error']);
    }

    // Check search cache before hitting Amazon API.
    $cached = psfa_get_cached_search( $payload );
    if ( null !== $cached ) {
        return $cached;
    }

    $result = $client->search_items($payload);

    // Note: The Creators API (OAuth) does NOT return CustomerReviews data for either
    // searchItems or getItems, despite accepting the resource names without error.
    // Ratings/review counts are only available on the AWSSig4 (PA-API v5) auth path.
    // The CustomerReviews resources are kept in the resource lists so that if Amazon
    // adds support in the future, it works automatically.

    // Cache successful responses only.
    // Pass original search params so client-side-only filters
    // (has_deal, sort when skipped) are captured in metadata.
    if ( ! isset( $result['error'] ) ) {
        psfa_set_cached_search( $payload, $result, $search_params );
    }

    return $result;
}
// @codeCoverageIgnoreEnd

