<?php

namespace OtomaticAi\Controllers\SingleMode;

use DOMDocument;
use DOMXPath;
use OtomaticAi\Api\OpenAi\Client as OpenAiClient;
use OtomaticAi\Api\StabilityAi\Client as StabilityAiClient;
use OtomaticAi\Api\Ideogram\Client as IdeogramClient;
use OtomaticAi\Api\Flux\Client as FluxClient;
use OtomaticAi\Api\Unsplash\Client as UnsplashClient;
use OtomaticAi\Api\Pexels\Client as PexelsClient;
use OtomaticAi\Api\Pixabay\Client as PixabayClient;
use OtomaticAi\Api\GoogleImage\Client as GoogleImageClient;
use OtomaticAi\Api\BingImage\Client as BingImageClient;
use OtomaticAi\Api\Gemini\Client as GeminiClient;
use OtomaticAi\Api\XAi\Client as XAiClient;
use OtomaticAi\Content\Image\BingImage;
use OtomaticAi\Content\Image\DallE;
use OtomaticAi\Content\Image\Flux;
use OtomaticAi\Content\Image\Gemini;
use OtomaticAi\Content\Image\GoogleImage;
use OtomaticAi\Content\Image\Grok;
use OtomaticAi\Content\Image\Ideogram;
use OtomaticAi\Content\Image\Image;
use OtomaticAi\Content\Image\Pexels;
use OtomaticAi\Content\Image\Pixabay;
use OtomaticAi\Content\Image\StableDiffusion;
use OtomaticAi\Content\Image\Unsplash;
use OtomaticAi\Controllers\Controller;
use OtomaticAi\Models\SingleModeGeneration;
use OtomaticAi\Utils\Image as UtilsImage;
use OtomaticAi\Vendors\Illuminate\Support\Arr;

class MediaController extends Controller
{
    public function regenerateImage()
    {
        $this->validate([
            "id" => ["required", "integer"],
            "media_id" => ["required", "string"],
            "is_thumbnail" => ["boolean"],
        ]);

        $model = SingleModeGeneration::find($this->input('id'));

        if (empty($model)) {
            $this->response(["message" => "An error occurred", "error" => "Unable to find the generation #" . $this->input("id") . "."], 503);
        }

        $image = null;
        if ($this->input("is_thumbnail", false)) {

            // load the thumbnail
            if (($image = Arr::get($model->extras, "thumbnail")) !== null) {
                $image = Image::fromArray($image);
            }
        } else {

            // load html
            $doc = new DOMDocument('1.0', 'UTF-8');
            libxml_use_internal_errors(true);
            $doc->loadHTML('<?xml encoding="UTF-8">' . $model->content, LIBXML_NOWARNING);
            libxml_use_internal_errors(false);

            // create xpath
            $xpath = new DOMXPath($doc);

            $nodes = $xpath->query("//otoimage[@uid='" . $this->input('media_id') . "']");

            if ($nodes->length > 0) {
                $node = $nodes->item(0);
                $image = Image::fromHtmlElement($node);
            }
        }

        if (!empty($image)) {
            $newImage = null;
            switch ($image::PROVIDER) {
                case "stable-diffusion":
                    $newImage = $this->getStableDiffusionImage($image, $model);
                    break;
                case "ideogram":
                    $newImage = $this->getIdeogramImage($image, $model);
                    break;
                case "flux":
                    $newImage = $this->getFluxImage($image, $model);
                    break;
                case "dall-e":
                    $newImage = $this->getDallEImage($image, $model);
                    break;
                case "grok":
                    $newImage = $this->getGrokImage($image, $model);
                    break;
                case "gemini":
                    $newImage = $this->getGeminiImage($image, $model);
                    break;
                case "unsplash":
                    $newImage = $this->getUnsplashImage($image, $model);
                    break;
                case "pexels":
                    $newImage = $this->getPexelsImage($image, $model);
                    break;
                case "pixabay":
                    $newImage = $this->getPixabayImage($image, $model);
                    break;
                case "google-image":
                    $newImage = $this->getGoogleImage($image, $model);
                    break;
                case "bing-image":
                    $newImage = $this->getBingImage($image, $model);
                    break;
            }

            if (!empty($newImage)) {
                if ($this->input("is_thumbnail", false)) {
                    $extras = $model->extras;
                    $extras["thumbnail"] = $newImage->toArray();
                    $model->extras = $extras;
                } else {
                    $node->parentNode->replaceChild($newImage->toHtmlElement($doc), $node);
                    $model->content = trim(preg_replace("/^<body>|<\/body>$/", "", $doc->saveHTML($doc->getElementsByTagName("body")->item(0))));
                }

                $model->save();
            }
        }

        $this->response($model->fresh());
    }

