<?php
/**
 * Normal chatbot request runner.
 *
 * @package KimaAI\ChatBot
 */

declare(strict_types=1);

namespace KimaAI\ChatBot;

use Exception;
use KimaAI\Ai\AiService;
use KimaAI\ChatBot\Tools\ToolCommandHandler;
use KimaAI\Logger;
use WP_REST_Request;
use WP_REST_Response;

defined('ABSPATH') || exit;

class ChatbotNormalRequestRunner {

  /**
   * Create the runner.
   *
   * @param AiService $ai_service AI service.
   * @param ToolCommandHandler $command_handler Chatbot command handler.
   * @param ChatbotProviderErrorNormalizer $error_normalizer Provider error normalizer.
   */
  public function __construct(
    private AiService $ai_service,
    private ToolCommandHandler $command_handler,
    private ChatbotProviderErrorNormalizer $error_normalizer
  ) {
  }

  /**
   * Handle a prepared chatbot request and return a REST response.
   *
   * @param ChatbotPreparedRequest $prepared Prepared request.
   * @param WP_REST_Request $request REST request.
   * @return WP_REST_Response
   */
  public function handle(ChatbotPreparedRequest $prepared, WP_REST_Request $request): WP_REST_Response {
    $command_response = $this->handleCommand($prepared, $request);

    if ($command_response instanceof WP_REST_Response) {
      return $command_response;
    }

    try {
      $response = $this->ai_service->run($prepared->ai_payload, 'chat');
    } catch (Exception $exception) {
      $provider = $this->extractProvider($prepared->chatbot_settings, $prepared->ai_payload);
      $normalized = $this->error_normalizer->normalize($exception, $provider, 500);

      do_action(
        'kimaai_chatbot_error',
        'ai_exception',
        $normalized['message'] ?: $exception->getMessage(),
        [
          'exception'        => $exception,
          'ai_payload'       => $prepared->ai_payload,
          'payload'          => $prepared->payload,
          'normalized_error' => $normalized,
        ]
      );

      Logger::error($normalized['rawMessage'] ?: $exception->getMessage());
      return $this->makeErrorResponse($normalized);
    }

    $response['sessionId'] = $prepared->payload['sessionId'];

    $response = apply_filters(
      'kimaai_chatbot_response_payload',
      $response,
      $prepared->ai_payload,
      $prepared->payload,
      $prepared->chatbot_settings
    );

    do_action(
      'kimaai_chatbot_after_response',
      $response,
      $prepared->ai_payload,
      $prepared->payload,
      $prepared->chatbot_settings
    );

    return new WP_REST_Response($response, 200);
  }

  /**
   * Dispatch a command payload before the AI request, returning early if handled.
   *
   * @param ChatbotPreparedRequest $prepared Prepared request.
   * @param WP_REST_Request $request REST request.
   * @return WP_REST_Response|null
   */
  public function handleCommand(ChatbotPreparedRequest $prepared, WP_REST_Request $request): ?WP_REST_Response {
    return $this->command_handler->handle(
      $prepared->command,
      $prepared->tools,
      $prepared->ai_payload,
      $prepared->payload,
      $prepared->chatbot_settings,
      $request
    );
  }

  /**
   * Build an error REST response from a normalized error payload.
   *
   * @param array $normalized Normalized error array.
   * @return WP_REST_Response
   */
  private function makeErrorResponse(array $normalized): WP_REST_Response {
    $status = (int) ($normalized['status'] ?? 500);

    return new WP_REST_Response(
      [
        'error'        => (string) ($normalized['userMessage'] ?? __('Something went wrong.', 'kimaai')),
        'errorDetails' => $normalized,
      ],
      $status
    );
  }

  /**
   * Extract a short provider slug from the settings or payload.
   *
   * @param array $chatbot_settings Chatbot settings.
   * @param array $ai_payload AI request payload.
   * @return string
   */
  private function extractProvider(array $chatbot_settings, array $ai_payload): string {
    $provider_value = (string) ($ai_payload['aiProvider'] ?? ($chatbot_settings['chatAiProvider'] ?? 'openai'));

    return preg_match('/^[a-z]+/', $provider_value, $matches) ? $matches[0] : 'openai';
  }
}
