<?php
/**
 * React Frontend Container
 * 
 * Outputs a div container with initial data for the React frontend app.
 * This enables client-side hydration with server-provided initial state.
 */

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

// Include required helper files
require_once PSFA_PLUGIN_DIR . 'src/core/search/view-helpers.php';
require_once PSFA_PLUGIN_DIR . 'src/core/shared/customization-helpers.php';
require_once PSFA_PLUGIN_DIR . 'src/core/search/helpers.php';

/**
 * Determine if category banner should be hidden
 * Priority: URL param > shortcode > admin setting
 *
 * @param array $shortcode_atts The shortcode attributes
 * @return bool
 */
function psfa_should_hide_category_banner($shortcode_atts) {
    // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Public URL parameter
    if (isset($_GET['hide_category_banner'])) {
        return in_array($_GET['hide_category_banner'], array('true', '1', 'yes'), true);
    }
    // phpcs:enable WordPress.Security.NonceVerification.Recommended
    
    if (isset($shortcode_atts['hide_category_banner']) && $shortcode_atts['hide_category_banner'] !== '') {
        return in_array($shortcode_atts['hide_category_banner'], array('true', '1', 'yes', true, 1), true);
    }
    
    return in_array(get_option('psfa_hide_category_banner', '0'), array('1', 1, true, 'true'), true);
}

/**
 * Render the React frontend container with initial data.
 *
 * When the SSR skeleton file is present (generated by `npm run build:frontend-ssr:render`),
 * the container is pre-populated with server-rendered HTML that React hydrates on the client.
 * This eliminates the loading spinner flash and provides immediate visible content.
 *
 * When the skeleton file is absent (e.g. fresh clone before build), it falls back to the
 * loading spinner so the plugin continues to work without the SSR step.
 *
 * @param array $search_data The search data from the handler
 * @param array $shortcode_atts The shortcode attributes
 * @return string The HTML container
 */
function psfa_render_react_container($search_data, $shortcode_atts) {
    // Build the config object for React
    $config = psfa_build_frontend_config($search_data, $shortcode_atts);

    // Encode config as JSON for data attribute
    $config_json = wp_json_encode($config, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);

    // Generate unique container ID
    $container_id = 'psfa-frontend-root-' . wp_unique_id();

    // Load pre-rendered SSR skeleton if available (generated by build pipeline)
    $ssr_skeleton_file = PSFA_PLUGIN_DIR . 'src/core/frontend/ssr/skeleton.php';
    $has_ssr = file_exists($ssr_skeleton_file);
    if ($has_ssr && ! function_exists('psfa_get_ssr_skeleton')) {
        try {
            require_once $ssr_skeleton_file;
        } catch (\Throwable $e) {
            // Corrupt skeleton artifact - degrade gracefully to spinner fallback
            $has_ssr = false;
        }
    }

    // Appearance classes on the container - outside React's hydration scope to avoid #418 mismatch
    $appearance_classes = array(
        'psfa-base-' . sanitize_html_class($config['settings']['baseColor']),
        'psfa-accent-' . sanitize_html_class($config['settings']['accentColor']),
        'psfa-style-' . sanitize_html_class($config['settings']['style']),
    );
    if (!empty($config['settings']['darkMode'])) {
        $appearance_classes[] = 'psfa-dark-mode';
    }
    $appearance_class_str = implode(' ', $appearance_classes);

    // Inline styles for radius and font
    $inline_styles = '--radius: ' . esc_attr($config['settings']['radius']) . 'rem;';
    if (!empty($config['settings']['font'])) {
        require_once PSFA_PLUGIN_DIR . 'src/core/shared/theme-manager.php';
        $font_family = psfa_get_font_family($config['settings']['font']);
        if (!empty($font_family)) {
            $inline_styles .= ' --font-sans: ' . $font_family . ';';
        }
    }

    ob_start();
    ?>
    <div
        id="<?php echo esc_attr($container_id); ?>"
        class="psfa-frontend-root <?php echo esc_attr($appearance_class_str); ?>"
        style="<?php echo esc_attr($inline_styles); ?>"
        data-config="<?php echo esc_attr($config_json); ?>"
        <?php if ($has_ssr && function_exists('psfa_get_ssr_skeleton')) : ?>data-ssr="1"<?php endif; ?>
    ><?php
    if ($has_ssr && function_exists('psfa_get_ssr_skeleton')) {
        // Output pre-rendered skeleton - React will hydrate this on the client.
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Trusted build artifact, not user input
        echo psfa_get_ssr_skeleton();
    } else {
        // Fallback: loading spinner while React initialises
        ?>
        <div class="psfa-loading-placeholder" style="text-align: center; padding: 40px;">
            <div style="display: inline-block; width: 40px; height: 40px; border: 3px solid #f3f3f3; border-top: 3px solid #3498db; border-radius: 50%; animation: psfa-spin 1s linear infinite;"></div>
            <p style="margin-top: 16px; color: #666;">Loading search...</p>
        </div>
        <style>@keyframes psfa-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }</style>
        <?php
    }
    ?></div>
    <?php
    return ob_get_clean();
}

