<?php
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.LikeWildcardsInQuery, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.SafeRedirect.wp_redirect_wp_redirect, WordPress.WP.I18n.MissingTranslatorsComment, WordPress.WP.I18n.UnorderedPlaceholdersText, WordPress.DateTime.RestrictedFunctions.date_date, WordPress.WP.AlternativeFunctions.rand_mt_rand, WordPress.PHP.DevelopmentFunctions.error_log_error_log, Squiz.PHP.DiscouragedFunctions.Discouraged, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are built from the WordPress database prefix and escaped before use.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class YATOON_AI {

    private static function get_prompt_builder( $prompt ) {
        $function = 'wp_ai_client_' . 'prompt';
        return $function( $prompt );
    }

    public function __construct() {
        add_action( 'wp_ajax_yatoon_ai_service_recommendations', array( $this, 'ajax_service_recommendations' ) );
        add_action( 'wp_ajax_nopriv_yatoon_ai_service_recommendations', array( $this, 'ajax_service_recommendations' ) );
        add_action( 'wp_ajax_yatoon_ai_message_reply', array( $this, 'ajax_message_reply' ) );
        add_action( 'wp_ajax_yatoon_ai_service_description', array( $this, 'ajax_service_description' ) );
    }

    public static function is_text_available() {
        if ( ! function_exists( 'wp_ai_client_prompt' ) ) {
            return false;
        }

        try {
            $builder = self::get_prompt_builder( 'test' );
            if ( method_exists( $builder, 'using_temperature' ) ) {
                $builder = $builder->using_temperature( 0.2 );
            }
            if ( method_exists( $builder, 'is_supported_for_text_generation' ) ) {
                return (bool) $builder->is_supported_for_text_generation();
            }
        } catch ( \Throwable $e ) {
            yatoon_safe_log( 'AI support check failed: ' . $e->getMessage() );
            return false;
        }

        return true;
    }

    public static function status_message() {
        if ( ! function_exists( 'wp_ai_client_prompt' ) ) {
            return __( 'AI requires WordPress 7.0 or newer. This site will keep working normally without AI.', 'yatoon-booking-system' );
        }
        if ( ! self::is_text_available() ) {
            return __( 'AI is not available yet. Connect an AI provider in WordPress Settings > Connectors.', 'yatoon-booking-system' );
        }
        return __( 'AI is ready.', 'yatoon-booking-system' );
    }

    public function ajax_service_recommendations() {
        check_ajax_referer( 'yatoon_frontend_nonce', 'nonce' );
        $this->enforce_rate_limit( 'ai_service', 6, 10 );

        if ( get_option( 'yatoon_ai_service_helper_enabled', '1' ) !== '1' ) {
            wp_send_json_error( array( 'message' => __( 'AI service helper is disabled.', 'yatoon-booking-system' ) ) );
        }

        $need = isset( $_POST['need'] ) ? sanitize_textarea_field( wp_unslash( $_POST['need'] ) ) : '';
        if ( strlen( $need ) < 3 ) {
            wp_send_json_error( array( 'message' => __( 'Tell us what you are looking for first.', 'yatoon-booking-system' ) ) );
        }

        $services = $this->get_services_for_prompt();
        if ( empty( $services ) ) {
            wp_send_json_error( array( 'message' => __( 'No services are available for AI recommendations yet.', 'yatoon-booking-system' ) ) );
        }

        $availability = $this->get_availability_snapshot( $this->pick_likely_services( $need, $services, 6 ), $need );

        $business_name = sanitize_text_field( get_option( 'yatoon_business_name', get_bloginfo( 'name' ) ) );
        $prompt = "A customer is choosing an appointment service for {$business_name}.\n\n";
        $prompt .= "Customer request:\n" . $need . "\n\n";
        $prompt .= "Available services:\n" . wp_json_encode( $services ) . "\n\n";
        if ( ! empty( $availability ) ) {
            $prompt .= "Realtime availability snapshot from the booking system:\n" . wp_json_encode( array_values( $availability ) ) . "\n\n";
        }
        $prompt .= "Recommend up to 3 matching services. Use exact service names from the available services list so the booking form can make them clickable. Use the customer's language when possible. Be concise, friendly, and practical. Format as clean plain text with numbered recommendations. Do not use Markdown, asterisks, headings, or tables. For each recommendation include: service name, why it matches, and any useful note about length, art, maintenance, budget, or availability. If the customer asks when, earliest, latest, today, tomorrow, available staff, or free time, use the realtime availability snapshot when available. Do not invent appointment times, staff, prices, durations, policies, medical advice, or guarantees. Say that the customer should pick a service on the booking form to confirm the live time slot before submitting. If the customer mentions age, allergies, pregnancy, medical conditions, damaged nails, or uncertainty, add a short safety note to check with the salon or a parent/guardian when appropriate.";

        $text = $this->generate_text( $prompt, 'You are a polished salon consultation assistant. You help customers describe their desired result and choose from the provided service menu only.' );
        if ( is_wp_error( $text ) ) {
            wp_send_json_error( array( 'message' => $text->get_error_message(), 'status' => self::status_message() ) );
        }

        $clean_text = $this->trim_ai_text( $text );

        wp_send_json_success( array(
            'answer'          => wp_kses_post( wpautop( $clean_text ) ),
            'recommendations' => $this->match_services_from_text( $clean_text, $services, $availability ),
        ) );
    }

    public function ajax_message_reply() {
        check_ajax_referer( 'yatoon_ai_message_reply', 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'Permission denied.', 'yatoon-booking-system' ) ) );
        }
        if ( ! yatoon_is_pro() ) {
            wp_send_json_error( array( 'message' => __( 'AI reply drafts require an active Pro license.', 'yatoon-booking-system' ) ) );
        }
        if ( get_option( 'yatoon_ai_reply_drafts_enabled', '1' ) !== '1' ) {
            wp_send_json_error( array( 'message' => __( 'AI reply drafts are disabled.', 'yatoon-booking-system' ) ) );
        }

        $message_id = isset( $_POST['message_id'] ) ? absint( $_POST['message_id'] ) : 0;
        if ( ! $message_id ) {
            wp_send_json_error( array( 'message' => __( 'Invalid message.', 'yatoon-booking-system' ) ) );
        }

        global $wpdb;
        $table = esc_sql( $wpdb->prefix . 'yatoon_contact_messages' );
        $msg = yatoon_db_get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $message_id ) );
        if ( ! $msg ) {
            wp_send_json_error( array( 'message' => __( 'Message not found.', 'yatoon-booking-system' ) ) );
        }

        $business_name = sanitize_text_field( get_option( 'yatoon_business_name', get_bloginfo( 'name' ) ) );
        $business_phone = sanitize_text_field( get_option( 'yatoon_business_phone', get_option( 'yatoon_contact_phone', '' ) ) );
        $prompt = "Write a reply draft for this salon/spa customer message.\n\n";
        $prompt .= "Business: {$business_name}\n";
        if ( $business_phone ) {
            $prompt .= "Business phone: {$business_phone}\n";
        }
        $prompt .= "Customer name: " . sanitize_text_field( $msg->name ) . "\n";
        $prompt .= "Customer message:\n" . sanitize_textarea_field( $msg->message ) . "\n\n";
        $prompt .= "Keep it under 120 words. Sound warm and professional. Do not promise a booking time, price, refund, diagnosis, or medical/safety outcome. If the question needs staff confirmation, say the team will confirm.";

        $text = $this->generate_text( $prompt, 'You write short customer-service reply drafts for a salon booking team.' );
        if ( is_wp_error( $text ) ) {
            wp_send_json_error( array( 'message' => $text->get_error_message(), 'status' => self::status_message() ) );
        }

        wp_send_json_success( array(
            'reply' => $this->trim_ai_text( $text ),
        ) );
    }

    public function ajax_service_description() {
        check_ajax_referer( 'yatoon_ai_service_description', 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'Permission denied.', 'yatoon-booking-system' ) ) );
        }

        $service_name = isset( $_POST['service_name'] ) ? sanitize_text_field( wp_unslash( $_POST['service_name'] ) ) : '';
        if ( $service_name === '' ) {
            wp_send_json_error( array( 'message' => __( 'Enter a service name first.', 'yatoon-booking-system' ) ) );
        }

        $category     = isset( $_POST['category'] ) ? sanitize_text_field( wp_unslash( $_POST['category'] ) ) : '';
        $price        = isset( $_POST['price'] ) ? sanitize_text_field( wp_unslash( $_POST['price'] ) ) : '';
        $duration     = isset( $_POST['duration'] ) ? absint( $_POST['duration'] ) : 0;
        $description  = isset( $_POST['description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['description'] ) ) : '';
        $business     = sanitize_text_field( get_option( 'yatoon_business_name', get_bloginfo( 'name' ) ) );

        $prompt = "Write a polished customer-facing service description for a salon/spa booking menu.\n\n";
        $prompt .= "Business: {$business}\n";
        $prompt .= "Service name: {$service_name}\n";
        if ( $category ) {
            $prompt .= "Category: {$category}\n";
        }
        if ( $duration ) {
            $prompt .= "Duration: {$duration} minutes\n";
        }
        if ( $price !== '' ) {
            $prompt .= "Price: {$price}\n";
        }
        if ( $description !== '' ) {
            $prompt .= "Existing description:\n{$description}\n";
        }
        $prompt .= "\nCreate 2 to 4 short sentences that help a customer understand the result, who it is best for, and what to mention when booking. Make it useful for AI service matching too. Do not use Markdown, headings, asterisks, emojis, or bullet points. Do not invent policies, guarantees, medical claims, exact products, discounts, or hidden inclusions. Keep the tone professional, clear, and inviting.";

        $text = $this->generate_text( $prompt, 'You write accurate, polished service descriptions for salon and spa booking menus. Use only the supplied service details and avoid unsupported claims.' );
        if ( is_wp_error( $text ) ) {
            wp_send_json_error( array( 'message' => $text->get_error_message(), 'status' => self::status_message() ) );
        }

        wp_send_json_success( array(
            'description' => mb_substr( $this->trim_ai_text( $text ), 0, 900 ),
        ) );
    }

    private function get_services_for_prompt() {
        global $wpdb;
        $table = $wpdb->prefix . 'yatoon_services';
        $exists = yatoon_db_get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $exists !== $table ) {
            return array();
        }

        $rows = yatoon_db_get_results( "SELECT id, name, description, category, price, duration_minutes FROM {$table} WHERE is_active = 1 ORDER BY sort_order ASC, name ASC LIMIT 60" );
        $services = array();
        foreach ( (array) $rows as $row ) {
            $services[] = array(
                'id'          => (int) $row->id,
                'name'        => sanitize_text_field( $row->name ),
                'category'    => sanitize_text_field( $row->category ),
                'description' => wp_strip_all_tags( (string) $row->description ),
                'price'       => (float) $row->price,
                'duration'    => (int) $row->duration_minutes,
            );
        }
        return $services;
    }

    private function pick_likely_services( $need, $services, $limit = 6 ) {
        $need_lc = mb_strtolower( wp_strip_all_tags( (string) $need ) );
        $terms = preg_split( '/[^\p{L}\p{N}]+/u', $need_lc, -1, PREG_SPLIT_NO_EMPTY );
        $terms = array_values( array_filter( array_unique( (array) $terms ), function( $term ) {
            return mb_strlen( $term ) >= 3;
        } ) );

        $scored = array();
        foreach ( (array) $services as $service ) {
            $haystack = mb_strtolower( implode( ' ', array(
                $service['name'] ?? '',
                $service['category'] ?? '',
                $service['description'] ?? '',
            ) ) );
            $score = 0;
            foreach ( $terms as $term ) {
                if ( mb_strpos( $haystack, $term ) !== false ) {
                    $score += 3;
                }
            }
            if ( $score === 0 && ! empty( $service['name'] ) && mb_strpos( $need_lc, mb_strtolower( (string) $service['name'] ) ) !== false ) {
                $score += 8;
            }
            if ( $score > 0 ) {
                $scored[] = array( 'score' => $score, 'service' => $service );
            }
        }

        usort( $scored, function( $a, $b ) {
            return $b['score'] <=> $a['score'];
        } );

        if ( empty( $scored ) ) {
            return array_slice( $services, 0, $limit );
        }

        return array_map( function( $item ) {
            return $item['service'];
        }, array_slice( $scored, 0, $limit ) );
    }

    private function get_availability_snapshot( $services, $need = '' ) {
        if ( empty( $services ) || ! class_exists( 'YATOON_Business_Hours' ) ) {
            return array();
        }

        $snapshot = array();
        $platform = get_option( 'yatoon_active_platform', 'local' );
        $intent   = $this->get_availability_intent( $need );
        foreach ( (array) $services as $service ) {
            $service_id = absint( $service['id'] ?? 0 );
            if ( ! $service_id ) {
                continue;
            }

            $openings = $this->get_next_openings_for_service( $service_id, 14, 4, $intent );

            $snapshot[ $service_id ] = array(
                'service_id'      => $service_id,
                'service_name'    => sanitize_text_field( $service['name'] ?? '' ),
                'staff_available' => $this->get_staff_names_for_service( $service_id, 6 ),
                'next_openings'   => $openings,
                'note'            => empty( $openings )
                    ? 'No synced open times found in the next 14 days.'
                    : ( $platform === 'local'
                        ? 'Times are a local availability snapshot and must be confirmed in the booking form.'
                        : 'Times are based on synced local availability data, not a fresh Square or Vagaro live query, and must be confirmed in the booking form.' ),
            );
        }

        return $snapshot;
    }

    private function get_availability_intent( $need ) {
        $text = mb_strtolower( wp_strip_all_tags( (string) $need ) );

        $intent = array(
            'day_offset'              => null,
            'earliest_start_minute'   => null,
            'latest_start_before_min' => null,
        );

        if ( mb_strpos( $text, 'day after tomorrow' ) !== false || mb_strpos( $text, '鍚庡ぉ' ) !== false ) {
            $intent['day_offset'] = 2;
        } elseif ( mb_strpos( $text, 'tomorrow' ) !== false || mb_strpos( $text, '鏄庡ぉ' ) !== false ) {
            $intent['day_offset'] = 1;
        } elseif ( mb_strpos( $text, 'today' ) !== false || mb_strpos( $text, '浠婂ぉ' ) !== false ) {
            $intent['day_offset'] = 0;
        }

        if (
            mb_strpos( $text, '涓崍涔嬪墠' ) !== false ||
            mb_strpos( $text, '涓婂崍' ) !== false ||
            mb_strpos( $text, '鏃╀笂' ) !== false ||
            mb_strpos( $text, 'morning' ) !== false ||
            mb_strpos( $text, 'before noon' ) !== false ||
            mb_strpos( $text, 'before 12' ) !== false
        ) {
            $intent['latest_start_before_min'] = 12 * 60;
        } elseif (
            mb_strpos( $text, '涓嬪崍' ) !== false ||
            mb_strpos( $text, 'afternoon' ) !== false
        ) {
            $intent['earliest_start_minute'] = 12 * 60;
        } elseif (
            mb_strpos( $text, '鏅氫笂' ) !== false ||
            mb_strpos( $text, 'evening' ) !== false
        ) {
            $intent['earliest_start_minute'] = 17 * 60;
        }

        return $intent;
    }

    private function score_slot_for_availability_intent( DateTime $slot_start, $intent ) {
        if ( empty( $intent ) || ! is_array( $intent ) ) {
            return 0;
        }

        $score = 0;
        $minutes = ( (int) $slot_start->format( 'G' ) * 60 ) + (int) $slot_start->format( 'i' );

        if ( isset( $intent['day_offset'] ) && $intent['day_offset'] !== null ) {
            $tz          = $slot_start->getTimezone();
            $today       = new DateTime( 'today', $tz );
            $slot_day    = new DateTime( $slot_start->format( 'Y-m-d' ) . ' 00:00:00', $tz );
            $actual_days = (int) $today->diff( $slot_day )->format( '%r%a' );
            if ( $actual_days !== (int) $intent['day_offset'] ) {
                $score += 100 + abs( $actual_days - (int) $intent['day_offset'] );
            }
        }

        if ( isset( $intent['earliest_start_minute'] ) && $intent['earliest_start_minute'] !== null && $minutes < (int) $intent['earliest_start_minute'] ) {
            $score += 25 + ( ( (int) $intent['earliest_start_minute'] - $minutes ) / 60 );
        }

        if ( isset( $intent['latest_start_before_min'] ) && $intent['latest_start_before_min'] !== null && $minutes >= (int) $intent['latest_start_before_min'] ) {
            $score += 25 + ( ( $minutes - (int) $intent['latest_start_before_min'] ) / 60 );
        }

        return $score;
    }

    private function get_next_openings_for_service( $service_id, $days = 14, $limit = 4, $intent = array() ) {
        $service_id = absint( $service_id );
        if ( ! $service_id || ! class_exists( 'YATOON_Business_Hours' ) ) {
            return array();
        }

        $timezone = get_option( 'timezone_string' ) ?: 'America/New_York';
        try {
            $tz = new DateTimeZone( $timezone );
        } catch ( Exception $e ) {
            $tz = new DateTimeZone( 'America/New_York' );
        }

        $now = new DateTime( 'now', $tz );
        $candidates = array();

        for ( $d = 0; $d < $days; $d++ ) {
            $date = ( clone $now )->modify( '+' . $d . ' days' );
            $date_str = $date->format( 'Y-m-d' );
            $slots = YATOON_Business_Hours::generate_slots( $date_str, $service_id, 0 );
            if ( empty( $slots ) || ! is_array( $slots ) ) {
                continue;
            }

            foreach ( $slots as $slot ) {
                $time = is_array( $slot ) ? ( $slot['time'] ?? '' ) : (string) $slot;
                $display = is_array( $slot ) ? ( $slot['display'] ?? '' ) : '';
                if ( ! $time ) {
                    continue;
                }
                try {
                    $slot_start = new DateTime( $date_str . ' ' . $time, $tz );
                } catch ( Exception $e ) {
                    continue;
                }
                $candidates[] = array(
                    'score'   => $this->score_slot_for_availability_intent( $slot_start, $intent ),
                    'ts'      => $slot_start->getTimestamp(),
                    'opening' => array(
                        'date'  => $date_str,
                        'day'   => $date->format( 'D, M j' ),
                        'time'  => $display ?: $time,
                        'label' => $date->format( 'D, M j' ) . ' at ' . ( $display ?: $time ),
                    ),
                );
            }
        }

        usort( $candidates, function( $a, $b ) {
            if ( $a['score'] === $b['score'] ) {
                return $a['ts'] <=> $b['ts'];
            }
            return $a['score'] <=> $b['score'];
        } );

        return array_map( function( $item ) {
            return $item['opening'];
        }, array_slice( $candidates, 0, $limit ) );
    }

    private function get_staff_names_for_service( $service_id, $limit = 6 ) {
        global $wpdb;
        $service_id = absint( $service_id );
        if ( ! $service_id ) {
            return array();
        }

        $staff_table = $wpdb->prefix . 'yatoon_staff';
        $assign_table = $wpdb->prefix . 'yatoon_staff_services';
        $assign_exists = yatoon_db_get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $assign_table ) );

        if ( $assign_exists === $assign_table ) {
            $names = yatoon_db_get_col( $wpdb->prepare(
                "SELECT st.name
                 FROM {$staff_table} st
                 INNER JOIN {$assign_table} ss ON st.id = ss.staff_id
                 WHERE ss.service_id = %d AND st.is_active = 1 AND st.is_bookable = 1
                 ORDER BY st.sort_order ASC, st.name ASC
                 LIMIT %d",
                $service_id,
                absint( $limit )
            ) );
            if ( ! empty( $names ) ) {
                return array_map( 'sanitize_text_field', $names );
            }
        }

        $names = yatoon_db_get_col( $wpdb->prepare(
            "SELECT name FROM {$staff_table}
             WHERE is_active = 1 AND is_bookable = 1
             ORDER BY sort_order ASC, name ASC
             LIMIT %d",
            absint( $limit )
        ) );

        return array_map( 'sanitize_text_field', (array) $names );
    }

    private function match_services_from_text( $text, $services, $availability = array() ) {
        $matches = array();
        $haystack = mb_strtolower( wp_strip_all_tags( (string) $text ) );

        foreach ( (array) $services as $service ) {
            $name = isset( $service['name'] ) ? (string) $service['name'] : '';
            if ( $name === '' ) {
                continue;
            }
            if ( mb_strpos( $haystack, mb_strtolower( $name ) ) === false ) {
                continue;
            }

            $service_id = (int) $service['id'];
            $match = array(
                'id'       => (int) $service['id'],
                'name'     => sanitize_text_field( $service['name'] ),
                'price'    => (float) $service['price'],
                'duration' => (int) $service['duration'],
            );
            if ( isset( $availability[ $service_id ] ) ) {
                $next = $availability[ $service_id ]['next_openings'][0]['label'] ?? '';
                if ( $next ) {
                    $match['next_opening'] = sanitize_text_field( $next );
                }
            }

            $matches[] = $match;

            if ( count( $matches ) >= 3 ) {
                break;
            }
        }

        return $matches;
    }

    private function generate_text( $prompt, $system_instruction = '' ) {
        if ( ! self::is_text_available() ) {
            return new WP_Error( 'yatoon_ai_unavailable', self::status_message() );
        }

        try {
            $builder = self::get_prompt_builder( $prompt );
            if ( $system_instruction && method_exists( $builder, 'using_system_instruction' ) ) {
                $builder = $builder->using_system_instruction( $system_instruction );
            }
            if ( method_exists( $builder, 'using_temperature' ) ) {
                $builder = $builder->using_temperature( 0.3 );
            }
            if ( method_exists( $builder, 'using_max_tokens' ) ) {
                $builder = $builder->using_max_tokens( 700 );
            }
            $model_preferences = $this->get_model_preferences();
            if ( method_exists( $builder, 'using_model_preference' ) && ! empty( $model_preferences ) ) {
                $builder = call_user_func_array( array( $builder, 'using_model_preference' ), $model_preferences );
            }
            return $builder->generate_text();
        } catch ( \Throwable $e ) {
            yatoon_safe_log( 'AI generation failed: ' . $e->getMessage() );
            return new WP_Error( 'yatoon_ai_error', __( 'AI could not generate a response right now.', 'yatoon-booking-system' ) );
        }
    }

    private function get_model_preferences() {
        $providers = get_option( 'yatoon_ai_provider_order', array( 'google', 'openai', 'anthropic' ) );
        if ( is_string( $providers ) ) {
            $decoded = json_decode( $providers, true );
            $providers = is_array( $decoded ) ? $decoded : explode( ',', $providers );
        }

        $providers = array_values( array_unique( array_filter( array_map( 'sanitize_key', (array) $providers ) ) ) );
        $allowed_providers = array( 'google', 'openai', 'anthropic' );
        $providers = array_values( array_intersect( $providers, $allowed_providers ) );
        foreach ( $allowed_providers as $provider ) {
            if ( ! in_array( $provider, $providers, true ) ) {
                $providers[] = $provider;
            }
        }

        $defaults = array(
            'google'    => 'gemini-1.5-flash',
            'openai'    => 'gpt-4o-mini',
            'anthropic' => 'claude-3-5-haiku-20241022',
        );

        $preferences = array();
        foreach ( $providers as $provider ) {
            $model = sanitize_text_field( get_option( 'yatoon_ai_model_' . $provider, $defaults[ $provider ] ) );
            if ( $model !== '' ) {
                $preferences[] = $model;
            }
        }

        return $preferences;
    }

    private function trim_ai_text( $text ) {
        $text = wp_strip_all_tags( (string) $text );
        $text = str_replace( array( '**', '__', '*', '###', '##', '#' ), '', $text );
        $text = trim( preg_replace( "/[ \t]+\n/", "\n", $text ) );
        return mb_substr( $text, 0, 2000 );
    }

    private function get_client_ip() {
        $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
        return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : '';
    }

    private function enforce_rate_limit( $scope, $limit = 5, $minutes = 10 ) {
        $client_ip = $this->get_client_ip();
        if ( $client_ip === '' ) {
            return;
        }

        $key = 'yatoon_public_' . sanitize_key( $scope ) . '_' . md5( $client_ip );
        $count = (int) get_transient( $key );
        if ( $count >= $limit ) {
            wp_send_json_error( array(
                'message' => sprintf( __( 'Too many requests. Please wait %d minutes and try again.', 'yatoon-booking-system' ), $minutes ),
            ) );
        }
        set_transient( $key, $count + 1, $minutes * MINUTE_IN_SECONDS );
    }
}
