<?php
/**
 * Coupon Selector Service for BoostBot.
 *
 * Smart coupon selection logic based on cart, conversation, and preferences.
 *
 * @package BoostBot
 * @since   1.3.0
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Class BoostBot_Coupon_Selector
 *
 * Provides intelligent coupon selection based on context.
 *
 * @since 1.3.0
 */
class BoostBot_Coupon_Selector {

    /**
     * Scoring weights.
     *
     * @var array
     */
    private const WEIGHTS = array(
        'eligibility'  => 100, // Must meet min/max cart requirements.
        'priority'     => 50,  // Preset-defined priority.
        'keyword'      => 30,  // Keyword match in conversation.
        'description'  => 20,  // Description relevance.
        'discount'     => 10,  // Higher discount value.
    );

    /**
     * Select the best coupon for the given context.
     *
     * @since 1.3.0
     * @param array  $available_coupons Array of coupon data from model.
     * @param float  $cart_total        Current cart total.
     * @param string $conversation      Conversation context text.
     * @return array|null Best coupon or null if none suitable.
     */
    public function select_coupon( array $available_coupons, float $cart_total, string $conversation = '' ): ?array {
        if ( empty( $available_coupons ) ) {
            return null;
        }

        $scored_coupons = array();

        foreach ( $available_coupons as $coupon ) {
            $score = $this->calculate_score( $coupon, $cart_total, $conversation );

            // Only consider eligible coupons (score > 0 means eligible).
            if ( $score > 0 ) {
                $scored_coupons[] = array(
                    'coupon' => $coupon,
                    'score'  => $score,
                );
            }
        }

        if ( empty( $scored_coupons ) ) {
            return null;
        }

        // Sort by score descending.
        usort( $scored_coupons, function ( $a, $b ) {
            return $b['score'] <=> $a['score'];
        } );

        return $scored_coupons[0]['coupon'];
    }

    /**
     * Get all eligible coupons sorted by score.
     *
     * @since 1.3.0
     * @param array  $available_coupons Array of coupon data.
     * @param float  $cart_total        Current cart total.
     * @param string $conversation      Conversation context.
     * @return array Scored and sorted coupons.
     */
    public function get_ranked_coupons( array $available_coupons, float $cart_total, string $conversation = '' ): array {
        $scored_coupons = array();

        foreach ( $available_coupons as $coupon ) {
            $score = $this->calculate_score( $coupon, $cart_total, $conversation );

            if ( $score > 0 ) {
                $coupon['score'] = $score;
                $scored_coupons[] = $coupon;
            }
        }

        // Sort by score descending.
        usort( $scored_coupons, function ( $a, $b ) {
            return $b['score'] <=> $a['score'];
        } );

        return $scored_coupons;
    }

    /**
     * Calculate score for a coupon.
     *
     * @since 1.3.0
     * @param array  $coupon       Coupon data.
     * @param float  $cart_total   Cart total.
     * @param string $conversation Conversation text.
     * @return float Calculated score (0 if ineligible).
     */
    private function calculate_score( array $coupon, float $cart_total, string $conversation ): float {
        $score = 0.0;

        // Check validity first.
        if ( isset( $coupon['is_valid'] ) && ! $coupon['is_valid'] ) {
            return 0.0;
        }

        // 1. Eligibility check (min/max cart total).
        if ( ! $this->is_eligible( $coupon, $cart_total ) ) {
            return 0.0; // Not eligible, score = 0.
        }

        $score += self::WEIGHTS['eligibility'];

        // 2. Priority score.
        $priority = isset( $coupon['priority'] ) ? (int) $coupon['priority'] : 0;
        $score   += $priority * ( self::WEIGHTS['priority'] / 10 );

        // 3. Keyword matching.
        if ( ! empty( $conversation ) && ! empty( $coupon['keywords'] ) ) {
            $keyword_score = $this->calculate_keyword_score( $coupon['keywords'], $conversation );
            $score        += $keyword_score * self::WEIGHTS['keyword'];
        }

        // 4. Description relevance.
        if ( ! empty( $conversation ) && ! empty( $coupon['description'] ) ) {
            $desc_score = $this->calculate_relevance_score( $coupon['description'], $conversation );
            $score     += $desc_score * self::WEIGHTS['description'];
        }

        // 5. Discount value bonus.
        if ( isset( $coupon['discount_amount'] ) && $coupon['discount_amount'] > 0 ) {
            $discount_score = min( $coupon['discount_amount'] / 100, 1 ); // Normalize to 0-1.
            $score         += $discount_score * self::WEIGHTS['discount'];
        }

        return $score;
    }

