<?php
/**
 * Chatbot command handling.
 *
 * @package KimaAI\ChatBot\Tools
 */

declare(strict_types=1);

namespace KimaAI\ChatBot\Tools;

use KimaAI\Ai\AiService;
use WP_REST_Request;
use WP_REST_Response;

defined('ABSPATH') || exit;

/**
 * Handles chatbot command payloads before the model request is sent.
 */
final class ToolCommandHandler {

  /**
   * Create the command handler.
   *
   * @param AiService $ai_service AI service used to persist callback responses.
   */
  public function __construct(private AiService $ai_service) {
  }

  /**
   * Apply a command to the current chatbot request.
   *
   * @param array|null $command Chatbot command payload.
   * @param array $tools Exposed tool definitions.
   * @param array $ai_payload AI request payload, mutated for force_tool.
   * @param array $payload Chatbot request payload.
   * @param array $chatbot_settings Chatbot settings.
   * @param WP_REST_Request $request REST request.
   * @return WP_REST_Response|null
   */
  public function handle(
    ?array $command,
    array $tools,
    array &$ai_payload,
    array $payload,
    array $chatbot_settings,
    WP_REST_Request $request
  ): ?WP_REST_Response {
    if (!$command) {
      return null;
    }

    $mode = isset($command['mode']) ? sanitize_key((string) $command['mode']) : '';

    $exposed_tool_names = [];
    foreach ($tools as $tool) {
      if (is_array($tool) && !empty($tool['name']) && is_string($tool['name'])) {
        $exposed_tool_names[$tool['name']] = true;
      }
    }

    if ($mode === 'force_tool') {
      $tool = isset($command['tool']) ? (string) $command['tool'] : '';

      if (empty($tool) || empty($exposed_tool_names[$tool])) {
        return new WP_REST_Response(['error' => __('Invalid or disallowed tool for command.', 'kimaai')], 400);
      }

      $ai_payload['toolChoice'] = [
        'type' => 'function',
        'name' => $tool,
      ];
      $ai_payload['parallelToolCalls'] = isset($command['parallel']) && (bool) $command['parallel'];
      $ai_payload['kbContext'] = trim((string) ($ai_payload['kbContext'] ?? '') . "\n\n" . sprintf('You must call the tool "%s" to answer the user request.', $tool));

      return null;
    }

    if ($mode === 'callback') {
      return $this->handleCallback($command, $ai_payload, $payload, $chatbot_settings, $request);
    }

    return null;
  }

  /**
   * Execute a server-side command callback and persist the fabricated response.
   *
   * @param array $command Chatbot command payload.
   * @param array $ai_payload AI request payload.
   * @param array $payload Chatbot request payload.
   * @param array $chatbot_settings Chatbot settings.
   * @param WP_REST_Request $request REST request.
   * @return WP_REST_Response
   */
  private function handleCallback(
    array $command,
    array $ai_payload,
    array $payload,
    array $chatbot_settings,
    WP_REST_Request $request
  ): WP_REST_Response {
    $name = isset($command['name']) ? sanitize_key((string) $command['name']) : '';
    $args = isset($command['args']) && is_array($command['args']) ? $command['args'] : [];

    if (empty($name)) {
      return new WP_REST_Response(['error' => __('Invalid callback name.', 'kimaai')], 400);
    }

    $registry = apply_filters('kimaai_chatbot_command_callbacks', [], $payload, $chatbot_settings);
    $callback = is_array($registry) ? ($registry[$name] ?? null) : null;

    if (!is_callable($callback)) {
      return new WP_REST_Response(['error' => __('Unknown callback command.', 'kimaai')], 400);
    }

    try {
      $result = call_user_func(
        $callback,
        $args,
        [
          'payload' => $payload,
          'chatbot_settings' => $chatbot_settings,
          'request' => $request,
        ]
      );
    } catch (\Throwable $exception) {
      return new WP_REST_Response(['error' => $exception->getMessage()], 500);
    }

    $bot_text = is_string($result) ? $result : wp_json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $fake_openai_response = [
      'output' => [
        [
          'type' => 'message',
          'role' => 'assistant',
          'content' => [
            [
              'type' => 'output_text',
              'text' => (string) $bot_text,
            ],
          ],
        ],
      ],
    ];

    $provider_payload = $this->ai_service->resolveChatPayload('openai', (string) ($ai_payload['aiModel'] ?? 'gpt-4o'), $ai_payload, false);
    $saved = $this->ai_service->saveChatResponse('openai', $provider_payload, $fake_openai_response);

    return new WP_REST_Response(
      [
        'message' => $saved['message'] ?? (string) $bot_text,
        'sessionId' => $payload['sessionId'],
      ],
      200
    );
  }
}
