<?php
/**
 * Chat Endpoint for BoostBot REST API.
 *
 * @package BoostBot
 */

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

/**
 * BoostBot_Chat_Endpoint class.
 *
 * Handles chat message sending and history retrieval.
 *
 * @since 1.0.0
 */
class BoostBot_Chat_Endpoint extends BoostBot_Base_Endpoint
{
    // TODO: Continue the English-msgid multilingual migration in the remaining
    // admin-facing endpoints:
    // class-widgets-endpoint.php, class-meetings-endpoint.php,
    // class-bots-endpoint.php, class-woocommerce-endpoint.php,
    // class-contact-lists-endpoint.php, class-feedback-endpoint.php,
    // class-indexing-credits-endpoint.php, class-opening-screen-endpoint.php,
    // class-opening-screen-presets-endpoint.php, class-personalities-endpoint.php,
    // class-settings-endpoint.php

    /**
     * The base path for this endpoint.
     *
     * @since 1.0.0
     * @access protected
     * @var string $rest_base
     */
    protected $rest_base = 'chat';

    /**
     * Register the routes for this endpoint.
     *
     * @since 1.0.0
     */
    public function register()
    {
        // POST /wp-json/boostbot/v1/chat
        $this->register_route(
            '',
            'POST',
            [$this, 'handle_message'],
            [$this, 'public_with_validation'],
            $this->get_message_args()
        );

        // GET /wp-json/boostbot/v1/chat/history/{conversation_id}
        $this->register_route(
            '/history/(?P<conversation_id>[a-zA-Z0-9-]+)',
            'GET',
            [$this, 'get_history'],
            [$this, 'public_with_validation']
        );

        // POST /wp-json/boostbot/v1/chat/start
        $this->register_route(
            '/start',
            'POST',
            [$this, 'start_conversation'],
            [$this, 'public_with_rate_limit']
        );
    }

