<?php

namespace App\Modules\Ai\Services;

use App\Core\Contracts\Ai\AiImageGenerationPort;
use App\Modules\Ai\Exceptions\OpenAiImageGenerationException;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Throwable;

/**
 * توليد صور عبر OpenAI Images API — POST /v1/images/generations
 * النماذج المدعومة: gpt-image-1 | gpt-image-1-mini فقط.
 */
final class OpenAiImageGenerationService implements AiImageGenerationPort
{
    private const IMAGES_ENDPOINT = '/images/generations';

    /** @var list<string> */
    private const ALLOWED_MODELS = ['gpt-image-1', 'gpt-image-1-mini'];

    private const MODEL_UNAVAILABLE_MSG = 'نموذج توليد الصور غير متاح حالياً لهذا الحساب أو يحتاج تفعيل الصلاحية من OpenAI.';

    private ?string $activeQuality = null;

    public function generate(
        string $prompt,
        ?string $aspectRatio = null,
        ?string $quality = null,
        ?string $model = null,
    ): array {
        $apiKey = config('openai.api_key');
        if (! is_string($apiKey) || $apiKey === '') {
            throw new \RuntimeException('OPENAI_API_KEY غير مضبوط في .env');
        }

        $baseUrl = (string) config('openai.base_url', 'https://api.openai.com/v1');
        $url = $baseUrl.self::IMAGES_ENDPOINT;
        $fullPrompt = trim($prompt);
        $models = $this->resolveModelChain($model);
        $this->activeQuality = $quality;

        $lastException = null;
        foreach ($models as $model) {
            try {
                $payload = $this->buildPayload($model, $fullPrompt, $aspectRatio);

                Log::info('openai.image_request', [
                    'endpoint' => $url,
                    'model' => $model,
                    'size' => $payload['size'] ?? null,
                ]);

                $response = $this->postImagesApi($apiKey, $url, $payload);

                if ($response->successful()) {
                    Log::info('openai.image_success', ['model' => $model, 'http_status' => $response->status()]);

                    $parsed = $this->parseSuccess($response, $model);
                    $parsed['model'] = $model;

                    return $parsed;
                }

                $lastException = $this->exceptionFromResponse($response, $model, $url);

                if ($lastException->shouldTryFallbackModel()) {
                    Log::warning('openai.image_model_fallback', $lastException->logContext());

                    continue;
                }

                throw $lastException;
            } catch (OpenAiImageGenerationException $e) {
                $lastException = $e;
                if ($e->shouldTryFallbackModel()) {
                    Log::warning('openai.image_model_fallback', $e->logContext());

                    continue;
                }
                throw $e;
            }
        }

        throw $lastException ?? new \RuntimeException('فشل توليد الصورة.');
    }

    /**
     * @return list<string>
     */
    private function resolveModelChain(?string $preferred = null): array
    {
        $configured = trim((string) ($preferred ?: config('openai.image_model', 'gpt-image-1')));

        if (! in_array($configured, self::ALLOWED_MODELS, true)) {
            Log::warning('openai.image_invalid_model_config', [
                'configured' => $configured,
                'allowed' => self::ALLOWED_MODELS,
                'fallback' => 'gpt-image-1-mini',
            ]);
            $configured = 'gpt-image-1-mini';
        }

        $alternate = $configured === 'gpt-image-1' ? 'gpt-image-1-mini' : 'gpt-image-1';

        return array_values(array_unique([$configured, $alternate]));
    }

    /**
     * @return array{image_url: string, provider: string, external_id: string|null}
     */
    private function parseSuccess(Response $response, string $model): array
    {
        $json = $response->json();

        $imageUrl = data_get($json, 'data.0.url');
        if (is_string($imageUrl) && $imageUrl !== '') {
            return [
                'image_url' => $imageUrl,
                'provider' => 'openai:'.$model,
                'external_id' => null,
            ];
        }

        $b64 = data_get($json, 'data.0.b64_json');
        if (! is_string($b64) || $b64 === '') {
            Log::error('openai.image_empty_response', [
                'model' => $model,
                'response_keys' => is_array($json) ? array_keys($json) : [],
            ]);
            throw new \RuntimeException('لم يُرجع المزود صورة (لا url ولا b64_json).');
        }

        $revised = data_get($json, 'data.0.revised_prompt');

        return [
            'image_url' => $this->storeBase64Image($b64),
            'provider' => 'openai:'.$model,
            'external_id' => is_string($revised) && $revised !== '' ? mb_substr($revised, 0, 191) : null,
        ];
    }