/**
 * Build the frontend config object for React
 *
 * @param array $search_data The search data from the handler
 * @param array $shortcode_atts The shortcode attributes
 * @return array The config object
 */
function psfa_build_frontend_config($search_data, $shortcode_atts) {
    // Get admin settings
    $admin_settings = get_option('psfa_settings', array());
    
    // Determine if user is premium
    $is_premium = psfa_is_premium();
    
    // Get appearance settings - multi-axis model with premium gating in PHP
    require_once PSFA_PLUGIN_DIR . 'src/core/shared/theme-manager.php';
    $base_color = psfa_get_selected_base_color();
    $accent_color = psfa_get_selected_accent_color();
    $style = psfa_get_selected_style();
    $radius = psfa_get_radius();
    $is_dark = psfa_is_dark_mode();
    $font = psfa_get_selected_font();
    // Get display mode (grid/list) - list is premium only
    $display_mode = get_option('psfa_display_mode', 'grid');
    
    
    // Get customization settings (premium only)
    $button_text = 'Buy on Amazon';
    if ($is_premium && function_exists('psfa_get_button_text')) {
        $button_text = psfa_get_button_text();
    }
    $hide_amazon_icon = $is_premium && get_option('psfa_hide_amazon_icon', '0') === '1';
    
    // Get display options with priority: URL param > shortcode attr > global option
    // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Public URL parameters for demo preview
    if (isset($_GET['hide_search'])) {
        $hide_search = in_array($_GET['hide_search'], array('true', '1', 'yes'), true);
    } elseif (isset($shortcode_atts['hide_search']) && $shortcode_atts['hide_search'] !== '') {
        $hide_search = in_array($shortcode_atts['hide_search'], array('true', '1', 'yes', true, 1), true);
    } else {
        $hide_search = in_array(get_option('psfa_hide_search', '0'), array('1', 1, true, 'true'), true);
    }
    
    if (isset($_GET['hide_filters'])) {
        $hide_filters = in_array($_GET['hide_filters'], array('true', '1', 'yes'), true);
    } elseif (isset($shortcode_atts['hide_filters']) && $shortcode_atts['hide_filters'] !== '') {
        $hide_filters = in_array($shortcode_atts['hide_filters'], array('true', '1', 'yes', true, 1), true);
    } else {
        $hide_filters = in_array(get_option('psfa_hide_filters', '0'), array('1', 1, true, 'true'), true);
    }
    // phpcs:enable WordPress.Security.NonceVerification.Recommended

    // Get pagination with priority: URL param > shortcode attr > default
    // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Public URL parameter for demo preview
    $valid_pagination = array( 'infinite', 'button', 'none' );
    if ( isset( $_GET['pagination'] ) && in_array( sanitize_text_field( wp_unslash( $_GET['pagination'] ) ), $valid_pagination, true ) ) {
        $pagination = sanitize_text_field( wp_unslash( $_GET['pagination'] ) );
    } elseif ( isset( $shortcode_atts['pagination'] ) ) {
        $pagination = $shortcode_atts['pagination'];
    } else {
        $pagination = 'infinite';
    }
    $count = isset( $_GET['count'] ) ? absint( $_GET['count'] ) : ( isset( $shortcode_atts['count'] ) ? absint( $shortcode_atts['count'] ) : 20 );
    // phpcs:enable WordPress.Security.NonceVerification.Recommended

    // Build settings object
    // Include shortcode defaults so React can preserve them on clearAll
    $settings = array(
        'baseColor'         => $base_color,
        'accentColor'       => $accent_color,
        'style'             => $style,
        'radius'            => $radius,
        'darkMode'          => $is_dark,
        'font'              => $font,
        'displayMode'       => $display_mode,
        'hideSearch'        => $hide_search,
        'hideFilters'       => $hide_filters,
        'pagination'        => $pagination,
        'count'             => $count,
        'buttonText'        => $button_text,
        'hideAmazonIcon'    => $hide_amazon_icon,
        // Shortcode defaults for clearAll to preserve
        'category'          => isset($shortcode_atts['category']) ? $shortcode_atts['category'] : '',
        'query'             => isset($shortcode_atts['query']) ? $shortcode_atts['query'] : '',
        'brand'             => isset($shortcode_atts['brand']) ? $shortcode_atts['brand'] : '',
        'sort'              => isset($shortcode_atts['sort']) ? $shortcode_atts['sort'] : '',
        'minPrice'          => isset($shortcode_atts['min_price']) ? floatval($shortcode_atts['min_price']) : 0,
        'maxPrice'          => isset($shortcode_atts['max_price']) ? floatval($shortcode_atts['max_price']) : 0,
        // Category banner visibility
        'hideCategoryBanner' => psfa_should_hide_category_banner($shortcode_atts),
    );
    
    // Transform products for React
    // filtered_results has structure: ['SearchResult']['Items']
    $products = array();
    $items = array();
    if (!empty($search_data['filtered_results']['SearchResult']['Items'])) {
        $items = $search_data['filtered_results']['SearchResult']['Items'];
    }
    foreach ($items as $item) {
        $products[] = psfa_transform_product_for_react($item, $is_premium);
    }

    // Apply count limit when pagination is 'none' (shortcode count="N" or URL ?count=N)
    if ( $pagination === 'none' && count( $products ) > $count ) {
        $products = array_slice( $products, 0, $count );
    }

    // Build filter options
    $filter_options = array(
        'brands'     => array(),
        // Creators API (OAuth) only supports 'New' condition filter.
        // PA-API (AWS Sig V4) supports New, Used, Refurbished, Collectible.
        'conditions' => psfa_is_oauth_auth_type()
            ? array(
                array('value' => 'New', 'label' => 'New'),
            )
            : array(
                array('value' => 'New', 'label' => 'New'),
                array('value' => 'Used', 'label' => 'Used'),
                array('value' => 'Refurbished', 'label' => 'Refurbished'),
                array('value' => 'Collectible', 'label' => 'Collectible'),
            ),
        'sortOptions' => array(
            array('value' => 'Relevance', 'label' => 'Relevance'),
            array('value' => 'Price:LowToHigh', 'label' => 'Price: Low to High'),
            array('value' => 'Price:HighToLow', 'label' => 'Price: High to Low'),
            array('value' => 'AvgCustomerReviews', 'label' => 'Customer Reviews'),
            array('value' => 'NewestArrivals', 'label' => 'Newest Arrivals'),
        ),
    );
    
    // Extract brand options from filter data or from products
    if (!empty($search_data['filter_options']['brands'])) {
        foreach ($search_data['filter_options']['brands'] as $brand) {
            $filter_options['brands'][] = array('value' => $brand, 'label' => $brand);
        }
    }
    
    // Fallback 1: extract brands from transformed products
    if (empty($filter_options['brands']) && !empty($products)) {
        $unique_brands = array();
        foreach ($products as $product) {
            if (!empty($product['brand']) && !in_array($product['brand'], $unique_brands, true)) {
                $unique_brands[] = $product['brand'];
            }
        }
        sort($unique_brands);
        foreach ($unique_brands as $brand) {
            $filter_options['brands'][] = array('value' => $brand, 'label' => $brand);
        }
    }
    
    // Fallback 2: extract brands from raw API results (before client-side filtering)
    // This is needed when filters like min_saving_percent filter out all products
    if (empty($filter_options['brands']) && !empty($search_data['results']['SearchResult']['Items'])) {
        $unique_brands = array();
        foreach ($search_data['results']['SearchResult']['Items'] as $item) {
            $brand = $item['ItemInfo']['ByLineInfo']['Brand']['DisplayValue'] ?? '';
            if ($brand !== '' && !in_array($brand, $unique_brands, true)) {
                $unique_brands[] = $brand;
            }
        }
        sort($unique_brands);
        foreach ($unique_brands as $brand) {
            $filter_options['brands'][] = array('value' => $brand, 'label' => $brand);
        }
    }
    
    // Build initial state
    $initial_state = array(
        'query'            => isset($search_data['search_params']['query']) ? $search_data['search_params']['query'] : '',
        'category'         => isset($search_data['search_params']['category']) ? $search_data['search_params']['category'] : '',
        'products'         => $products,
        'pagesFetched'     => isset($search_data['pages_fetched']) ? $search_data['pages_fetched'] : 1,
        'totalCount'       => isset($search_data['total_unfiltered_count']) ? $search_data['total_unfiltered_count'] : count($products),
        'hasMore'          => $pagination === 'none' ? false : count($products) >= 10,
        'error'            => isset($search_data['error_message']) ? $search_data['error_message'] : null,
    );
    
    // Admin capabilities for add-on integration
    $is_admin = current_user_can('manage_options') && !is_admin();
    $is_mock_mode = defined('PSFA_MOCK_MODE') && PSFA_MOCK_MODE;
    
    // Addon availability for React-rendered buttons
    $addons = array(
        'productBoxes' => $is_admin && !$is_mock_mode && class_exists('PSFA_Product_Boxes'),
        'comparisonTables' => $is_admin && !$is_mock_mode && class_exists('PSFA_Comparison_Tables'),
    );
    // Addon AJAX config (only if addons are active)
    $addon_config = array();
    if ($addons['productBoxes']) {
        $addon_config['boxes'] = array(
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('psfa_boxes_nonce'),
            'i18n' => array(
                'add' => __('Box', 'paapi-product-search-for-amazon'),
                'added' => __('Added!', 'paapi-product-search-for-amazon'),
                'error' => __('Error', 'paapi-product-search-for-amazon'),
            ),
        );
    }
    if ($addons['comparisonTables']) {
        $addon_config['compare'] = array(
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('psfa_tables_nonce'),
            'i18n' => array(
                'add' => __('Compare', 'paapi-product-search-for-amazon'),
                'added' => __('Added!', 'paapi-product-search-for-amazon'),
                'error' => __('Error', 'paapi-product-search-for-amazon'),
            ),
        );
    }
    
    // Filter visibility settings
    $filter_visibility = get_option('psfa_filter_visibility', array(
        'brand' => true, 'price' => true, 'sort' => true, 'condition' => true,
        'merchant' => true, 'availability' => true, 'has_deal' => true, 'deals' => true,
    ));
    
    // Branding visibility (premium white-label)
    $show_branding = true;
    if (function_exists('psfa_show_branding')) {
        $show_branding = psfa_show_branding();
    }
    
    // Premium feature settings from admin
    $features = array(
        'dealBadge'      => true,
        'dealCountdown'  => true,
        'primeExclusive' => true,
        'lowStock'       => true,
        'bestseller'     => true,
        'buyboxWinner'   => false,
        'usedPrice'      => false,
    );
    if (function_exists('psfa_get_features')) {
        $saved_features = psfa_get_features();
        $features = array(
            'dealBadge'      => isset($saved_features['deal_badge']) ? $saved_features['deal_badge'] : true,
            'dealCountdown'  => isset($saved_features['deal_countdown']) ? $saved_features['deal_countdown'] : true,
            'primeExclusive' => isset($saved_features['prime_exclusive']) ? $saved_features['prime_exclusive'] : true,
            'lowStock'       => isset($saved_features['low_stock']) ? $saved_features['low_stock'] : true,
            'bestseller'     => true, // Always enabled for premium
            'buyboxWinner'   => isset($saved_features['buybox_winner']) ? $saved_features['buybox_winner'] : false,
            'usedPrice'      => isset($saved_features['used_price']) ? $saved_features['used_price'] : false,
        );
    }

    // Debug mode: add ?psfa_debug=1 to URL
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only debug flag
    $debug_mode = isset($_GET['psfa_debug']) && $_GET['psfa_debug'] === '1';

    // Max pages based on license (5 free, 999 premium, capped at Amazon's 10-page limit)
    $max_pages = min(psfa_get_max_pages(), 10);

    // Build final config
    return array(
        'ajaxUrl'          => admin_url('admin-ajax.php'),
        'nonce'            => wp_create_nonce('psfa_ajax_nonce'),
        'pluginUrl'        => PSFA_PLUGIN_URL,
        'isPremium'        => $is_premium,
        'isAdmin'          => $is_admin,
        'isMockMode'       => $is_mock_mode,
        'showBranding'     => $show_branding,
        'filterVisibility' => $filter_visibility,
        'features'         => $features,
        'maxPages'         => $max_pages,
        'addons'           => $addons,
        'addonConfig'      => $addon_config,
        'settings'         => $settings,
        'initialState'     => $initial_state,
        'filterOptions'    => $filter_options,
        'debugMode'        => $debug_mode,
    );
}