    public function updateImagePrompt()
    {
        $this->validate([
            "id" => ["required", "integer"],
            "media_id" => ["required", "string"],
            "is_thumbnail" => ["boolean"],
            "prompt" => ["required", "string"],
        ]);

        $model = SingleModeGeneration::find($this->input('id'));

        if (empty($model)) {
            $this->response(["message" => "An error occurred", "error" => "Unable to find the generation #" . $this->input("id") . "."], 503);
        }

        $image = null;
        if ($this->input("is_thumbnail", false)) {

            // load the thumbnail
            if (($image = Arr::get($model->extras, "thumbnail")) !== null) {
                $image = Image::fromArray($image);
            }
        } else {

            // load html
            $doc = new DOMDocument('1.0', 'UTF-8');
            libxml_use_internal_errors(true);
            $doc->loadHTML('<?xml encoding="UTF-8">' . $model->content, LIBXML_NOWARNING);
            libxml_use_internal_errors(false);

            // create xpath
            $xpath = new DOMXPath($doc);

            $nodes = $xpath->query("//otoimage[@uid='" . $this->input('media_id') . "']");

            if ($nodes->length > 0) {
                $node = $nodes->item(0);
                $image = Image::fromHtmlElement($node);
            }
        }

        if (!empty($image)) {
            $image->prompt = $this->input('prompt');

            if ($this->input("is_thumbnail", false)) {
                $extras = $model->extras;
                $extras["thumbnail"] = $image->toArray();
                $model->extras = $extras;
            } else {
                $node->parentNode->replaceChild($image->toHtmlElement($doc), $node);
                $model->content = trim(preg_replace("/^<body>|<\/body>$/", "", $doc->saveHTML($doc->getElementsByTagName("body")->item(0))));
            }

            $model->save();
        }

        $this->response($model->fresh());
    }

    /**
     * Generate a stable diffusion image
     *
     * @param StableDiffusion $image
     * @param SingleModeGeneration $generation
     * @return StableDiffusion|StableDiffusion[]|null
     */
    private function getStableDiffusionImage(StableDiffusion $image, SingleModeGeneration $generation)
    {
        // get model id
        $model = Arr::get(UtilsImage::imageModelToProviderAndModel($image->model), "model", "core");

        // make the payload
        if ($model === "sdxl") {
            $payload = [
                "text_prompts" => [
                    [
                        "text" => $image->prompt,
                        "weight" => 1
                    ]
                ]
            ];
        } else {
            // others models
            $payload = [
                "prompt" => $image->prompt,
                "model" => $model,
            ];
        }

        // call the stability ai api
        $api = new StabilityAiClient();
        if ($image->model === "sdxl") {
            // sdxl model
            $result = [];
            $result = $api->textToImage($payload);
            if (Arr::get($result, "artifacts.0.finishReason") === "SUCCESS") {
                return StableDiffusion::make(Arr::get($result, "artifacts.0.base64"), $generation->title, $image->description, null,  $image->prompt, $image->model);
            }
        } else {
            // others models
            $result = $api->stableImageGenerate($payload);

            if (!empty(Arr::get($result, "image"))) {
                return StableDiffusion::make(Arr::get($result, "image"), $generation->title, $image->description, null,  $image->prompt, $image->model);
            }
        }
    }

