<?php

namespace app\service\Embedding;

use app\service\LLM\LLMRequestException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use RuntimeException;
use Throwable;

class BigModelEmbeddingClient
{
    private Client $client;
    private array $config;

    public function __construct(?array $config = null)
    {
        $this->config = $config ?? config('LLMapi.embedding', []);
        $baseUrl = rtrim((string) ($this->config['base_url'] ?? ''), '/');

        $this->client = new Client([
            'base_uri' => $baseUrl . '/',
            'timeout' => (int) ($this->config['timeout'] ?? 60),
            'connect_timeout' => (int) ($this->config['connect_timeout'] ?? 10),
        ]);
    }

    public function isConfigured(): bool
    {
        return trim((string) ($this->config['api_key'] ?? '')) !== ''
            && trim((string) ($this->config['base_url'] ?? '')) !== '';
    }

    public function embed(array $texts, array $options = []): array
    {
        if (!$this->isConfigured()) {
            throw new RuntimeException('BigModel embedding API is not configured.');
        }

        $texts = array_values($texts);
        if ($texts === []) {
            return [
                'model' => (string) ($options['model'] ?? $this->config['model'] ?? 'embedding-3'),
                'data' => [],
                'usage' => [],
            ];
        }

        $batchSize = (int) ($this->config['batch_size'] ?? 64);
        if (count($texts) > $batchSize) {
            throw new RuntimeException("Embedding batch exceeds configured batch size {$batchSize}.");
        }

        $body = [
            'model' => $options['model'] ?? $this->config['model'] ?? 'embedding-3',
            'input' => $texts,
        ];

        $dimensions = $options['dimensions'] ?? $this->config['dimensions'] ?? null;
        if ($dimensions !== null) {
            $dimensions = (int) $dimensions;
            if (!in_array($dimensions, [256, 512, 1024, 2048], true)) {
                throw new RuntimeException('Embedding dimensions must be one of 256, 512, 1024, or 2048.');
            }
            $body['dimensions'] = $dimensions;
        }

        try {
            $response = $this->client->post('embeddings', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->config['api_key'],
                    'Content-Type' => 'application/json',
                ],
                'json' => $body,
            ]);
        } catch (RequestException $exception) {
            throw $this->requestException($exception);
        } catch (Throwable $exception) {
            throw new RuntimeException('BigModel embedding request failed: ' . $exception->getMessage(), 0, $exception);
        }

        $payload = json_decode((string) $response->getBody(), true);
        if (!is_array($payload) || !isset($payload['data']) || !is_array($payload['data'])) {
            throw new RuntimeException('BigModel embedding response is invalid.');
        }

        return $payload;
    }

    private function requestException(RequestException $exception): LLMRequestException
    {
        $statusCode = $exception->getResponse()?->getStatusCode();
        $body = $exception->getResponse() ? (string) $exception->getResponse()->getBody() : '';
        $payload = json_decode($body, true);
        $providerCode = null;
        $providerMessage = null;

        if (is_array($payload)) {
            $providerCode = isset($payload['error']['code']) ? (string) $payload['error']['code'] : null;
            $providerMessage = isset($payload['error']['message']) ? (string) $payload['error']['message'] : null;

            if ($providerCode === null && isset($payload['code'])) {
                $providerCode = (string) $payload['code'];
            }
            if ($providerMessage === null && isset($payload['message'])) {
                $providerMessage = (string) $payload['message'];
            }
        }

        $message = 'BigModel embedding request failed';
        if ($statusCode !== null) {
            $message .= " with HTTP {$statusCode}";
        }
        if ($providerCode !== null) {
            $message .= " and provider code {$providerCode}";
        }
        if ($providerMessage !== null) {
            $message .= ": {$providerMessage}";
        } else {
            $message .= ': ' . $exception->getMessage();
        }

        return new LLMRequestException($message, $statusCode, $providerCode, is_array($payload) ? $payload : null);
    }
}