/**
 * Transform a product item for React consumption
 * Handles raw API structure from PA-API/Creators API
 *
 * @param array $item The product item from API (raw structure)
 * @param bool $is_premium Whether user has premium
 * @return array The transformed product
 */
function psfa_transform_product_for_react($item, $is_premium) {
    // Extract data from raw API structure
    $asin = $item['ASIN'] ?? '';
    $title = $item['ItemInfo']['Title']['DisplayValue'] ?? '';
    $url = $item['DetailPageURL'] ?? '#';
    $img = $item['Images']['Primary']['Large']['URL'] ?? '';
    $brand = $item['ItemInfo']['ByLineInfo']['Brand']['DisplayValue'] ?? '';
    
    // Price extraction - handle both OffersV2 (OAuth/Creators API) and Offers (PA-API) paths
    $listing = null;
    if (isset($item['OffersV2']['Listings'][0])) {
        $listing = $item['OffersV2']['Listings'][0];
    } elseif (isset($item['Offers']['Listings'][0])) {
        $listing = $item['Offers']['Listings'][0];
    }
    
    $price = '';
    $saving_basis = '';
    $saving_percent = 0;
    if ($listing) {
        // OffersV2 path: Price.Money.DisplayAmount
        if (isset($listing['Price']['Money']['DisplayAmount'])) {
            $price = $listing['Price']['Money']['DisplayAmount'];
        }
        // Legacy Offers path: Price.DisplayAmount
        elseif (isset($listing['Price']['DisplayAmount'])) {
            $price = $listing['Price']['DisplayAmount'];
        }
        
        // OffersV2 path: Price.SavingBasis.Money.DisplayAmount
        if (isset($listing['Price']['SavingBasis']['Money']['DisplayAmount'])) {
            $saving_basis = $listing['Price']['SavingBasis']['Money']['DisplayAmount'];
        }
        // Legacy Offers path: SavingBasis.DisplayAmount
        elseif (isset($listing['SavingBasis']['DisplayAmount'])) {
            $saving_basis = $listing['SavingBasis']['DisplayAmount'];
        }
        
        if (isset($listing['Price']['Savings']['Percentage'])) {
            $saving_percent = intval($listing['Price']['Savings']['Percentage']);
        }
    }
    
    // Rating
    $rating = 0;
    $review_count = 0;
    if (isset($item['CustomerReviews']['StarRating']['Value'])) {
        $rating = floatval($item['CustomerReviews']['StarRating']['Value']);
    }
    if (isset($item['CustomerReviews']['Count'])) {
        $review_count = intval($item['CustomerReviews']['Count']);
    }
    // Note: The Creators API (OAuth) does not return CustomerReviews data.
    // Ratings are only available on AWSSig4 (PA-API v5) auth path.

    // Prime eligibility: OffersV2 does NOT include DeliveryInfo (V1 Offers only).
    // isPrime will be false on OffersV2. Prime Exclusive deals still work via DealDetails.
    $is_prime = false;
    
    $product = array(
        'asin'          => $asin,
        'title'         => $title,
        'url'           => $url,
        'img'           => $img,
        'price'         => $price,
        'brand'         => $brand,
        'rating'        => $rating,
        'reviewCount'   => $review_count,
        'isPrime'       => $is_prime,
        'savingBasis'   => $saving_basis,
        'savingPercent'  => $saving_percent,
    );

    // Add premium fields
    if ($is_premium) {
        
        // Extract deal details from OffersV2
        $deal_details = $listing['DealDetails'] ?? null;
        $deal_badge = '';
        $deal_countdown = null;
        $is_prime_exclusive = false;
        
        // Check for deal countdown
        if ($deal_details && isset($deal_details['EndTime'])) {
            $end_time = strtotime($deal_details['EndTime']);
            $now = time();
            $time_left = $end_time - $now;
            
            if ($time_left > 0) {
                // Format time left for display
                $hours = floor($time_left / 3600);
                $minutes = floor(($time_left % 3600) / 60);
                $seconds = $time_left % 60;
                $time_left_display = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
                
                $deal_countdown = array(
                    'end_time' => $deal_details['EndTime'],
                    'time_left_seconds' => $time_left,
                    'time_left_display' => $time_left_display,
                );
                
                // Set deal badge for when countdown is disabled
                $deal_badge = 'Limited Time Deal';
            }
        }
        
        // Check deal badge from API (if not already set by countdown)
        if (empty($deal_badge) && $deal_details && isset($deal_details['Badge'])) {
            $badge = $deal_details['Badge'];
            // Filter out redundant badges (Prime handled separately, ENDS IN shows countdown)
            $filter_keywords = array('PRIME', 'WITH PRIME', 'PRIME EXCLUSIVE', 'PRIME EARLY ACCESS', 'ENDS IN');
            $badge_upper = strtoupper(trim($badge));
            $is_filtered = false;
            foreach ($filter_keywords as $keyword) {
                if (strpos($badge_upper, $keyword) !== false) {
                    $is_filtered = true;
                    break;
                }
            }
            if (!$is_filtered) {
                $deal_badge = $badge;
            }
        }
        
        // Check Prime Exclusive
        if ($listing) {
            if (isset($listing['ProgramEligibility']['IsPrimeExclusive']) && $listing['ProgramEligibility']['IsPrimeExclusive']) {
                $is_prime_exclusive = true;
            }
            // Also check DealDetails AccessType for Prime Exclusive
            if ($deal_details && isset($deal_details['AccessType']) && $deal_details['AccessType'] === 'PRIME_EXCLUSIVE') {
                $is_prime_exclusive = true;
            }
        }
        
        $product['dealBadge'] = $deal_badge;
        $product['dealCountdown'] = $deal_countdown;
        $product['isPrimeExclusive'] = $is_prime_exclusive;
        
        // Check stock scarcity from availability type
        $availability_type = $listing['Availability']['Type'] ?? '';
        $product['isStockScarce'] = ($availability_type === 'IN_STOCK_SCARCE');
        
        $product['salesRank'] = isset($item['BrowseNodeInfo']['WebsiteSalesRank']['SalesRank']) ? intval($item['BrowseNodeInfo']['WebsiteSalesRank']['SalesRank']) : 0;
    }
    
    return $product;
}

