<?php

namespace App\Api\Plugins;

use App\Models\ApiProvider;
use App\Models\Image;
use App\Models\Style;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use RuntimeException;

class LeonardoAi implements ApiPluginInterface
{
    use LoggablePlugin;

    private const BASE_URL = 'https://cloud.leonardo.ai/api/rest/v2';

    private const SUPPORTED_MODELS = [
        'gemini-image-2', // Nano Banana Pro
        'flux-pro-2.0',   // FLUX.2 Pro
        'seedream-4.0',   // Seedream 4.0
    ];

    public function __construct(private ApiProvider $apiProvider) {}

    public function getIdentifier(): string
    {
        return 'leonardoai';
    }

    public function getName(): string
    {
        return 'Leonardo AI';
    }

    public function isEnabled(): bool
    {
        return $this->apiProvider->enabled;
    }

    public function enable(): bool
    {
        $this->apiProvider->enabled = true;

        return $this->apiProvider->save();
    }

    public function disable(): bool
    {
        $this->apiProvider->enabled = false;

        return $this->apiProvider->save();
    }

    public function getStatus(string $imageUUID): array
    {
        // Not supported directly; handled via getStyledImage polling.
        return ['status' => 'unknown'];
    }

    public function getProgress(string $imageUUID): array
    {
        return ['progress' => 0];
    }

    public function processImageStyleChange(Image $image, Style $style): array
    {
        $this->assertApiConfig();

        $modelId = $style->aiModel?->model_id;
        if (! $modelId || ! in_array($modelId, self::SUPPORTED_MODELS, true)) {
            throw new RuntimeException('Unsupported Leonardo model. Please choose a v2 model (Nano Banana Pro, FLUX.2 Pro, Seedream 4.0).');
        }

        $referenceId = $this->uploadReferenceImage($image);

        $width = $style->parameters['width'] ?? 1024;
        $height = $style->parameters['height'] ?? 1024;
        $styleIds = $style->parameters['style_ids'] ?? [];
        $promptEnhance = $style->parameters['prompt_enhance'] ?? 'OFF';

        $payload = [
            'model' => $modelId,
            'parameters' => [
                'width' => $width,
                'height' => $height,
                'prompt' => $style->prompt,
                'quantity' => 1,
                'guidances' => [
                    'image_reference' => [[
                        'image' => [
                            'id' => $referenceId,
                            'type' => 'UPLOADED',
                        ],
                        'strength' => 'MID',
                    ]],
                ],
                'style_ids' => $styleIds,
                'prompt_enhance' => $promptEnhance,
            ],
            'public' => false,
        ];

        $response = $this->http()->post(self::BASE_URL.'/generations', $payload);
        $data = $response->json();

        $this->throwOnApiError($response, $data);

        $generationId = $data['generationId'] ?? $data['id'] ?? $data['data'][0]['generationId'] ?? $data['data'][0]['id'] ?? null;
        if (! $generationId) {
            $this->logError('Leonardo generation response missing generationId', ['response' => $data]);
            throw new RuntimeException('Leonardo generation did not return an id.');
        }

        // Return identifier to allow polling
        return [
            'prompt_id' => $generationId,
            'plugin' => 'LeonardoAi',
        ];
    }

    public function getStyledImage(string $promptId): string
    {
        $this->assertApiConfig();

        $response = $this->http()->get(self::BASE_URL.'/generations/'.$promptId);
        $data = $response->json();

        $this->throwOnApiError($response, $data);

        $images = $data['images'] ?? $data['data'][0]['images'] ?? null;
        if (! $images || ! isset($images[0]['url'])) {
            throw new RuntimeException('Leonardo generation not ready yet or no image URL returned.');
        }

        return $images[0]['url'];
    }

    public function testConnection(array $data): bool
    {
        $apiKey = $data['token'] ?? null;
        if (! $apiKey) {
            throw new RuntimeException('API key fehlt.');
        }

        $payload = [
            'model' => self::SUPPORTED_MODELS[0],
            'parameters' => [
                'prompt' => 'health check',
                'width' => 512,
                'height' => 512,
                'quantity' => 1,
            ],
            'public' => false,
        ];

        $response = $this->http($apiKey)->post(self::BASE_URL.'/generations', $payload);
        $json = $response->json();

        $this->throwOnApiError($response, $json);

        return true;
    }

    public function searchModels(string $searchTerm): array
    {
        $models = [
            ['name' => 'Nano Banana Pro', 'id' => 'gemini-image-2'],
            ['name' => 'FLUX.2 Pro', 'id' => 'flux-pro-2.0'],
            ['name' => 'Seedream 4.0', 'id' => 'seedream-4.0'],
        ];

        return array_values(array_filter($models, function ($model) use ($searchTerm) {
            return stripos($model['name'], $searchTerm) !== false || stripos($model['id'], $searchTerm) !== false;
        }));
    }

    public function checkAvailability(): array
    {
        try {
            $this->testConnection($this->apiProvider->toArray());

            return [
                'available' => true,
                'reason' => 'Connection successful',
                'provider_id' => $this->apiProvider->id,
                'provider_name' => $this->apiProvider->name,
            ];
        } catch (\Exception $e) {
            return [
                'available' => false,
                'reason' => $e->getMessage(),
                'provider_id' => $this->apiProvider->id,
                'provider_name' => $this->apiProvider->name,
            ];
        }
    }

    private function uploadReferenceImage(Image $image): string
    {
        $path = public_path('storage/'.$image->path);

        if (! file_exists($path)) {
            throw new RuntimeException('Bild nicht gefunden: '.$path);
        }

        $response = $this->http()->attach('image', file_get_contents($path), basename($path))
            ->post(self::BASE_URL.'/init-image', [
                'extension' => pathinfo($path, PATHINFO_EXTENSION),
            ]);

        $json = $response->json();
        $this->throwOnApiError($response, $json);

        $id = $json['id'] ?? $json['imageId'] ?? $json['uploadId'] ?? $json['data'][0]['id'] ?? null;

        if (! $id) {
            $this->logError('Leonardo upload response missing id', ['response' => $json]);
            throw new RuntimeException('Upload to Leonardo failed (no id returned).');
        }

        return $id;
    }

    private function http(?string $apiKey = null)
    {
        $key = $apiKey ?? $this->apiProvider->token;

        if (! $key) {
            throw new RuntimeException('Leonardo API key ist nicht gesetzt.');
        }

        return Http::withHeaders([
            'Authorization' => 'Bearer '.$key,
            'Accept' => 'application/json',
        ]);
    }

    private function throwOnApiError($response, ?array $data): void
    {
        if ($response->successful() && empty($data['errors'])) {
            return;
        }

        $error = $data['errors'][0] ?? null;

        if ($error) {
            $code = $error['code'] ?? 'error';
            $message = $error['message'] ?? 'API error';
            $task = $error['taskUUID'] ?? null;
            $detail = trim(($code ? $code.': ' : '').$message.' '.($task ? "(task {$task})" : ''));
            throw new RuntimeException($detail);
        }

        $status = $response->status();
        $body = $response->body();
        throw new RuntimeException('Leonardo API Fehler '.$status.($body ? ': '.Str::limit($body, 200) : ''));
    }

    private function assertApiConfig(): void
    {
        if (! $this->apiProvider->enabled) {
            throw new RuntimeException('API Provider ist deaktiviert.');
        }

        if (! $this->apiProvider->token) {
            throw new RuntimeException('Leonardo API key fehlt.');
        }
    }
}
