<?php

namespace App\Api\Plugins;

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

class ComfyUi implements ApiPluginInterface
{
    use LoggablePlugin;

    protected $apiProvider;

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

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

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

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

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

        return $result;
    }

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

        return $result;
    }

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

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

    public function getProgress(string $imageUUID): array
    {
        $this->logDebug('Progress updates are handled via WebSocket.', ['image_uuid' => $imageUUID]);

        return ['progress' => 0]; // Progress is now handled by WebSocket
    }

    private function getHistory(string $promptId): array
    {
        // This method is no longer used for progress polling, but might be used for final result retrieval
        $apiUrl = rtrim($this->apiProvider->api_url, '/');
        $timeout = 60; // seconds
        $this->logDebug('ComfyUI History API URL:', ['url' => $apiUrl.'/history/'.$promptId, 'timeout' => $timeout]);
        $response = Http::timeout($timeout)->get($apiUrl.'/history/'.$promptId);
        if ($response->failed()) {
            throw new \Exception('Failed to get history from ComfyUI');
        }

        return $response->json();
    }

    public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array
    {
        $this->logInfo('Starting ComfyUI style change process.', ['image_id' => $image->id, 'style_id' => $style->id]);

        // 1. Upload image to ComfyUI
        $uploadResponse = $this->uploadImage(public_path('storage/'.$image->path));
        $filename = $uploadResponse['name'];

        // 2. Construct the prompt
        $promptData = $this->constructPrompt($style, $filename);

        // 3. Queue the prompt
        $queueResponse = $this->queuePrompt($promptData);
        $promptId = $queueResponse['prompt_id'];

        // Return the prompt_id for frontend WebSocket tracking
        return ['prompt_id' => $promptId];
    }

    private function uploadImage(string $imagePath): array
    {
        $this->logInfo('Uploading image to ComfyUI.', ['image_path' => $imagePath]);
        $response = Http::attach(
            'image', file_get_contents($imagePath), basename($imagePath)
        )->timeout(60)->post(rtrim($this->apiProvider->api_url, '/').'/upload/image', [
            'type' => 'input',
            'overwrite' => 'false',
        ]);

        if ($response->failed()) {
            $this->logError('ComfyUI image upload failed.', ['response' => $response->body()]);
            throw new \Exception('Failed to upload image to ComfyUI');
        }

        return $response->json();
    }

    private function constructPrompt(\App\Models\Style $style, string $filename): array
    {
        $modelParams = $style->aiModel->parameters ?? [];
        $styleParams = $style->parameters ?? [];

        // Ensure both parameters are arrays, decode JSON if needed
        if (is_string($modelParams)) {
            $modelParams = json_decode($modelParams, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                $modelParams = [];
            }
        }

        if (is_string($styleParams)) {
            $styleParams = json_decode($styleParams, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                $styleParams = [];
            }
        }

        if (empty($modelParams) && empty($styleParams)) {
            throw new \Exception('ComfyUI workflow (parameters) is missing.');
        }

        // Use array_replace_recursive for a deep merge
        $mergedParams = array_replace_recursive($modelParams, $styleParams);
        $workflow = json_encode($mergedParams);

        // Properly escape the values for JSON injection
        $prompt = substr(json_encode($style->prompt, JSON_UNESCAPED_SLASHES), 1, -1);
        $filename_escaped = substr(json_encode($filename, JSON_UNESCAPED_SLASHES), 1, -1);
        $modelId_escaped = substr(json_encode($style->aiModel->model_id, JSON_UNESCAPED_SLASHES), 1, -1);

        $workflow = str_replace('__PROMPT__', $prompt, $workflow);
        $workflow = str_replace('__FILENAME__', $filename_escaped, $workflow);
        $workflow = str_replace('__MODEL_ID__', $modelId_escaped, $workflow);

        $decodedWorkflow = json_decode($workflow, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            $this->logError('Failed to decode workflow JSON after placeholder replacement.', [
                'json_error' => json_last_error_msg(),
                'workflow_string' => $workflow,
            ]);
            throw new \Exception('Failed to construct valid ComfyUI workflow JSON: '.json_last_error_msg());
        }

        return $decodedWorkflow;
    }

    private function queuePrompt(array $promptData): array
    {
        $this->logInfo('Queueing prompt in ComfyUI.');
        $response = Http::timeout(60)->post(rtrim($this->apiProvider->api_url, '/').'/prompt', ['prompt' => $promptData]);

        if ($response->failed()) {
            $this->logError('Failed to queue prompt in ComfyUI.', ['response' => $response->body()]);
            throw new \Exception('Failed to queue prompt in ComfyUI');
        }

        return $response->json();
    }

    public function waitForResult(string $promptId): string
    {
        set_time_limit(120); // Set maximum execution time for this function
        $this->logInfo('waitForResult: Waiting for ComfyUI result.', ['prompt_id' => $promptId]);
        $startTime = microtime(true);
        $timeout = 180; // seconds

        while (true) {
            if (microtime(true) - $startTime > $timeout) {
                $this->logError('waitForResult: ComfyUI result polling timed out.', ['prompt_id' => $promptId]);
                throw new \Exception('ComfyUI result polling timed out.');
            }

            try {
                $response = Http::timeout(60)->get(rtrim($this->apiProvider->api_url, '/').'/history/'.$promptId);
                $this->logDebug('waitForResult: History API response status.', ['status' => $response->status(), 'prompt_id' => $promptId]);

                if ($response->failed()) {
                    $this->logError('waitForResult: Failed to get history from ComfyUI.', ['prompt_id' => $promptId, 'response' => $response->body()]);
                    throw new \Exception('Failed to get history from ComfyUI');
                }

                $data = $response->json();
                $this->logDebug('waitForResult: History API response data.', ['data' => $data, 'prompt_id' => $promptId]);

                if (isset($data[$promptId]['outputs'])) {
                    $outputs = $data[$promptId]['outputs'];
                    $this->logInfo('waitForResult: Found outputs in history.', ['prompt_id' => $promptId, 'outputs_count' => count($outputs)]);

                    foreach ($outputs as $output) {
                        if (isset($output['images'][0]['type']) && $output['images'][0]['type'] === 'output') {
                            $imageUrl = sprintf('%s/view?filename=%s&subfolder=%s&type=output',
                                rtrim($this->apiProvider->api_url, '/'),
                                $output['images'][0]['filename'],
                                $output['images'][0]['subfolder']
                            );
                            $this->logInfo('waitForResult: Constructed image URL.', ['imageUrl' => $imageUrl, 'prompt_id' => $promptId]);

                            $imageResponse = Http::timeout(60)->get($imageUrl);
                            $this->logDebug('waitForResult: Image fetch response status.', ['status' => $imageResponse->status(), 'imageUrl' => $imageUrl]);

                            if ($imageResponse->failed()) {
                                $this->logError('waitForResult: Failed to retrieve image data from ComfyUI.', ['imageUrl' => $imageUrl, 'response' => $imageResponse->body()]);
                                throw new \Exception('Failed to retrieve image data from ComfyUI');
                            }
                            $this->logInfo('waitForResult: Successfully retrieved image data.', ['prompt_id' => $promptId]);

                            return base64_encode($imageResponse->body());
                        }
                    }
                } else {
                    $this->logDebug('waitForResult: No outputs found yet for prompt.', ['prompt_id' => $promptId]);
                }
            } catch (\Exception $e) {
                $this->logError('waitForResult: Exception caught during polling.', ['prompt_id' => $promptId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
                throw $e; // Re-throw the exception to be caught by ImageController
            }

            usleep(500000); // Wait for 0.5 seconds before polling again
        }
    }

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

        if (! $apiUrl) {
            throw new \RuntimeException('ComfyUI API-URL fehlt.');
        }

        try {
            $response = Http::timeout(5)->get($apiUrl.'/queue');

            if ($response->successful()) {
                return true;
            }

            $body = $response->body();
            $snippet = $body ? Str::limit($body, 200) : null;

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

            throw new \RuntimeException('ComfyUI HTTP '.$response->status().($snippet ? ': '.$snippet : ''));
        } catch (\Exception $e) {
            $this->logError('ComfyUI connection test failed.', ['error' => $e->getMessage()]);
            throw $e;
        }
    }

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

        if (! $this->apiProvider->enabled) {
            $this->logDebug('ComfyUI 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('ComfyUI API URL is not configured.');

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

        try {
            $response = Http::timeout(5)->get(rtrim($this->apiProvider->api_url, '/').'/queue');
            if ($response->successful()) {
                $this->logInfo('ComfyUI is available.');

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

                return [
                    'available' => false,
                    'reason' => 'Connection failed: '.$response->status(),
                    'provider_id' => $this->apiProvider->id,
                    'provider_name' => $this->apiProvider->name,
                ];
            }
        } catch (\Exception $e) {
            $this->logError('ComfyUI 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('ComfyUI does not support model search. Returning empty list.', ['searchTerm' => $searchTerm]);

        return [];
    }

    public function getStyledImage(string $promptId): string
    {
        return $this->waitForResult($promptId);
    }
}
