<?php

namespace Breakdance\AI;

use CurlHandle;
use Orhanerday\OpenAi\OpenAi;

add_action('breakdance_loaded', function () {
    \Breakdance\AJAX\register_handler(
        'breakdance_ai_request_completion',
        'Breakdance\AI\requestCompletion',
        'edit',
        false,
        [
            'args' => [
                'messages' => FILTER_UNSAFE_RAW,
                'format' => FILTER_SANITIZE_SPECIAL_CHARS,
            ]
        ]
    );

    \Breakdance\AJAX\register_handler(
        'breakdance_ai_get_suggested_prompts',
        'Breakdance\AI\getSuggestedPrompts',
        'edit',
        false,
        [
            'args' => [
                'prompt' => FILTER_SANITIZE_SPECIAL_CHARS,
            ]
        ]
    );
});

/**
 * @param string $messages
 * @param string $format
 * @return array{data:string}
 * @throws \Exception
 */
function requestCompletion($messages, $format) {
    $response = talkToOpenAi(
        json_decode($messages, true),
        $format
    );

    return $response;
}

/**
 * @param array $messages
 * @param string $format
 * @param int|null $temperature
 * @param bool $stream
 * @return string|array{error:string}|array{data:string}
 * @throws \Exception
 */
function talkToOpenAi($messages, $format, $temperature = null, $stream = false) {
    if ($stream) {
        header('Content-type: text/event-stream');
        header('Cache-Control: no-cache');
        header("X-Accel-Buffering: no");
    }

    $key = \Breakdance\APIKeys\getKey('openai');

    if (!$key) {
        showError('Please enter an OpenAI API key in the settings page.', $stream);
    }

    if (isRateLimited()) {
        showError('You have exceeded your daily usage limit, please reset it and try again.', $stream);
    }

    $open_ai = getOpenAi($key);

    $defaults = [
        'temperature' => 1.0,
    ];

    $formats = [
        'json' => [
            'temperature' => 1.0,
            'response_format' => [
                'type' => 'json_object'
            ]
        ],
        'css' => [
            'temperature' => 0.2
        ],
    ];

    $opts = array_merge(
        $defaults,
        $formats[$format] ?? [],
        [
            'model' => getModel(),
            'messages' => $messages,
            "stream" => $stream
        ]
    );

    if ($temperature !== null) {
        $opts['temperature'] = $temperature;
    }

    if ($stream) {
        $output = '';

        $open_ai->chat($opts, function (CurlHandle $ch, $data) use (&$output) {
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

            if ($httpCode !== 200) {
                echo "event: error\n";
                echo "data: " . json_encode($data) . "\n\n";
                ob_flush();
                flush();
                return strlen($data);
            }

            $chunks = explode('data: ', $data);
            foreach ($chunks as $chunk) {
                $output .= getChunkMessage($chunk);
            }

            echo $data;
            echo PHP_EOL;
            ob_flush();
            flush();
            return strlen($data);
        });

        trackUsage($messages, $output);
        exit();
    } else {
        $chat = $open_ai->chat($opts);
        $completion = json_decode($chat);

        if (isset($completion->error)) {
            return ['error' => $completion->error->message];
        }

        $data = $completion->choices[0]->message->content;
        trackUsage($messages, $data);
        return ['data' => $data];
    }
}

function getSuggestedPrompts($prompt) {
    $key = \Breakdance\APIKeys\getKey('openai');

    if (!$key) {
        return ['error' => 'Please enter an OpenAI API key in the settings page.'];
    }

    $messages = [
        [
            'content' => getSuggestionsPrompt(),
            'role' => 'system'
        ],
        [
            'content' => "Element name: $prompt",
            'role' => 'user'
        ]
    ];

    $open_ai = getOpenAi($key);

    $chat = $open_ai->chat([
        'model' => getModel(),
        'messages' => $messages,
        'max_tokens' => 500,
        "response_format" => [
            'type' => 'json_object'
        ]
    ]);

    $completion = json_decode($chat);

    if (isset($completion->error)) {
        return ['error' => $completion->error->message];
    }

    $data = $completion->choices[0]->message->content;
    return ['data' => json_decode($data)];
}

function streamEndpoint() {
    $messages = getPersistedMessages();
    $format = getPersistedFormat();
    $temperature = getPersistedTemperature();

    talkToOpenAi(
        json_decode($messages, true),
        $format,
        $temperature,
        true
    );
}

function showError($message, $stream = false) {
    if ($stream) {
        echo "event: error\n";
        echo "data: " . json_encode([
            'error' => [
                'message' => $message
            ]
        ]) . "\n\n";
        ob_flush();
        flush();
        exit();
    } else {
        return json_encode(['error' => $message]);
    }
}

add_action('rest_api_init', function () {
    register_rest_route('breakdance/v1', '/ai/stream-completion', [
        'methods' => 'GET',
        'callback' => '\Breakdance\AI\streamEndpoint',
        'permission_callback' => function () {
            return \Breakdance\Permissions\hasMinimumPermission(getAiRole());
        }
    ]);
});
