<?php

namespace App\Api\Plugins;

use App\Models\ApiProvider;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

class RunwareAi implements ApiPluginInterface
{
    use LoggablePlugin;

    protected $apiProvider;

    public function __construct(ApiProvider $apiProvider)
    {
        $this->apiProvider = $apiProvider;
        $this->logInfo('RunwareAi plugin initialized.', ['provider_name' => $apiProvider->name]);
    }

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

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

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

    public function enable(): bool
    {
        $this->apiProvider->enabled = true;
        $result = $this->apiProvider->save();
        if ($result) {
            $this->logInfo('RunwareAi plugin enabled.', ['provider_name' => $this->apiProvider->name]);
        } else {
            $this->logError('Failed to enable RunwareAi plugin.', ['provider_name' => $this->apiProvider->name]);
        }

        return $result;
    }

    public function disable(): bool
    {
        $this->apiProvider->enabled = false;
        $result = $this->apiProvider->save();
        if ($result) {
            $this->logInfo('RunwareAi plugin disabled.', ['provider_name' => $this->apiProvider->name]);
        } else {
            $this->logError('Failed to disable RunwareAi plugin.', ['provider_name' => $this->apiProvider->name]);
        }

        return $result;
    }

    public function getStyledImage(string $promptId): string
    {
        throw new \Exception('RunwareAi does not support fetching styled images by prompt ID.');
    }

    public function getStatus(string $imageUUID): array
    {
        $this->logDebug('Getting status for image.', ['image_uuid' => $imageUUID]);

        // Implement RunwareAI specific status check
        return ['status' => 'unknown'];
    }

    public function getProgress(string $imageUUID): array
    {
        $this->logDebug('Getting progress for image.', ['image_uuid' => $imageUUID]);

        // Implement RunwareAI specific progress check
        return ['progress' => 0];
    }

    public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array
    {
        // Step 1: Upload the original image
        $uploadResult = $this->upload(public_path('storage/'.$image->path));

        if (! isset($uploadResult['data'][0]['imageUUID'])) {
            throw new \Exception('Image upload to AI service failed or returned no UUID.');
        }
        $seedImageUUID = $uploadResult['data'][0]['imageUUID'];

        // Step 2: Request style change using the uploaded image's UUID
        $result = $this->styleChangeRequest($style, $seedImageUUID);

        if (! isset($result['base64Data'])) {
            throw new \Exception('AI service did not return base64 image data.');
        }

        return $result;
    }

    public function testConnection(array $data): bool
    {
        $apiUrl = rtrim($data['api_url'], '/');
        $token = $data['token'] ?? null;

        if (! $apiUrl || ! $token) {
            $this->logError('RunwareAI connection test failed: API URL or Token missing.');
            throw new \RuntimeException('API URL oder Token fehlt.');
        }

        try {
            $payload = [[
                'taskType' => 'authentication',
                'apiKey' => $token,
                'taskUUID' => (string) Str::uuid(),
            ]];

            $response = Http::withHeaders([
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ])->timeout(5)->post($apiUrl, $payload);

            $responseData = $response->json();

            // Official Runware format: errors array with code/message.
            if (isset($responseData['errors'][0])) {
                $error = $responseData['errors'][0];
                $messageParts = [
                    $error['code'] ?? 'error',
                    $error['message'] ?? null,
                ];
                $detail = implode(': ', array_filter($messageParts));

                $this->logError('RunwareAI connection test failed: API returned error.', [
                    'status' => $response->status(),
                    'error' => $error,
                ]);

                throw new \RuntimeException($detail ?: 'Runware API Fehler.');
            }

            if ($response->failed()) {
                $this->logError('RunwareAI connection test failed: HTTP error.', [
                    'status' => $response->status(),
                    'body' => $response->body(),
                ]);

                throw new \RuntimeException('HTTP '.$response->status().' beim Test der Runware-API.');
            }

            $this->logInfo('RunwareAI connection test successful: Authentication successful.', [
                'status' => $response->status(),
                'response' => $responseData,
            ]);

            return true;
        } catch (\Exception $e) {
            $this->logError('RunwareAI connection test failed: Exception caught.', ['error' => $e->getMessage()]);
            throw $e;
        }
    }