    /**
     * Generate a ideogram image
     *
     * @param Ideogram $image
     * @param SingleModeGeneration $generation
     * @return Ideogram|Ideogram[]|null
     */
    private function getIdeogramImage(Ideogram $image, SingleModeGeneration $generation)
    {
        // get model id
        $model = Arr::get(UtilsImage::imageModelToProviderAndModel($image->model), "model", "V_2A_TURBO");

        // make the payload
        $payload = [
            "image_request" => [
                "prompt" => $image->prompt,
                "model" => $model,
            ]
        ];

        // call the ideogram api
        $api = new IdeogramClient();
        $result = $api->generate($payload);

        // make the Ideogram Image
        if (!empty(Arr::get($result, "data.0.url", null))) {
            return Ideogram::make(Arr::get($result, "data.0.url"), $generation->title, $image->description, null, $image->prompt, $image->model);
        }
    }

    /**
     * Generate a Flux image
     *
     * @param Flux $image
     * @param SingleModeGeneration $generation
     * @return Flux|Flux[]|null
     */
    private function getFluxImage(Flux $image, SingleModeGeneration $generation)
    {
        // get model id
        $model = Arr::get(UtilsImage::imageModelToProviderAndModel($image->model), "model", "flux-dev");

        // make the payload
        $payload = [
            "prompt" => $image->prompt,
            "model" => $model,
        ];

        // call the flux api
        $api = new FluxClient();

        // create the task
        $response = $api->createTask($payload);

        // get the task id
        $pollingUrl = Arr::get($response, "polling_url");

        // get the tasks results
        $result = null;
        while (true) {
            sleep(3);

            $result = $api->getResult($pollingUrl);
            if (Arr::get($result, "status") !== "Pending") {
                break;
            }
        }

        // make the Flux Image
        if (!empty($result) && !empty(Arr::get($result, "result.sample"))) {
            return Flux::make(Arr::get($result, "result.sample"), $generation->title, $image->description, null, $image->prompt, $image->model);
        }
    }

    /**
     * Generate a dall-e image
     *
     * @param DallE $image
     * @param SingleModeGeneration $generation
     * @return DallE|DallE[]|null
     */
    private function getDallEImage(DallE $image, SingleModeGeneration $generation)
    {
        // get model id
        $modelData = UtilsImage::imageModelToProviderAndModel($image->model);
        $model = Arr::get($modelData, "model", "flux-dev");

        // make the settings
        $settings = [
            "prompt" => [],
            "settings" => [],
        ];

        $settings["prompt"] = $image->prompt;
        $settings["settings"] = [
            "model" => $model,
            "quality" => Arr::get($modelData, "params.quality", "auto"),
            "size" => "auto",
            "moderation" => "low",
        ];

        // call the openai api
        $api = new OpenAiClient();
        $result = $api->image($settings["prompt"], $settings["settings"]);

        // make the DallE Image
        if (!empty($result) && !empty(Arr::get($result, "data.0.b64_json"))) {
            return DallE::make(Arr::get($result, "data.0.b64_json"), $generation->title, $image->description, null, $image->prompt, $image->model);
        }
    }

    /**
     * Generate a grok image
     *
     * @param Grok $image
     * @param SingleModeGeneration $generation
     * @return Grok|Grok[]|null
     */
    private function getGrokImage(Grok $image, SingleModeGeneration $generation)
    {
        // get model id
        $model = Arr::get(UtilsImage::imageModelToProviderAndModel($image->model), "model", "grok-2-image");

        // make the payload
        $payload = [
            "model" => $model,
            "prompt" => $image->prompt,
        ];

        // call the xai api
        $api = new XAiClient();
        $result = $api->image($payload);

        // make the Grok Image
        if (!empty($result) && !empty(Arr::get($result, "data.0.b64_json"))) {
            return Grok::make(Arr::get($result, "data.0.b64_json"), $generation->title, $image->description, null, $image->prompt, $image->model);
        }
    }

