<?php

namespace App\Helpers\FirstParty\Meme;

use App\Helpers\FirstParty\AI\CloudflareAI;
use App\Helpers\FirstParty\AI\OpenAI;
use App\Helpers\FirstParty\AI\RunwareAI;
use App\Helpers\FirstParty\AspectRatio;
use App\Helpers\FirstParty\MediaEngine\MediaEngine;
use App\Models\BackgroundMedia;
use App\Models\Category;
use App\Models\Meme;
use App\Models\MemeMedia;
use App\Models\MemeMediaEmbedding;
use Pgvector\Laravel\Distance;
use Str;

class MemeGenerator
{
    const TYPE_SINGLE_CAPTION_MEME_BACKGROUND = 'single_caption_meme_background';

    const STATUS_PENDING = 'pending';

    const STATUS_COMPLETED = 'completed';

    public static function getSuitableMemeMedia(Meme $meme, $tolerance = 5)
    {
        $meme_media = null;

        $primary_keyword_type = $meme->primary_keyword_type;

        if ($primary_keyword_type == 'action') {

            $meme_media = self::getMemeMediaByKeywords($meme->action_keywords, $tolerance, 'action_keywords');

            if (is_null($meme_media)) {

                $keywords = array_merge($meme->emotion_keywords, $meme->misc_keywords, $meme->keywords);

                $meme_media = self::getMemeMediaByKeywords($keywords, $tolerance);
            }
        } elseif ($primary_keyword_type == 'emotion') {
            $meme_media = self::getMemeMediaByKeywords($meme->emotion_keywords, $tolerance, 'emotion_keywords');

            if (is_null($meme_media)) {
                $keywords = array_merge($meme->action_keywords, $meme->misc_keywords, $meme->keywords);

                $meme_media = self::getMemeMediaByKeywords($keywords, $tolerance);
            }
        } elseif ($primary_keyword_type == 'misc') {
            $meme_media = self::getMemeMediaByKeywords($meme->misc_keywords, $tolerance, 'misc_keywords');

            if (is_null($meme_media)) {
                $keywords = array_merge($meme->action_keywords, $meme->emotion_keywords, $meme->keywords);

                $meme_media = self::getMemeMediaByKeywords($keywords, $tolerance);
            }
        }

        if (is_null($meme_media)) {
            $meme_media = MemeMedia::query()->inRandomOrder()->first();
        }

        return $meme_media;
    }

    public static function generateMemeByCategory(Category $category)
    {
        $meme_output = self::generateMemeOutputByCategory($category);

        $meme = null;

        if ($meme_output->success) {
            $meme = Meme::create([
                'type' => self::TYPE_SINGLE_CAPTION_MEME_BACKGROUND,
                'prompt' => $meme_output->prompt,
                'category_id' => $category->id,
                'caption' => $meme_output->caption,
                'meme_keywords' => $meme_output->keywords,
                'background' => $meme_output->background,
                'keywords' => $meme_output->keywords,
                'is_system' => true,
                'status' => self::STATUS_PENDING,
                'primary_keyword_type' => $meme_output->primary_keyword_type,
                'action_keywords' => $meme_output->action_keywords,
                'emotion_keywords' => $meme_output->emotion_keywords,
                'misc_keywords' => $meme_output->misc_keywords,
            ]);

            $meme->attachTags($meme_output->keywords, 'meme');
        }

        if (! is_null($meme) && $meme->status == self::STATUS_PENDING) {
            // populate meme_media_id
            $meme->meme_media_id = self::getSuitableMemeMedia($meme)->id;
            $meme->background_media_id = self::generateBackgroundMediaWithRunware($meme_output->background)->id;

            if (
                // !is_null($meme->meme_media_id) &&
                ! is_null($meme->background_media_id)
            ) {
                $meme->status = self::STATUS_COMPLETED;
            }

            $meme->save();
        }

        return $meme;
    }

