<?php

namespace FSPoster\App\AI\App;

use Exception;
use FSPoster\App\AI\Api\Gemini\Api as GeminiApi;
use FSPoster\App\AI\Api\OpenAi\Api;
use FSPoster\App\AI\Helpers\AIImageGeneratorResponse;
use FSPoster\App\AI\Helpers\AITextGeneratorResponse;
use FSPoster\App\Models\AILogs;
use FSPoster\App\Models\AITemplate;
use FSPoster\App\Providers\Core\RestRequest;
use FSPoster\App\Providers\Core\Settings;
use FSPoster\App\Providers\DB\Collection;
use FSPoster\App\Providers\Helpers\Helper;
use FSPoster\App\Providers\Helpers\WPPostThumbnail;
use FSPoster\App\Providers\Schedules\ScheduleObject;
use FSPoster\App\Providers\Schedules\ScheduleShareException;

class Listener
{
    public static function saveSocialNetworkSettings(RestRequest $request, string $socialNetwork)
    {
        $mediaTypeToUpload = $request->param('media_type_to_upload', 'featured_image', RestRequest::TYPE_STRING);

        if ($mediaTypeToUpload === 'ai_image') {
            $aiTempalteId = (int)$request->param('ai_template_id', 0, RestRequest::TYPE_INTEGER);

            if ($aiTempalteId <= 0) {
                throw new \Exception(fsp__('Please select AI template!'));
            }

            Settings::set(sprintf('%s_ai_template_id', $socialNetwork), $aiTempalteId);
        }
    }

    public static function getSocialNetworkSettings($data, $socialNetwork)
    {
        if (array_key_exists('media_type_to_upload', $data) && $data['media_type_to_upload'] === 'ai_image') {
            $data['ai_template_id'] = (int)Settings::get(sprintf('%s_ai_template_id', $socialNetwork));
        }

        return $data;
    }

    public static function getCustomPostData(array $customPostData, Collection $channel, string $socialNetwork): array
    {
        if (isset($customPostData["upload_media_type"]) && $customPostData["upload_media_type"] === 'ai_image') {
            $customPostData["ai_template_id"] = (int)Settings::get(sprintf('%s_ai_template_id', $socialNetwork));
        }

        return $customPostData;
    }

    public static function addAIToShortCodes($shortCodes)
    {
        $shortCodes['ai_text_generator'] = [ self::class, 'aiTextGenerator' ];

        return $shortCodes;
    }

    public static function aiTextGenerator(ScheduleObject $scheduleObj, $props = []): string
    {
        if ($scheduleObj->readOnlyMode) {
            return '[AI_text_will_be_generated_while_posting]';
        }

        if (empty($props['template_id'])) {
            return '';
        }

        $template = AITemplate::get($props[ 'template_id' ]);
        if (! $template) {
            return '';
        }

        /* Ola bilecek sonsuz loopun qarshisini almag uchundu bu replace */
        $propmt =  str_replace('{ai_text_generator', '{!ai_text_generator', $template->prompt);

        $renderedPrompt = $scheduleObj->replaceShortCodes($propmt, true);

        if ($template->type !== 'text') {
            self::insertAITextLog($template, $scheduleObj, $renderedPrompt, self::createFailedTextResponse(
                $template,
                $renderedPrompt,
                fsp__('Selected AI template is not a text template.')
            ));

            return '';
        }

        $cached = AILogs::where('schedule_id', $scheduleObj->getSchedule()->id)
                        ->where('template_id', $template->id)
                        ->where('status', 'success')
                        ->where('ai_model', self::getAILogModel($template))
                        ->where('prompt', $renderedPrompt)
                        ->orderBy('created_at DESC')
                        ->fetch();

        if ($cached && isset($cached->response)) {
            return $cached->response;
        }

        try {
            /** @var AITextGeneratorResponse $aiResponse */
            $aiResponse = apply_filters('fsp_ai_text_generator', new AITextGeneratorResponse(), $template->provider, $renderedPrompt, $template);
        } catch (Exception $e) {
            $aiResponse = self::createFailedTextResponse($template, $renderedPrompt, $e->getMessage());
        }

        self::insertAITextLog($template, $scheduleObj, $renderedPrompt, $aiResponse);

        if ($aiResponse->status !== 'success') {
            /* Ola bilecek sonsuz loopun qarshisini almag uchundu bu replace */
            $fallbackText = str_replace('{ai_text_generator', '{!ai_text_generator', $template->fallback_text);

            return $scheduleObj->replaceShortCodes($fallbackText, true);
        }

        return $aiResponse->aiGeneratedText;
    }