    /**
     * Generate a gemini image
     *
     * @param Gemini $image
     * @param SingleModeGeneration $generation
     * @return Gemini|Gemini[]|null
     */
    private function getGeminiImage(Gemini $image, SingleModeGeneration $generation)
    {
        // get model id
        $model = Arr::get(UtilsImage::imageModelToProviderAndModel($image->model), "model", "gemini-2.5-flash-image");

        // make the payload
        $payload = [
            "model" => $model,
            "contents" => [
                ["parts" => [["text" => $image->prompt]]]
            ],
            "generationConfig" => [
                "imageConfig" => [
                    "aspectRatio" => "16:9"
                ]
            ]
        ];

        // call the xai api
        $api = new GeminiClient();
        $result = $api->image($payload);

        // make the Gemini Image
        $base64 = GeminiClient::getImageInlineData($result);
        if (!empty($base64)) {
            return Gemini::make($base64, $generation->title, $image->description, null, $image->prompt, $image->model);
        }
    }

    /**
     * Generate an Unsplash image
     *
     * @param Unsplash $image
     * @param SingleModeGeneration $generation
     * @return Unsplash|Unsplash[]|null
     */
    private function getUnsplashImage(Unsplash $image, SingleModeGeneration $generation)
    {

        // make the search for unsplash
        $search = $image->search;

        // call unsplash api
        $api = new UnsplashClient;
        $response = $api->searchPhotos([
            "query" => $search,
            "per_page" => 20,
        ]);

        // get results
        $result = Arr::get($response, "results", []);

        if (!empty($result)) {

            // get a random image
            $result = Arr::random($result);

            // make the Unsplash Image
            return Unsplash::make($result["urls"]["regular"], $generation->title, $image->description, null, $search);
        }
    }

    /**
     * Generate an Pexels image
     *
     * @param Pexels $image
     * @param SingleModeGeneration $generation
     * @return Pexels|Pexels[]|null
     */
    private function getPexelsImage(Pexels $image, SingleModeGeneration $generation)
    {
        // make the search for pexels
        $search = $image->search;

        // call pexels api
        $api = new PexelsClient();
        $response = $api->searchPhotos([
            "query" => $search,
            "per_page" => 20,
        ]);

        // get results
        $result = Arr::get($response, "photos", []);

        if (!empty($result)) {

            // get a random image
            $result = Arr::random($result);

            // make the Pexels Image
            return Pexels::make($result["src"]["landscape"], $generation->title, $image->description, null, $search);
        }
    }

    /**
     * Generate an Pixabay image
     *
     * @param Pixabay $image
     * @param SingleModeGeneration $generation
     * @return Pixabay|Pixabay[]|null
     */
    private function getPixabayImage(Pixabay $image, SingleModeGeneration $generation)
    {
        // make the search for pixabay
        $search = $image->search;

        // call pixabay api
        $api = new PixabayClient();
        $response = $api->images([
            "q" => $search,
        ]);

        // get a random image
        $result = Arr::get($response, "hits", []);

        if (!empty($result)) {

            // get a random image
            $result = Arr::random($result);

            // make the Pixabay Image
            return Pixabay::make($result["largeImageURL"], $generation->title, $image->description, null, $search);
        }
    }

    /**
     * Generate an Google image
     *
     * @param GoogleImage $image
     * @param SingleModeGeneration $generation
     * @return GoogleImage|GoogleImage[]|null
     */
    private function getGoogleImage(GoogleImage $image, SingleModeGeneration $generation)
    {
        // make the search for google image
        $search = $image->search;

        // call google image api
        $api = new GoogleImageClient();
        $result = $api->images([
            "q" => $search,
        ]);

        if (!empty($result)) {

            // get a random image
            $result = Arr::random($result);

            // make the Google Image
            return GoogleImage::make($result, $generation->title, $image->description, null, $search);
        }
    }

    /**
     * Generate an Bing image
     *
     * @param BingImage $image
     * @param SingleModeGeneration $generation
     * @return BingImage|BingImage[]|null
     */
    private function getBingImage(BingImage $image, SingleModeGeneration $generation)
    {
        // make the search for bing image
        $search = $image->search;

        // call google image api
        $api = new BingImageClient();
        $result = $api->images([
            "q" => $search,
        ]);

        if (!empty($result)) {

            // get a random image
            $result = Arr::random($result);

            // make the Bing Image
            return BingImage::make($result, $generation->title, $image->description, null, $search);
        }
    }
}