    public static function generateMemeByKeyword($keyword)
    {
        $meme_output = self::generateMemeOutputByKeyword($keyword);

        $meme = null;

        if ($meme_output->success) {
            $meme = Meme::create([
                'type' => self::TYPE_SINGLE_CAPTION_MEME_BACKGROUND,
                'prompt' => $meme_output->prompt,
                'category_id' => null,
                'caption' => $meme_output->caption,
                'meme_keywords' => $meme_output->keywords,
                'background' => $meme_output->background,
                'keywords' => $meme_output->keywords,
                'is_system' => true,
                'status' => self::STATUS_PENDING,
                'primary_keyword_type' => $meme_output->primary_keyword_type,
                'action_keywords' => $meme_output->action_keywords,
                'emotion_keywords' => $meme_output->emotion_keywords,
                'misc_keywords' => $meme_output->misc_keywords,
            ]);

            $meme->attachTags($meme_output->keywords, 'meme');
        }

        if (! is_null($meme) && $meme->status == self::STATUS_PENDING) {
            // populate meme_media_id
            $meme->meme_media_id = self::getSuitableMemeMedia($meme)->id;
            $meme->background_media_id = self::generateBackgroundMediaWithRunware($meme_output->background)->id;

            if (
                // !is_null($meme->meme_media_id) &&
                ! is_null($meme->background_media_id)
            ) {
                $meme->status = self::STATUS_COMPLETED;
            }

            $meme->save();
        }

        return $meme;
    }

    public static function generateMemeOutputByKeyword($keyword, $category = null)
    {

        $retries = 3;
        $attempt = 0;

        $prompt = "Write me 1 meme about {$keyword}";

        // RETRY MECHANISM START
        do {
            $attempt++;

            $meme_response = OpenAI::getSingleMemeGenerator($prompt);
            $meme_output = json_decode(OpenAI::getOpenAIOutput($meme_response));

            $output_is_valid = false;

            if (! is_null($meme_output)) {
                if (
                    isset($meme_output->caption) &&
                    isset($meme_output->background) &&
                    isset($meme_output->keywords) &&
                    isset($meme_output->primary_keyword_type) &&
                    isset($meme_output->action_keywords) &&
                    isset($meme_output->emotion_keywords)
                ) {
                    $output_is_valid = true;
                }
            }

            // If output is valid or we've exhausted all retries, break the loop
            if ($output_is_valid || $attempt >= $retries) {
                break;
            }

            // Optional: Add a small delay between retries to avoid rate limiting
            // sleep(1);

        } while ($attempt < $retries);
        // RETRY MECHANISM END

        if ($output_is_valid) {
            $meme_output->success = true;
            $meme_output->prompt = $prompt;
            $meme_output->category = $category;
            $meme_output->attempts = $attempt; // Optional: track how many attempts it took
        } else {
            $meme_output = (object) [
                'success' => false,
                'attempts' => $attempt, // Optional: track how many attempts were made
                'error' => 'Failed to generate valid meme after '.$retries.' attempts',
            ];
        }

        return $meme_output;
    }

    public static function generateMemeOutputByCategory(Category $category)
    {

        $random_keyword = Str::lower($category->name);

        if (! is_null($category->parent_id)) {
            $random_keyword = $category->parent->name.' - '.$random_keyword;
        }

        if (! is_null($category->meme_angles)) {
            $random_keyword .= ' - '.collect($category->meme_angles)->random();
        } elseif (! is_null($category->keywords)) {
            $random_keyword .= ' - '.collect($category->keywords)->random();
        }

        return self::generateMemeOutputByKeyword($random_keyword, $category);
    }

    public static function generateBackgroundMediaWithRunware($prompt)
    {
        $media_width = 1024;
        $media_height = 1024;
        $aspect_ratio = AspectRatio::get($media_width, $media_height);

        $runware_output_url = RunwareAI::generateSchnellImage(Str::uuid(), $prompt, $media_width, $media_height);

        $media = MediaEngine::addMedia(
            'system-i',
            'image',
            'system_uploaded',
            'replicate',
            null,
            $runware_output_url,
            'download'
        );

        $background_media = BackgroundMedia::create([
            'media_uuid' => $media->uuid,
            'media_url' => MediaEngine::getMediaCloudUrl($media),
            'prompt' => $prompt,
            'status' => 'completed',
            'aspect_ratio' => $aspect_ratio,
            'media_width' => $media_width,
            'media_height' => $media_height,

        ]);

        return $background_media;
    }

    public static function getMemeMediaByKeywords(array $keywords, int $tolerance = 10, ?string $tag = null)
    {

        $meme_embedding = CloudflareAI::getVectorEmbeddingBgeSmall(implode(' ', $keywords));

        $meme_medias = MemeMediaEmbedding::query()
            ->when(! is_empty($tag), function ($query) use ($tag) {
                return $query->where('tag', $tag);
            })
            ->nearestNeighbors('embedding', $meme_embedding, Distance::L2)
            ->take($tolerance)
            ->get();

        if ($meme_medias->count() > 0) {
            $meme_media = $meme_medias->random()->meme_media;
        }

        return $meme_media;
    }
}