    public static function addAIToMediaList($mediaListToUpload, ScheduleObject $scheduleObj)
    {
        $scheduleData = $scheduleObj->getSchedule()->customization_data_obj;

        if (! $scheduleObj->readOnlyMode && $scheduleData->upload_media && $scheduleData->upload_media_type === 'ai_image' && ! empty($scheduleData->ai_template_id)) {
            $template = AITemplate::get($scheduleData->ai_template_id);
            if (! $template) {
                return $mediaListToUpload;
            }

            $renderedPrompt = $scheduleObj->replaceShortCodes($template->prompt, true);

            if ($template->type !== 'image') {
                self::insertAIImageLog($template, $scheduleObj, $renderedPrompt, self::createFailedImageResponse(
                    $template,
                    $renderedPrompt,
                    fsp__('Selected AI template is not an image template.')
                ));

                return $mediaListToUpload;
            }

            $cached = AILogs::where('schedule_id', $scheduleObj->getSchedule()->id)
                            ->where('template_id', $template->id)
                            ->where('status', 'success')
                            ->where('ai_model', self::getAILogModel($template))
                            ->where('prompt', $renderedPrompt)
                            ->orderBy('created_at DESC')
                            ->fetch();

            if ($cached && isset($cached->response)) {
                return [
                    [
                        'id'    =>  $cached->response ,
                        'type'  =>  'image',
                        'path'  =>  WPPostThumbnail::getOrCreateImagePath($cached->response, $scheduleObj->readOnlyMode),
                        'url'   =>  wp_get_attachment_url($cached->response),
                    ],
                ];
            }

            try {
                /** @var AIImageGeneratorResponse $aiResponse */
                $aiResponse = apply_filters('fsp_ai_image_generator', new AIImageGeneratorResponse(), $template->provider, $renderedPrompt, $template);
            } catch (Exception $e) {
                $aiResponse = self::createFailedImageResponse($template, $renderedPrompt, $e->getMessage());
            }

            self::insertAIImageLog($template, $scheduleObj, $renderedPrompt, $aiResponse);

            if ($aiResponse->status === 'success') {
                return [
                    [
                        'id'    =>  $aiResponse->attachmentId ,
                        'type'  =>  'image',
                        'path'  =>  WPPostThumbnail::getOrCreateImagePath($aiResponse->attachmentId, $scheduleObj->readOnlyMode),
                        'url'   =>  wp_get_attachment_url($aiResponse->attachmentId),
                    ],
                ];
            }
        }

        return $mediaListToUpload;
    }

    private static function createFailedTextResponse(Collection $template, string $renderedPrompt, string $message): AITextGeneratorResponse
    {
        $aiResponse = new AITextGeneratorResponse();

        $aiResponse->model       = $template->ai_model;
        $aiResponse->provider    = $template->provider;
        $aiResponse->endpoint    = '';
        $aiResponse->templateId  = $template->id;
        $aiResponse->prompt      = $renderedPrompt;
        $aiResponse->body        = '';
        $aiResponse->rawResponse = $message;
        $aiResponse->status      = 'fail';

        return $aiResponse;
    }

    private static function createFailedImageResponse(Collection $template, string $renderedPrompt, string $message): AIImageGeneratorResponse
    {
        $aiResponse = new AIImageGeneratorResponse();

        $aiResponse->model       = $template->ai_model;
        $aiResponse->provider    = $template->provider;
        $aiResponse->endpoint    = '';
        $aiResponse->templateId  = $template->id;
        $aiResponse->prompt      = $renderedPrompt;
        $aiResponse->body        = '';
        $aiResponse->rawResponse = $message;
        $aiResponse->status      = 'fail';

        return $aiResponse;
    }

    private static function insertAITextLog(Collection $template, ScheduleObject $scheduleObj, string $renderedPrompt, AITextGeneratorResponse $aiResponse): void
    {
        self::insertAILog($template, $scheduleObj, $renderedPrompt, $aiResponse);
    }

    private static function insertAIImageLog(Collection $template, ScheduleObject $scheduleObj, string $renderedPrompt, AIImageGeneratorResponse $aiResponse): void
    {
        self::insertAILog($template, $scheduleObj, $renderedPrompt, $aiResponse);
    }

    private static function insertAILog(Collection $template, ScheduleObject $scheduleObj, string $renderedPrompt, $aiResponse): void
    {
        AILogs::insert([
            'provider'      => $template->provider,
            'ai_model'      => self::getAILogModel($template),
            'template_id'   => $template->id,
            'endpoint'      => $aiResponse->endpoint,
            'status'        => $aiResponse->status,
            'prompt'        => $renderedPrompt,
            'response'      => $aiResponse->response,
            'schedule_id'   => $scheduleObj->getSchedule()->id,
            'raw_response'  => $aiResponse->rawResponse,
            'body'          => $aiResponse->body,
            'blog_id'       => Helper::getBlogId(),
        ]);
    }

    private static function getAILogModel(Collection $template): string
    {
        if ($template->provider === 'gemini') {
            return GeminiApi::normalizeModel($template->ai_model);
        }

        return $template->ai_model;
    }

