<?php
declare(strict_types=1);

namespace App\Infrastructure\AI\GPT4All;

use App\Domain\AI\AiQuery;
use App\Domain\AI\AiQueryHandlerInterface;
use App\Domain\AI\AiResponse;
use App\Domain\AI\Exception\AiProviderUnavailableException;
use App\Domain\AI\AiProvider;
use App\Framework\Http\Headers;
use App\Framework\Http\Method;
use App\Framework\HttpClient\ClientRequest;
use App\Framework\HttpClient\HttpClient;
use App\Framework\HttpClient\Exception\CurlExecutionFailed;

final readonly class Gpt4AllQueryHandler implements AiQueryHandlerInterface
{
    public function __construct(
        private HttpClient $httpClient,
        private string $gpt4AllApiUrl = 'http://host.docker.internal:4891'
    ) {}

    public function isAvailable(): bool
    {
        try {
            $healthUrl = $this->gpt4AllApiUrl . '/health';
            $request = ClientRequest::json(Method::GET, $healthUrl, []);

            $this->httpClient->send($request);
            return true;
        } catch (CurlExecutionFailed $e) {
            return false;
        } catch (\Exception $e) {
            return false;
        }
    }

    public function __invoke(AiQuery $query): AiResponse
    {
        if (!$this->isAvailable()) {
            throw new AiProviderUnavailableException(
                AiProvider::GPT4ALL,
                "GPT4All Server ist nicht erreichbar unter {$this->gpt4AllApiUrl}. " .
                "Stellen Sie sicher, dass GPT4All läuft oder verwenden Sie einen anderen Provider."
            );
        }

        $url = $this->gpt4AllApiUrl . '/v1/chat/completions';

        $data = [
            'model' => $query->model->value,
            'messages' => $query->messages !== []
                ? $query->messages
                : [['role' => 'user', 'content' => $query->message]],
            'temperature' => $query->temperature,
        ];

        if ($query->maxTokens !== null) {
            $data['max_tokens'] = $query->maxTokens;
        }

        try {
            $request = ClientRequest::json(
                Method::POST,
                $url,
                $data
            )->with([
                'headers' => new Headers()
                    ->with('Content-Type', 'application/json')
            ]);

            $response = $this->httpClient->send($request);
            $body = json_decode($response->body, true);

            return new AiResponse(
                content: $body['choices'][0]['message']['content'] ?? '',
                provider: 'gpt4all',
                model: $query->model->value,
                tokensUsed: $body['usage']['total_tokens'] ?? null
            );
        } catch (CurlExecutionFailed $e) {
            throw new AiProviderUnavailableException(
                AiProvider::GPT4ALL,
                "Verbindung zu GPT4All fehlgeschlagen: " . $e->getMessage()
            );
        }
    }
}