    /**
     * Handle incoming chat message.
     *
     * @since 1.0.0
     * @param WP_REST_Request $request The REST request object.
     * @return WP_REST_Response|WP_Error The response or error.
     */
    public function handle_message($request)
    {
        BoostBot_Logger::warning('[Deprecated] FLOW=LEGACY_CHAT endpoint /chat was called. Canonical flow is /chat/retrieve-orchestrated + /chat/orchestrate + /chat/execute.');
        BoostBot_Logger::info('====== CHAT REQUEST START ======');
        BoostBot_Logger::info('Chat message received');
        
        BoostBot_Logger::debug('Chat message received', $request->get_params());

        // Validate & sanitize input
        $message = sanitize_textarea_field($request->get_param('message'));
        $conversation_id = sanitize_text_field($request->get_param('conversation_id'));
        $visitor_id = sanitize_text_field($request->get_param('visitor_id'));
        $page_context = $request->get_param('context') ?: [];

        BoostBot_Logger::info('Message: ' . substr($message, 0, 50) . '...');
        BoostBot_Logger::info('Conversation ID: ' . ($conversation_id ?: 'NEW'));

        // Validate message
        if (empty($message)) {
            BoostBot_Logger::warning('Empty message');
            return $this->error(__('Message is required', 'boostbot'), 400);
        }

        // Week 28: visitor input cap (chars) - configurable
        $input_cap_enabled = (bool) BoostBot_Settings::get( 'visitor_input_char_limit_enabled', true );
        $input_cap_value   = absint( BoostBot_Settings::get( 'visitor_input_char_limit', 500 ) );
        $effective_cap     = $input_cap_enabled && $input_cap_value > 0 ? $input_cap_value : 5000;

        // Week 28: cap is defined in characters, not bytes. Use mb_strlen for UTF-8 safety (Hebrew, emoji).
        $message_len = function_exists( 'mb_strlen' ) ? mb_strlen( (string) $message, 'UTF-8' ) : strlen( (string) $message );
        if ( $message_len > $effective_cap ) {
            BoostBot_Logger::warning('Message too long');
            return $this->error(
                sprintf(
                    /* translators: %d is max characters */
                    __( 'Message is too long (max %d characters)', 'boostbot' ),
                    (int) $effective_cap
                ),
                400
            );
        }

        // Credits backend enforcement (Week 26)
        $backend_base_url = (string) BoostBot_Settings::get( 'backend_base_url', '' );
        $backend_base_url = trim( $backend_base_url );
        $backend_enabled  = ! empty( $backend_base_url ) && class_exists( 'BoostBot_Credits_Backend_Client' );

        // If configured, enforce hard-block based on credits.
        if ( $backend_enabled ) {
            $credits_client = new BoostBot_Credits_Backend_Client();
            $site_id        = $credits_client->ensure_site_id();

            if ( is_wp_error( $site_id ) ) {
                BoostBot_Logger::error( 'Credits backend site registration failed', array(
                    'error' => $site_id->get_error_message(),
                ) );
                // Debug aid: show the actual backend error message to admins only.
                if ( current_user_can( 'manage_options' ) ) {
                    return $this->error(
                        sprintf(
                            /* translators: %s is an error message */
                            __( 'Credits system error (debug): %s', 'boostbot' ),
                            $site_id->get_error_message()
                        ),
                        500
                    );
                }
                return $this->error( __( 'Credits system error. Please try again later.', 'boostbot' ), 500 );
            }

            // Always check status (even for existing conversations) because allow_existing_conversations=false in spec.
            // Performance optimization: Cache credits status for 60 seconds to avoid repeated backend calls.
            // 60s is a good balance - typical chat sessions have messages within this window.
            $cache_key = 'boostbot_credits_status_' . $site_id;
            $status = get_transient( $cache_key );
            if ( false === $status ) {
                $status = $credits_client->get_status( (string) $site_id );
                if ( ! is_wp_error( $status ) ) {
                    // Cache for 60 seconds - catches most follow-up messages in a conversation
                    set_transient( $cache_key, $status, 60 );
                    BoostBot_Logger::info('[PERF] Credits status fetched from backend and cached for 60s');
                }
            } else {
                BoostBot_Logger::info('[PERF] Credits status served from cache (saved ~2.2s)');
            }
            if ( is_wp_error( $status ) ) {
                BoostBot_Logger::error( 'Credits backend status failed', array(
                    'error' => $status->get_error_message(),
                ) );
                // Debug aid: show the actual backend error message to admins only.
                if ( current_user_can( 'manage_options' ) ) {
                    return $this->error(
                        sprintf(
                            /* translators: %s is an error message */
                            __( 'Credits system error (debug): %s', 'boostbot' ),
                            $status->get_error_message()
                        ),
                        500
                    );
                }
                return $this->error( __( 'Credits system error. Please try again later.', 'boostbot' ), 500 );
            }

            // Ensure a conversation id exists if we're about to consume (conversation start).
            $is_new_conversation = empty( $conversation_id );
            if ( $is_new_conversation ) {
                $conversation_id = wp_generate_uuid4();
            }

            if ( empty( $status['allowed'] ) ) {
                // Hard block (no credits OR site disabled/blocked)
                $site_status = isset( $status['site_status'] ) ? (string) $status['site_status'] : '';
                if ( 'disabled' === $site_status ) {
                    return $this->success( array(
                        'reply'           => __( 'Service disabled. Please contact support.', 'boostbot' ),
                        'conversation_id' => $conversation_id,
                        'widgets'         => array(),
                        'suggestions'     => array(),
                        'blocked'         => true,
                        'block_reason'    => 'site_disabled',
                        'site_status'     => 'disabled',
                        'active_model'    => isset( $status['active_model'] ) ? $status['active_model'] : null,
                        'credits'         => isset( $status['credits'] ) ? $status['credits'] : null,
                    ) );
                }

                return $this->success( array(
                    'reply'           => __( "No credits available. Purchase more to continue.", 'boostbot' ),
                    'conversation_id' => $conversation_id,
                    'widgets'         => array(),
                    'suggestions'     => array(),
                    'blocked'         => true,
                    'block_reason'    => 'no_credits',
                    'site_status'     => isset( $status['site_status'] ) ? $status['site_status'] : null,
                    'active_model'    => isset( $status['active_model'] ) ? $status['active_model'] : null,
                    'credits'         => isset( $status['credits'] ) ? $status['credits'] : null,
                ) );
            }

            // Week 28: Do NOT consume here. The backend `/chat` endpoint is responsible for
            // char-metered charging per message using idempotency keys.
        }
        
        // BYOK is no longer supported. Credits backend is required.
        if ( ! $backend_enabled ) {
            return $this->success( array(
                'reply'           => __( 'The service is not available right now. Please try again later or contact support.', 'boostbot' ),
                'conversation_id' => $conversation_id ?: wp_generate_uuid4(),
                'widgets'         => array(),
                'suggestions'     => array(),
            ) );
        }

        // ── Per-visitor message rate limiting (Week 33) ──────────────
        if ( class_exists( 'BoostBot_Message_Limiter' ) ) {
            $msg_identifier = $this->get_visitor_identifier( $request );
            $msg_check      = BoostBot_Message_Limiter::check( $msg_identifier );

            if ( ! $msg_check['allowed'] ) {
                // Notify spam alert monitor.
                if ( class_exists( 'BoostBot_Spam_Alert_Monitor' ) && in_array( $msg_check['limit_type'], [ 'hourly', 'daily' ], true ) ) {
                    BoostBot_Spam_Alert_Monitor::on_limit_hit( $msg_identifier, $msg_check['limit_type'] );
                }

                $limit_reply = '';
                if ( 'hourly' === $msg_check['limit_type'] ) {
                    $minutes = max( 1, ceil( $msg_check['retry_after'] / 60 ) );
                    $limit_reply = sprintf(
                        /* translators: %d: number of minutes to wait */
                        __( 'You\'ve reached the message limit for this hour. Please try again in %d minutes.', 'boostbot' ),
                        $minutes
                    );
                } elseif ( 'daily' === $msg_check['limit_type'] ) {
                    $limit_reply = __( 'You\'ve reached the daily message limit. Please try again tomorrow.', 'boostbot' );
                } elseif ( 'cooldown' === $msg_check['limit_type'] ) {
                    $limit_reply = sprintf(
                        /* translators: %d: number of seconds to wait */
                        __( 'Please wait %d seconds before sending another message.', 'boostbot' ),
                        $msg_check['cooldown_seconds']
                    );
                }

                return $this->success( array(
                    'reply'            => $limit_reply,
                    'conversation_id'  => $conversation_id ?: wp_generate_uuid4(),
                    'widgets'          => array(),
                    'suggestions'      => array(),
                    'rate_limited'     => true,
                    'limit_type'       => $msg_check['limit_type'],
                    'retry_after'      => $msg_check['retry_after'],
                    'cooldown_seconds' => $msg_check['cooldown_seconds'],
                    'remaining_hour'   => $msg_check['remaining_hour'],
                    'remaining_day'    => $msg_check['remaining_day'],
                ) );
            }
        }

        try {
            BoostBot_Logger::info('Starting Chat Handler process...');
            
            // Increment message counter BEFORE processing (visitor is allowed).
            $msg_increment = null;
            if ( class_exists( 'BoostBot_Message_Limiter' ) && isset( $msg_identifier ) ) {
                $msg_increment = BoostBot_Message_Limiter::increment( $msg_identifier );
            }

            // Process message with Claude AI
            $response = BoostBot_Chat_Handler::process(
                $message,
                $conversation_id,
                $visitor_id,
                $page_context
            );

            // Inject rate-limit metadata into the response.
            if ( is_array( $msg_increment ) ) {
                $response['remaining_hour']   = $msg_increment['remaining_hour'];
                $response['remaining_day']    = $msg_increment['remaining_day'];
                $response['cooldown_seconds'] = $msg_increment['cooldown_seconds'];
            }

            BoostBot_Logger::info('Chat Handler returned successfully');
            BoostBot_Logger::info('Reply length: ' . strlen($response['reply'] ?? ''));
            
            BoostBot_Logger::info('Chat response sent', [
                'conversation_id' => $response['conversation_id'],
                'message_length'  => strlen($message),
                'has_widgets'     => !empty($response['widgets']),
            ]);

            BoostBot_Logger::info('====== CHAT REQUEST SUCCESS ======');
            return $this->success($response);

        } catch (Exception $e) {
            BoostBot_Logger::error('EXCEPTION in Chat Handler: ' . $e->getMessage());
            BoostBot_Logger::error('File: ' . $e->getFile() . ' Line: ' . $e->getLine());
            BoostBot_Logger::error('Stack trace: ' . $e->getTraceAsString());
            
            BoostBot_Logger::error('Chat processing error', [
                'error'   => $e->getMessage(),
                'message' => substr($message, 0, 100),
            ]);

            BoostBot_Logger::error('====== CHAT REQUEST FAILED ======');
            return $this->error(
                __( 'An error occurred while processing your message. Please try again.', 'boostbot' ),
                500
            );
        }
    }

