<?php

namespace FSPoster\App\AI\Api\Gemini;

use Exception;
use FSPoster\App\AI\Helpers\AIHelper;
use FSPoster\App\AI\Helpers\AIImageGeneratorResponse;
use FSPoster\App\AI\Helpers\AITextGeneratorResponse;
use FSPoster\App\Models\AITemplate;
use FSPoster\App\Providers\DB\Collection;
use FSPoster\App\Providers\Helpers\GuzzleClient;

class Api
{
    private string $key;
    public ?string $proxy = null;
    private ?GuzzleClient $client = null;
    private const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/';

    public function __construct(string $key)
    {
        $this->key = $key;
    }

    public function setProxy(?string $proxy): self
    {
        $this->proxy  = $proxy;
        $this->client = null;

        return $this;
    }

    public static function normalizeModel(string $model): string
    {
        $models = [
            'gemini-2.5-pro-preview-05-06'   => 'gemini-3-pro-preview',
            'gemini-2.5-pro-preview-06-05'   => 'gemini-3-pro-preview',
            'gemini-2.5-pro'                 => 'gemini-3-pro-preview',
            'gemini-2.5-flash-preview-05-20' => 'gemini-2.5-flash',
            'gemini-2.0-flash'               => 'gemini-2.5-flash',
            'gemini-2.0-flash-001'           => 'gemini-2.5-flash',
            'gemini-2.0-flash-lite'          => 'gemini-2.5-flash-lite',
            'gemini-2.0-flash-lite-001'      => 'gemini-2.5-flash-lite',
            'imagen-3.0-generate-002'        => 'imagen-4.0-generate-001',
        ];

        return $models[$model] ?? $model;
    }

    public static function isTextModel(string $model): bool
    {
        return preg_match('/^gemini-/', self::normalizeModel($model)) === 1;
    }

    public static function isImageModel(string $model): bool
    {
        return preg_match('/^imagen-/', self::normalizeModel($model)) === 1;
    }

    private function getClient(): GuzzleClient
    {
        if ($this->client === null) {
            $this->client = new GuzzleClient([
                'base_uri' => self::GEMINI_API_URL,
                'proxy'    => empty($this->proxy) ? null : $this->proxy,
                'verify'   => true,
                'headers'  => [
                    'x-goog-api-key' => $this->key,
                ],
            ]);
        }

        return $this->client;
    }

    /** @param Collection|AITemplate $template */
    public function generateText(string $renderedPrompt, Collection $template): AITextGeneratorResponse
    {
        $model = self::normalizeModel($template->ai_model);
        $aiResponse = new AITextGeneratorResponse();

        $aiResponse->model      = $model;
        $aiResponse->provider   = 'gemini';
        $aiResponse->endpoint   = '';
        $aiResponse->templateId = $template->id;
        $aiResponse->prompt     = $renderedPrompt;
        $aiResponse->body       = '';

        if (! self::isTextModel($model)) {
            $aiResponse->rawResponse = fsp__('Selected Gemini model is not supported for text generation.');
            $aiResponse->status      = 'fail';

            return $aiResponse;
        }

        $endpoint = sprintf('v1beta/models/%s:generateContent', $model);

        $body = [
            'contents'         => [
                [
                    'parts' => [
                        [ 'text' => $renderedPrompt ]
                    ]
                ]
            ],
            'generationConfig' => [
                'temperature' => $template->config_obj->temperature
            ]
        ];

        $aiResponse->endpoint   = $endpoint;
        $aiResponse->body       = json_encode($body);

        try {
            $response = (string) $this->getClient()->post($endpoint, [
                'json' => $body
            ])->getBody();

            $aiResponse->rawResponse = $response;

            $response = json_decode($response, true);

            if (! isset($response['candidates'][0]['content']['parts'][0]['text'])) {
                $aiResponse->status = 'fail';
            } else {
                $aiResponse->status          = 'success';
                $aiResponse->response        = trim($response['candidates'][0]['content']['parts'][0]['text'], '"');
                $aiResponse->aiGeneratedText = trim($response['candidates'][0]['content']['parts'][0]['text'], '"');
            }
        } catch (Exception $e) {
            $aiResponse->rawResponse = $e->getMessage();
            $aiResponse->status      = 'fail';
        }

        return $aiResponse;
    }

    /**
     * @param string $renderedPrompt
     * @param Collection $template
     * @return AIImageGeneratorResponse
     */
    public function generateImage(string $renderedPrompt, Collection $template): AIImageGeneratorResponse
    {
        $model = self::normalizeModel($template->ai_model);
        $aiResponse = new AIImageGeneratorResponse();

        $aiResponse->model      = $model;
        $aiResponse->provider   = 'gemini';
        $aiResponse->endpoint   = '';
        $aiResponse->templateId = $template->id;
        $aiResponse->prompt     = $renderedPrompt;
        $aiResponse->body       = '';

        if (! self::isImageModel($model)) {
            $aiResponse->rawResponse = fsp__('Selected Gemini model is not supported for image generation.');
            $aiResponse->status      = 'fail';

            return $aiResponse;
        }

        $endpoint = sprintf('v1beta/models/%s:predict', $model);

        $body = [
            'instances'  => [
                [ 'prompt' => $renderedPrompt ]
            ],
            'parameters' => [
                'sampleCount' => 1,
                'aspectRatio' => $template->config_obj->aspectRatio,
            ]
        ];

        $aiResponse->endpoint   = $endpoint;
        $aiResponse->body       = json_encode($body);

        try {
            $response = (string) $this->getClient()->post($endpoint, [
                'json' => $body
            ])->getBody();

            $aiResponse->rawResponse = $response;

            $response = json_decode($response, true);

            if (! isset($response['predictions'][0]['bytesBase64Encoded'])) {
                $aiResponse->status = 'fail';
            } else {
                $id                     = AIHelper::createImageAttachmentFromBase64($template->provider, $response['predictions'][0]['bytesBase64Encoded']);
                $aiResponse->status     = 'success';
                $aiResponse->response   = $id;
                $aiResponse->attachmentId = $id;
            }
        } catch (Exception $e) {
            $aiResponse->rawResponse = $e->getMessage();
            $aiResponse->status      = 'fail';
        }

        return $aiResponse;
    }
}