    private function storeBase64Image(string $b64): string
    {
        $binary = base64_decode($b64, true);
        if ($binary === false) {
            throw new \RuntimeException('تعذر فك ترميز الصورة.');
        }

        $path = 'ai-images/'.now()->format('Y/m/d').'/'.Str::uuid()->toString().'.png';
        Storage::disk('public')->put($path, $binary);

        return '/storage/'.$path;
    }

    /**
     * @return array<string, mixed>
     */
    private function buildPayload(string $model, string $prompt, ?string $aspectRatio): array
    {
        $quality = strtolower((string) ($this->activeQuality ?: config('openai.image_quality', 'high')));
        if (! in_array($quality, ['low', 'medium', 'high', 'auto'], true)) {
            $quality = 'high';
        }

        return [
            'model' => $model,
            'prompt' => $prompt,
            'n' => 1,
            'size' => $this->resolveSize($aspectRatio),
            'quality' => $quality,
            'output_format' => 'png',
        ];
    }

    private function resolveSize(?string $aspectRatio): string
    {
        return match ($aspectRatio) {
            '16:9' => '1536x1024',
            '9:16', '3:4' => '1024x1536',
            default => '1024x1024',
        };
    }

    private function exceptionFromResponse(Response $response, string $model, string $endpoint): OpenAiImageGenerationException
    {
        $body = $response->json();
        $apiMessage = is_array($body)
            ? (string) data_get($body, 'error.message', $response->body())
            : $response->body();
        $errorCode = is_array($body) ? (string) data_get($body, 'error.code', 'unknown') : 'unknown';
        $errorType = is_array($body) ? (string) data_get($body, 'error.type', '') : '';

        Log::error('openai.image_api_error', [
            'endpoint' => $endpoint,
            'api' => 'images_generations',
            'model' => $model,
            'http_status' => $response->status(),
            'error_type' => $errorType,
            'error_code' => $errorCode,
            'error_message' => $apiMessage,
            'error_param' => is_array($body) ? data_get($body, 'error.param') : null,
            'response_body' => $response->body(),
        ]);

        $userMessage = $this->resolveUserMessage($errorCode, $errorType, $apiMessage);

        return new OpenAiImageGenerationException(
            $userMessage,
            $apiMessage !== '' ? $apiMessage : 'فشل توليد الصورة.',
            $errorCode !== '' ? $errorCode : 'image_generation_failed',
            $response->status() >= 400 && $response->status() < 600 ? $response->status() : 502,
            [
                'endpoint' => $endpoint,
                'model' => $model,
                'error_type' => $errorType,
            ]
        );
    }

    private function resolveUserMessage(string $errorCode, string $errorType, string $apiMessage): string
    {
        $code = strtolower($errorCode);
        $type = strtolower($errorType);
        $msg = strtolower($apiMessage);

        if (in_array($code, ['model_not_found', 'invalid_value', 'invalid_model'], true)
            || $type === 'model_not_found'
            || str_contains($msg, 'does not exist')) {
            return self::MODEL_UNAVAILABLE_MSG;
        }

        if ($code === 'permission_denied' || $type === 'permission_denied' || str_contains($msg, 'permission')) {
            return self::MODEL_UNAVAILABLE_MSG;
        }

        if (str_contains($msg, 'billing') || str_contains($msg, 'quota') || str_contains($msg, 'insufficient')) {
            return 'رصيد OpenAI غير كافٍ لتوليد الصور — أضف طريقة دفع في platform.openai.com.';
        }

        return $apiMessage !== '' ? $apiMessage : 'فشل توليد الصورة.';
    }

    /**
     * POST {OPENAI_BASE_URL}/images/generations — Images API فقط.
     *
     * @param  array<string, mixed>  $payload
     */
    private function postImagesApi(string $apiKey, string $url, array $payload): Response
    {
        try {
            return Http::withOptions($this->httpClientOptions())
                ->withToken($apiKey)
                ->acceptJson()
                ->asJson()
                ->timeout((int) config('openai.image_timeout', config('openai.timeout', 120)))
                ->post($url, $payload);
        } catch (Throwable $e) {
            Log::error('openai.image_http_failed', [
                'endpoint' => $url,
                'model' => $payload['model'] ?? null,
                'exception' => $e->getMessage(),
                'exception_class' => $e::class,
            ]);

            $hint = '';
            $m = $e->getMessage();
            if (str_contains($m, 'SSL certificate') || str_contains(strtolower($m), 'curl error 60')) {
                $hint = ' عيّن OPENAI_HTTP_VERIFY_SSL=false في .env (للتطوير فقط).';
            }

            throw new \RuntimeException('تعذر الاتصال بمزود توليد الصور (Images API).'.$hint);
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function httpClientOptions(): array
    {
        if (config('openai.http_verify_ssl', true) === false) {
            return ['verify' => false];
        }

        return [];
    }
}