    public function checkAvailability(): array
    {
        $this->logInfo('Checking RunwareAI availability.');

        if (! $this->apiProvider->enabled) {
            $this->logDebug('RunwareAI provider is disabled.');

            return [
                'available' => false,
                'reason' => 'Provider is disabled',
                'provider_id' => $this->apiProvider->id,
                'provider_name' => $this->apiProvider->name,
            ];
        }

        if (empty($this->apiProvider->api_url)) {
            $this->logDebug('RunwareAI API URL is not configured.');

            return [
                'available' => false,
                'reason' => 'API URL not configured',
                'provider_id' => $this->apiProvider->id,
                'provider_name' => $this->apiProvider->name,
            ];
        }

        if (empty($this->apiProvider->token)) {
            $this->logDebug('RunwareAI API token is not configured.');

            return [
                'available' => false,
                'reason' => 'API token not configured',
                'provider_id' => $this->apiProvider->id,
                'provider_name' => $this->apiProvider->name,
            ];
        }

        try {
            $response = Http::withHeaders([
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ])->timeout(5)->post(rtrim($this->apiProvider->api_url, '/'), [
                'taskType' => 'authentication',
                'apiKey' => $this->apiProvider->token,
                'taskUUID' => (string) Str::uuid(),
            ]);

            $responseData = $response->json();

            if ($response->successful() && isset($responseData['data']) && ! isset($responseData['error'])) {
                $this->logInfo('RunwareAI is available.');

                return [
                    'available' => true,
                    'reason' => 'Connection successful',
                    'provider_id' => $this->apiProvider->id,
                    'provider_name' => $this->apiProvider->name,
                ];
            } else {
                $errorMessage = $responseData['error'] ?? 'Unknown error';
                $this->logError('RunwareAI connection failed.', [
                    'status' => $response->status(),
                    'error_message' => $errorMessage,
                ]);

                return [
                    'available' => false,
                    'reason' => 'Connection failed: '.$errorMessage,
                    'provider_id' => $this->apiProvider->id,
                    'provider_name' => $this->apiProvider->name,
                ];
            }
        } catch (\Exception $e) {
            $this->logError('RunwareAI availability check failed.', ['error' => $e->getMessage()]);

            return [
                'available' => false,
                'reason' => 'Connection error: '.$e->getMessage(),
                'provider_id' => $this->apiProvider->id,
                'provider_name' => $this->apiProvider->name,
            ];
        }
    }

    public function searchModels(string $searchTerm): array
    {
        $this->logInfo('Attempting model search on RunwareAI.', ['searchTerm' => $searchTerm]);
        if (! $this->apiProvider->api_url || ! $this->apiProvider->token) {
            $this->logError('RunwareAI API URL or Token not configured for model search.', ['provider_name' => $this->apiProvider->name]);

            return [];
        }

        $apiUrl = rtrim($this->apiProvider->api_url, '/');
        $token = $this->apiProvider->token;

        try {
            $response = Http::withHeaders([
                'Authorization' => 'Bearer '.$token,
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
            ])->timeout(10)->post($apiUrl, [
                [
                    'taskType' => 'modelSearch',
                    'search' => $searchTerm,
                    'type' => 'base',
                    'category' => 'checkpoint',
                    'limit' => 100,
                    'taskUUID' => (string) Str::uuid(),
                ],
            ]);

            $responseData = $response->json();

            if ($response->successful() && isset($responseData['data'][0]['results']) && ! isset($responseData['error'])) {
                $models = [];
                foreach ($responseData['data'][0]['results'] as $model) {
                    $models[] = [
                        'name' => $model['name'] ?? 'Unknown',
                        'id' => $model['air'] ?? 'Unknown',
                        'type' => $model['type'] ?? null,
                    ];
                }
                $this->logInfo('Model search successful on RunwareAI.', ['searchTerm' => $searchTerm, 'modelsFound' => count($models)]);

                return $models;
            } else {
                $errorMessage = $responseData['error'] ?? 'Unknown error';
                $this->logError('Model search failed on RunwareAI: Unsuccessful response.', [
                    'status' => $response->status(),
                    'response' => $responseData,
                    'error_message' => $errorMessage,
                ]);

                return [];
            }
        } catch (\Exception $e) {
            $this->logError('Model search failed on RunwareAI: Exception caught.', ['error' => $e->getMessage()]);

            return [];
        }
    }

