<?php
/**
 * OpenAI Client
 *
 * Client for interacting with OpenAI API.
 * Migrated from integrations/openai/OpenAIClient.php
 *
 * @package FleekDash\AI\Providers\OpenAI
 * @since 2.3.0
 */

namespace FleekDash\AI\Providers\OpenAI;

defined('ABSPATH') || exit;

require_once __DIR__ . '/OpenAIClientException.php';
require_once __DIR__ . '/OpenAIProviderException.php';

/**
 * Class OpenAIClient
 *
 * Handles communication with OpenAI API.
 */
class OpenAIClient
{
    /**
     * Settings
     *
     * @var array
     */
    protected $settings;

    /**
     * Constructor
     *
     * @param array $settings Provider settings
     */
    public function __construct($settings)
    {
        $this->settings = $settings;
    }

    /**
     * Make a chat request to OpenAI API
     *
     * @param array $params Request parameters
     * @param bool $stream Whether to stream the response
     * @return array|OpenAIStream Decoded JSON (non-stream) or stream handler (stream)
     * @throws OpenAIProviderException If API key is missing
     * @throws OpenAIClientException If the HTTP request fails or returns an error
     */
    public function chat($params, $stream = false)
    {
        $api_key = $this->settings['api_key'] ?? '';

        if (empty($api_key)) {
            throw new OpenAIProviderException('OpenAI API key is not configured');
        }

        // Format messages for OpenAI
        $formatted_messages = array_map(function ($message) {
            if (isset($message['tool_calls'])) {
                return [
                    'role' => 'assistant',
                    'content' => $message['content'] ?? null,
                    'tool_calls' => $message['tool_calls'],
                ];
            }
            return [
                'role' => $message['role'],
                'content' => $message['content'],
            ];
        }, $params['messages']);

        // Set up the OpenAI API request
        $ch = curl_init('https://api.openai.com/v1/chat/completions');

        // Essential cURL options
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, !$stream);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_FORBID_REUSE, $stream);
        $headers = [
            'Authorization: Bearer ' . $api_key,
            'Content-Type: application/json',
        ];

        if ($stream) {
            $headers[] = 'Accept: text/event-stream';
            $headers[] = 'Connection: keep-alive';
            $headers[] = 'Cache-Control: no-cache';
        }

        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        // Optimize for streaming performance
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);
        curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024);
        curl_setopt($ch, CURLOPT_TCP_NODELAY, 1);
        curl_setopt($ch, CURLOPT_TCP_FASTOPEN, 1);

        // Prepare the request body
        $body = [
            'model' => $params['model'] ?? $this->settings['model'] ?? 'gpt-4o',
            'messages' => $formatted_messages,
            'temperature' => $params['temperature'] ?? 0.9,
            'max_tokens' => $params['max_tokens'] ?? (int) ($this->settings['max_tokens'] ?? 4000),
            'top_p' => $params['top_p'] ?? 0.9,
            'stream' => $stream,
        ];

        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        curl_setopt($ch, CURLOPT_VERBOSE, false);

        // DEBUG: Log request details
        error_log('[OpenAIClient::chat] Sending to OpenAI: model=' . ($body['model'] ?? 'unknown') . ', stream=' . var_export($stream, true));
        error_log('[OpenAIClient::chat] API key length: ' . strlen($api_key) . ', first 8 chars: ' . substr($api_key, 0, 8));

        if ($stream) {
            error_log('[OpenAIClient::chat] Creating OpenAIStream for streaming');

            return new OpenAIStream($ch);
        } else {
            // Execute the request
            $response = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

            if (curl_errno($ch)) {
                $curlErr = curl_error($ch);
                curl_close($ch);
                throw new OpenAIClientException('Failed to connect to OpenAI API: ' . $curlErr);
            }

            curl_close($ch);

            if ($http_code !== 200) {
                throw new OpenAIClientException('OpenAI API returned HTTP ' . $http_code . ': ' . $response);
            }

            return json_decode($response, true);
        }
    }

    /**
     * Generate text using OpenAI API (non-streaming)
     *
     * @param array $params Request parameters
     * @return array Response with content
     * @throws OpenAIProviderException If API key is missing
     * @throws OpenAIClientException If the HTTP request fails or the response is invalid
     */
    public function generateText($params)
    {
        $api_key = $this->settings['api_key'] ?? '';

        if (empty($api_key)) {
            throw new OpenAIProviderException('OpenAI API key is not configured');
        }

        $request_params = [
            'model' => $params['model'] ?? 'gpt-4',
            'messages' => $params['messages'] ?? [],
            'max_tokens' => $params['max_tokens'] ?? 2000,
            'temperature' => $params['temperature'] ?? 0.7
        ];

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => 'https://api.openai.com/v1/chat/completions',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $api_key
            ],
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($request_params),
            CURLOPT_TIMEOUT => 60
        ]);

        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response === false) {
            throw new OpenAIClientException('Failed to connect to OpenAI API');
        }

        $decoded = json_decode($response, true);

        if ($http_code !== 200) {
            $error = \is_array($decoded) ? ($decoded['error']['message'] ?? 'Unknown error') : 'Unknown error';
            throw new OpenAIClientException('OpenAI API error: ' . $error);
        }

        if (!\is_array($decoded) || !isset($decoded['choices'][0]['message']['content'])) {
            throw new OpenAIClientException('Invalid response from OpenAI API');
        }

        return [
            'content' => $decoded['choices'][0]['message']['content']
        ];
    }
}