    /**
     * Check if coupon is eligible based on cart total.
     *
     * @since 1.3.0
     * @param array $coupon     Coupon data.
     * @param float $cart_total Cart total.
     * @return bool True if eligible.
     */
    private function is_eligible( array $coupon, float $cart_total ): bool {
        // Check minimum cart total.
        if ( ! empty( $coupon['min_cart_total'] ) && $cart_total < (float) $coupon['min_cart_total'] ) {
            return false;
        }

        // Check maximum cart total.
        if ( ! empty( $coupon['max_cart_total'] ) && $cart_total > (float) $coupon['max_cart_total'] ) {
            return false;
        }

        return true;
    }

    /**
     * Calculate keyword matching score.
     *
     * @since 1.3.0
     * @param string $keywords     Comma-separated keywords.
     * @param string $conversation Conversation text.
     * @return float Score between 0 and 1.
     */
    private function calculate_keyword_score( string $keywords, string $conversation ): float {
        $keyword_list = array_map( 'trim', explode( ',', $keywords ) );
        $keyword_list = array_filter( $keyword_list );

        if ( empty( $keyword_list ) ) {
            return 0.0;
        }

        $conversation_lower = mb_strtolower( $conversation );
        $matches            = 0;

        foreach ( $keyword_list as $keyword ) {
            if ( mb_strpos( $conversation_lower, mb_strtolower( $keyword ) ) !== false ) {
                $matches++;
            }
        }

        return $matches / count( $keyword_list );
    }

    /**
     * Calculate description relevance score.
     *
     * Simple word matching for relevance.
     *
     * @since 1.3.0
     * @param string $description  Coupon description.
     * @param string $conversation Conversation text.
     * @return float Score between 0 and 1.
     */
    private function calculate_relevance_score( string $description, string $conversation ): float {
        // Extract significant words (3+ chars).
        $desc_words = preg_split( '/\s+/', mb_strtolower( $description ) );
        $desc_words = array_filter( $desc_words, function ( $word ) {
            return mb_strlen( $word ) >= 3;
        } );

        if ( empty( $desc_words ) ) {
            return 0.0;
        }

        $conversation_lower = mb_strtolower( $conversation );
        $matches            = 0;

        foreach ( $desc_words as $word ) {
            if ( mb_strpos( $conversation_lower, $word ) !== false ) {
                $matches++;
            }
        }

        return min( $matches / 5, 1 ); // Cap at 1.0, 5 matches = full score.
    }

    /**
     * Get the best coupon for a preset.
     *
     * Helper method that loads coupons and selects the best one.
     *
     * @since 1.3.0
     * @param int    $preset_id    Preset ID.
     * @param float  $cart_total   Cart total.
     * @param string $conversation Conversation text.
     * @return array|null Best coupon or null.
     */
    public function get_best_for_preset( int $preset_id, float $cart_total, string $conversation = '', string $language = '' ): ?array {
        $coupon_model = new BoostBot_WooCommerce_Coupon_Model();
        $coupons      = $coupon_model->get_active_by_preset_and_purpose(
            $preset_id,
            BoostBot_WooCommerce_Coupon_Model::PURPOSE_WIDGET,
            $language ?: null
        );

        // Convert to scoring format.
        $scoring_data = array();
        foreach ( $coupons as $coupon ) {
            $scoring_data[] = $coupon_model->get_for_scoring( $coupon->id );
        }

        $scoring_data = array_filter( $scoring_data );

        return $this->select_coupon( $scoring_data, $cart_total, $conversation );
    }

    /**
     * Suggest a coupon based on conversation intent.
     *
     * Analyzes conversation for coupon-seeking intent.
     *
     * @since 1.3.0
     * @param string $conversation Conversation text.
     * @return bool True if user seems to want a coupon/discount.
     */
    public function detect_coupon_intent( string $conversation ): bool {
        $intent_keywords = array(
            'הנחה',
            'קופון',
            'מבצע',
            'זול',
            'discount',
            'coupon',
            'sale',
            'deal',
            'cheaper',
            'הוזלה',
            'מחיר מיוחד',
            'קוד הנחה',
        );

        $conversation_lower = mb_strtolower( $conversation );

        foreach ( $intent_keywords as $keyword ) {
            if ( mb_strpos( $conversation_lower, $keyword ) !== false ) {
                return true;
            }
        }

        return false;
    }
}