    private function upload(string $imagePath): array
    {
        $this->logInfo('Attempting to upload image to RunwareAI.', ['image_path' => $imagePath]);
        if (! $this->apiProvider->api_url || ! $this->apiProvider->token) {
            $this->logError('RunwareAI API URL or Token not configured for upload.', ['provider_name' => $this->apiProvider->name]);
            throw new \Exception('RunwareAI API URL or Token not configured.');
        }

        $apiUrl = rtrim($this->apiProvider->api_url, '/');
        $token = $this->apiProvider->token;
        $taskUUID = (string) Str::uuid();

        $imageData = 'data:image/png;base64,'.base64_encode(file_get_contents($imagePath));

        try {
            $response = Http::withHeaders([
                'Authorization' => 'Bearer '.$token,
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
            ])->post($apiUrl, [
                [
                    'taskType' => 'imageUpload',
                    'taskUUID' => $taskUUID,
                    'image' => $imageData,
                ],
            ]);

            $response->throw();
            $this->logInfo('Image uploaded successfully to RunwareAI.', ['task_uuid' => $taskUUID, 'response' => $response->json()]);

            return $response->json();
        } catch (\Exception $e) {
            $this->logError('Image upload to RunwareAI failed.', ['error' => $e->getMessage(), 'image_path' => $imagePath]);
            throw $e;
        }
    }

    private function styleChangeRequest(\App\Models\Style $style, string $seedImageUUID): array
    {
        $this->logInfo('Attempting style change request to RunwareAI.', ['style_id' => $style->id, 'seed_image_uuid' => $seedImageUUID]);
        if (! $this->apiProvider->api_url || ! $this->apiProvider->token) {
            $this->logError('RunwareAI API URL or Token not configured for style change.', ['provider_name' => $this->apiProvider->name]);
            throw new \Exception('RunwareAI API URL or Token not configured.');
        }

        $apiUrl = rtrim($this->apiProvider->api_url, '/');
        $token = $this->apiProvider->token;
        $taskUUID = (string) Str::uuid();

        $modelParams = $style->aiModel->parameters ?? [];
        $styleParams = $style->parameters ?? [];
        $mergedParams = array_replace_recursive($modelParams, $styleParams);

        $data = [
            'taskType' => 'imageInference',
            'taskUUID' => $taskUUID,
            'positivePrompt' => $style->prompt,
            'seedImage' => $seedImageUUID,
            'outputType' => 'base64Data',
            'model' => $style->aiModel->model_id,
        ];

        foreach ($mergedParams as $key => $value) {
            $data[$key] = $value;
        }

        try {
            $response = Http::withHeaders([
                'Authorization' => 'Bearer '.$token,
                'Accept' => 'application/json',
            ])->post($apiUrl, [
                $data,
            ]);

            $response->throw();
            $responseData = $response->json();

            if (! isset($responseData['data'][0]['imageBase64Data'])) {
                throw new \Exception('AI service did not return base64 image data.');
            }

            $base64Image = $responseData['data'][0]['imageBase64Data'];

            $this->logInfo('Style change request successful to RunwareAI.', ['task_uuid' => $taskUUID, 'response' => $responseData]);

            return ['base64Data' => $base64Image];
        } catch (\Exception $e) {
            $errorData = [];
            /*if ($e instanceof \Illuminate\Http\Client\RequestException && $e->response) {
                $errorData['response_body'] = $e->response->body();
                $errorData['response_status'] = $e->response->status();
            }*/
            $this->logError('Style change request to RunwareAI failed.', ['error' => $e->getMessage(), 'task_uuid' => $taskUUID] + $errorData);
            throw $e;
        }
    }
}