    /**
     * Get chat history for a conversation.
     *
     * @since 1.0.0
     * @param WP_REST_Request $request The REST request object.
     * @return WP_REST_Response|WP_Error The response or error.
     */
    public function get_history($request)
    {
        $conversation_id = sanitize_text_field($request->get_param('conversation_id'));

        BoostBot_Logger::debug('Getting chat history', ['conversation_id' => $conversation_id]);

        $conversation_model = new BoostBot_Conversation_Model();
        $conversation = $conversation_model->get_by_conversation_id($conversation_id);

        if (!$conversation) {
            return $this->not_found('Conversation');
        }

        // Parse session data (contains message history)
        $session_data = json_decode($conversation->session_data, true) ?: [];

        return $this->success([
            'conversation_id' => $conversation_id,
            'messages'        => $session_data['messages'] ?? [],
            'started_at'      => $conversation->started_at,
            'last_message_at' => $conversation->last_message_at,
            'total_messages'  => $conversation->total_messages,
        ]);
    }

    /**
     * Start a new conversation.
     *
     * @since 1.0.0
     * @param WP_REST_Request $request The REST request object.
     * @return WP_REST_Response|WP_Error The response or error.
     */
    public function start_conversation($request)
    {
        $visitor_id = sanitize_text_field($request->get_param('visitor_id'));
        $page_context = $request->get_param('context');

        if (empty($visitor_id)) {
            $visitor_id = $this->get_visitor_identifier($request);
        }

        // Generate new conversation ID
        $conversation_id = wp_generate_uuid4();

        // Create conversation
        $conversation_model = new BoostBot_Conversation_Model();
        $result = $conversation_model->create([
            'conversation_id' => $conversation_id,
            'visitor_id'      => $visitor_id,
            'session_data'    => ['messages' => []],
            'context_data'    => $page_context,
        ]);

        if (!$result) {
            return $this->error(__('Failed to start conversation', 'boostbot'), 500);
        }

        // Track new conversation
        if (class_exists('BoostBot_Usage_Tracker')) {
            BoostBot_Usage_Tracker::increment('conversations');
        }

        // Get welcome message
        $welcome_message = BoostBot_Settings::get('welcome_message');

        BoostBot_Logger::info('New conversation started', [
            'conversation_id' => $conversation_id,
            'visitor_id'      => $visitor_id,
        ]);

        return $this->success([
            'conversation_id'  => $conversation_id,
            'welcome_message'  => $welcome_message,
            'visitor_id'       => $visitor_id,
        ], 201);
    }