    /**
     * @throws Exception
     */
    public static function save(array $template, string $provider): array
    {
        if ($provider != 'openai') {
            return $template;
        }

        if (!(($template['type'] === 'text' && preg_match('/\b(gpt)\b/i', $template['ai_model'])) || ($template['type'] === 'image' && $template['ai_model'] === 'dall-e-3'))) {
            throw new Exception(fsp__('Model type and AI model doesn\'t match'));
        }

        if (preg_match('/\b(gpt)\b/i', $template['ai_model'])) {
            if (!isset($template['config']['temperature'])) {
                throw new Exception(fsp__('Please enter a temperature value'));
            }

            $temperature = $template['config']['temperature'];

            if (!is_numeric($temperature) || $temperature < 0 || $temperature > 2) {
                throw new Exception(fsp__('Temperature must be a a numeric value between 0 and 2'));
            }

            $template['config']['temperature'] = (float) $temperature;
        }

        if ($template['ai_model'] === 'dall-e-3') {
            if (!isset($template['config']['size']) || !in_array($template['config']['size'], ['1024x1024', '1792x1024', '1024x1792'])) {
                throw new Exception(fsp__('Please select an image size'));
            }

            if (!isset($template['config']['style']) || !in_array($template['config']['style'], ['vivid', 'natural'])) {
                throw new Exception(fsp__('Please select an image style'));
            }
        }

        return $template;
    }

    /** @param AITemplate|Collection $template */
    public static function ShortCodeAITextGenerator(AITextGeneratorResponse $aiResponse, string $provider, string $renderedPrompt, Collection $template): AITextGeneratorResponse
    {
        if ($provider !== 'openai') {
            return $aiResponse;
        }

        $key = Settings::get('openai_key');

        if (empty($key)) {
            throw new ScheduleShareException(fsp__('OpenAI API key is empty! Please enter the Setting > AI Settings and then enter your OpenAI API key.'));
        }

        return (new Api($key))->generateText($renderedPrompt, $template);
    }

    /** @param AITemplate|Collection $template */
    public static function AIImageGenerator(AIImageGeneratorResponse $aiResponse, string $provider, string $renderedPrompt, Collection $template): AIImageGeneratorResponse
    {
        if ($provider !== 'openai') {
            return $aiResponse;
        }

        $key = Settings::get('openai_key');

        return (new Api($key))->generateImage($renderedPrompt, $template);
    }

    /**
     * @throws Exception
     */
    public static function saveGemini(array $template, string $provider): array
    {
        if ($provider != 'gemini') {
            return $template;
        }

        $template['ai_model'] = GeminiApi::normalizeModel($template['ai_model']);

        if ($template['type'] === 'text' && ! GeminiApi::isTextModel($template['ai_model'])) {
            throw new Exception(fsp__('Model type and AI model doesn\'t match'));
        }

        if ($template['type'] === 'image' && ! GeminiApi::isImageModel($template['ai_model'])) {
            throw new Exception(fsp__('Model type and AI model doesn\'t match'));
        }

        if (GeminiApi::isTextModel($template['ai_model'])) {
            if (! isset($template['config']['temperature'])) {
                throw new Exception(fsp__('Please enter a temperature value'));
            }

            $temperature = $template['config']['temperature'];

            if (! is_numeric($temperature) || $temperature < 0 || $temperature > 2) {
                throw new Exception(fsp__('Temperature must be a a numeric value between 0 and 2'));
            }

            $template['config']['temperature'] = (float) $temperature;
        }

        if (GeminiApi::isImageModel($template['ai_model'])) {
            $allowedRatios = [ '1:1', '16:9', '9:16', '4:3', '3:4' ];

            if (! isset($template['config']['aspectRatio']) || ! in_array($template['config']['aspectRatio'], $allowedRatios)) {
                throw new Exception(fsp__('Please select an aspect ratio'));
            }
        }

        return $template;
    }

    /** @param AITemplate|Collection $template */
    public static function GeminiShortCodeAITextGenerator(AITextGeneratorResponse $aiResponse, string $provider, string $renderedPrompt, Collection $template): AITextGeneratorResponse
    {
        if ($provider !== 'gemini') {
            return $aiResponse;
        }

        $key = Settings::get('gemini_key');

        if (empty($key)) {
            throw new ScheduleShareException(fsp__('Gemini API key is empty! Please enter the Setting > AI Settings and then enter your Gemini API key.'));
        }

        return (new GeminiApi($key))->generateText($renderedPrompt, $template);
    }

    /** @param AITemplate|Collection $template */
    public static function GeminiAIImageGenerator(AIImageGeneratorResponse $aiResponse, string $provider, string $renderedPrompt, Collection $template): AIImageGeneratorResponse
    {
        if ($provider !== 'gemini') {
            return $aiResponse;
        }

        $key = Settings::get('gemini_key');

        if (empty($key)) {
            throw new ScheduleShareException(fsp__('Gemini API key is empty! Please enter the Setting > AI Settings and then enter your Gemini API key.'));
        }

        return (new GeminiApi($key))->generateImage($renderedPrompt, $template);
    }
}