    /**
     * Get argument schema for message endpoint.
     *
     * @since 1.0.0
     * @return array Argument schema.
     */
    private function get_message_args()
    {
        return [
            'message'         => [
                'required'          => true,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_textarea_field',
                'validate_callback' => function ($value) {
                    return !empty($value) && strlen($value) <= 5000;
                },
            ],
            'conversation_id' => [
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'visitor_id'      => [
                'required'          => true,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'context'         => [
                'required' => false,
                'type'     => 'object',
            ],
        ];
    }

    /**
     * Get placeholder response (until Claude integration in Week 5).
     *
     * @since 1.0.0
     * @param string $message The user's message.
     * @return string Placeholder response.
     */
    private function get_placeholder_response($message)
    {
        $responses = [
            __( 'Thanks for your message! Claude integration will be added in week 5. For now, I am only a placeholder.', 'boostbot' ),
            __( 'I received your message. The full AI experience will be available soon!', 'boostbot' ),
            __( 'Hello! The system is still in development. Claude AI will be enabled later.', 'boostbot' ),
        ];

        return $responses[array_rand($responses)];
    }

    /**
     * Get suggested follow-up questions.
     *
     * @since 1.0.0
     * @param string $message The user's message.
     * @return array Suggested questions.
     */
    private function get_suggestions($message)
    {
        // Placeholder suggestions (will be AI-generated in Week 5)
        return [
            __( 'How can I help?', 'boostbot' ),
            __( 'Tell me more about your services', 'boostbot' ),
            __( 'How can I get in touch?', 'boostbot' ),
        ];
    }
}
