<?php

namespace OtomaticAi\Controllers\OneClick;

use Exception;
use OtomaticAi\Models\Persona;
use OtomaticAi\Models\Presets\Preset;
use OtomaticAi\Vendors\Illuminate\Support\Arr;
use OtomaticAi\Vendors\Illuminate\Support\Str;
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\Controllers\Controller;
use OtomaticAi\Models\WP\User;
use OtomaticAi\Utils\Image;
use OtomaticAi\Utils\Language;
use Simple_Local_Avatars;

class ProfilesController extends Controller
{
    /**
     * Create profile
     *
     * @return void
     */
    public function createProfile()
    {
        $this->verifyNonce();

        $this->validate([
            "language" => ["required", "string"],
            "model" => ["required", "string"],
            "profile" => ["required", "array"],
        ]);

        $language = Language::find($this->input("language"));
        $model = $this->input("model");

        $profile = $this->input("profile");

        $prf = null;

        if (Arr::get($profile, "existing", false)) {
            $id = Arr::get($profile, "id");
            $id = str_replace("prf_", "", $id);
            $prf = Persona::find($id);
        } else {
            $prf = $this->createPersona($profile, $model, $language);
        }

        if (!empty($prf)) {
            $prf->old_id = Arr::get($profile, "id");

            $this->response([
                "id" => $prf->id,
                "old_id" => $prf->old_id,
                "first_name" => $prf->user_first_name,
                "last_name" => $prf->user_last_name,
                "age" => $prf->age,
                "job" => $prf->job,
                "biography" => $prf->user_description,
            ]);
        }

        $this->response(["message" => "The profile has not been created."], 422);
    }

    /**
     * Create a persona
     *
     * @param array $profile
     * @param string $model
     * @param Language $language
     * @return Persona|null
     */
    private function createPersona(array $profile, string $model, Language $language): ?Persona
    {
        $firstName = Arr::get($profile, "first_name");
        $lastName = Arr::get($profile, "last_name");
        $name = $firstName . ' ' . $lastName;
        $age = Arr::get($profile, "age", 20);
        $job = Arr::get($profile, "job");
        $description = Arr::get($profile, "biography");
        $password = Str::random(32);
        $writingStyle = $this->getRandomWritingStyle();

        // create or get the user
        $userId = wp_create_user($this->makeUsername($name), $password);
        if (!is_wp_error($userId)) {

            // set role
            $user = get_user_by("ID", $userId);
            $user->set_role("administrator");

            // update user_meta
            update_user_meta($userId, "first_name", $firstName);
            update_user_meta($userId, "last_name", $lastName);
            update_user_meta($userId, "description", $description);

            // get model
            $user = User::find($userId);

            // create the persona
            $persona = new Persona();
            $persona->user()->associate($user);
            $persona->language = $language->key;
            $persona->age = $age;
            $persona->job = $job;
            $persona->writing_style = $writingStyle;
            $persona->save();

            if (class_exists("Simple_Local_Avatars")) {
                $avatar = null;
                try {
                    $avatar = $this->makePersonaAvatar([
                        "first_name" => $firstName,
                        "last_name" => $lastName,
                        "age" => $age,
                        "job" => $job
                    ], $model, $language);
                } catch (Exception $e) {
                }

                if (!empty($avatar)) {
                    $attachmentId = $avatar->save('user avatar');

                    if (!empty($attachmentId)) {
                        $sla = new Simple_Local_Avatars();
                        $sla->assign_new_user_avatar($attachmentId, $userId);
                    }
                }
            }

            return $persona;
        }

        return null;
    }

    /**
     * Make a persona avatar
     *
     * @param string $name
     * @param int $age
     * @param string $job
     * @param Language $language
     * @return Image|null
     */
    private function makePersonaAvatar(array $profile, string $model, Language $language): ?Image
    {
        // get the preset for generate the avatar prompt
        $preset = Preset::findFromAPI("generate_persona_avatar_prompt");

        // process the preset
        $response = $preset->process([
            "language" => $language->value,
            "name" => Arr::get($profile, "first_name") . ' ' . Arr::get($profile, "last_name"),
            "age" => Arr::get($profile, "age", 20),
            "job" => Arr::get($profile, "job"),
            "has_custom_instructions" => false,
        ]);

        // get the response content
        $response = json_decode(Arr::get($response, 'choices.0.message.content'), true, 512, JSON_THROW_ON_ERROR);
        $prompt = Arr::get($response, "value");
        $prompt = Str::clean($prompt);

        try {
            $provider = Image::imageModelToProviderAndModel($model);

            switch ($provider["provider"]) {
                case "dall_e":
                    return $this->generateDallEImage($prompt, $model);
                    break;
                case "stable_diffusion":
                    return $this->generateStableDiffusionImage($prompt, $provider["model"]);
                    break;
                case "ideogram":
                    return $this->generateIdeogramImage($prompt, $provider["model"]);
                    break;
                case "flux":
                    return $this->generateFluxImage($prompt, $provider["model"]);
                    break;
            }
        } catch (Exception $e) {
        }

        return null;
    }

    /**
     * Generate a stable diffusion image
     *
     * @param string $prompt
     * @param string $model
     * @return Image|null
     */
    private function generateStableDiffusionImage(string $prompt, string $model): ?Image
    {
        $api = new StabilityAiClient();

        switch ($model) {
            case "sdxl":
                // sdxl model
                $payload = [
                    "text_prompts" => [
                        [
                            "text" => $prompt,
                            "weight" => 1
                        ]
                    ]
                ];

                $result = $api->textToImage($payload);

                if (!empty(Arr::get($result, "artifacts.0.base64"))) {
                    return Image::fromBase64(Arr::get($result, "artifacts.0.base64"));
                }
                break;
            default:
                // others models
                $payload = [
                    "prompt" => $prompt,
                ];

                switch ($model) {
                    case "stable_image_core":
                        $payload["model"] = "core";
                        break;
                    case "stable_image_sd3_medium":
                        $payload["model"] = "sd3-medium";
                        break;
                    case "stable_image_sd3_large_turbo":
                        $payload["model"] = "sd3-large-turbo";
                        break;
                    case "stable_image_sd3_large":
                        $payload["model"] = "sd3-large";
                        break;
                    case "stable_image_sd3_5_large":
                        $payload["model"] = "sd3.5-large";
                        break;
                    case "stable_image_sd3_5_large_turbo":
                        $payload["model"] = "sd3.5-large-turbo";
                        break;
                    case "stable_image_ultra":
                        $payload["model"] = "ultra";
                        break;
                    default:
                        $payload["model"] = "core";
                }

                $result = $api->stableImageGenerate($payload);

                if (!empty(Arr::get($result, "image"))) {
                    return Image::fromBase64(Arr::get($result, "image"));
                }
        }

        return null;
    }

    /**
     * Generate a ideogram image
     *
     * @param string $prompt
     * @param string $model
     * @return Image|null
     */
    private function generateIdeogramImage(string $prompt, string $model): ?Image
    {
        $api = new IdeogramClient();

        $payload = [
            "image_request" => [
                "prompt" => $prompt,
            ]
        ];

        switch ($model) {
            case "ideogram_v_1":
                $payload["image_request"]["model"] = "V_1";
                break;
            case "ideogram_v_1_turbo":
                $payload["image_request"]["model"] = "V_1_TURBO";
                break;
            case "ideogram_v_2":
                $payload["image_request"]["model"] = "V_2";
                break;
            case "ideogram_v_2_turbo":
                $payload["image_request"]["model"] = "V_2_TURBO";
                break;
            case "ideogram_v_2a":
                $payload["image_request"]["model"] = "V_2A";
                break;
            case "ideogram_v_2a_turbo":
                $payload["image_request"]["model"] = "V_2A_TURBO";
                break;
            default:
                $payload["image_request"]["model"] = "V_2";
        }

        $result = $api->generate($payload);

        if (!empty(Arr::get($result, "data.0.url"))) {
            return Image::fromUrl(Arr::get($result, "data.0.url"));
        }

        return null;
    }

    /**
     * Generate a Flux image
     *
     * @param string $prompt
     * @param string $model
     * @return Image|null
     */
    private function generateFluxImage(string $prompt, string $model): ?Image
    {
        $api = new FluxClient();

        $payload = [
            "prompt" => $prompt,
        ];

        switch ($model) {
            case "flux_1_dev":
                $payload["model"] = "flux-dev";
                break;
            case "flux_1_pro":
                $payload["model"] = "flux-pro";
                break;
            case "flux_1_1_pro":
                $payload["model"] = "flux-pro-1.1";
                break;
            case "flux_1_1_pro_ultra":
                $payload["model"] = "flux-pro-1.1-ultra";
                break;
            default:
                $payload["model"] = "flux-dev";
        }

        $result = $api->createTask($payload);

        $pollingUrl = Arr::get($result, "polling_url");
        if (!empty($id)) {

            while (true) {

                // wait 3 seconds for the task to be ready
                sleep(3);

                $result = $api->getResult($pollingUrl);

                switch (Arr::get($result, "status")) {
                    case "Pending":
                        break;
                    case "Ready":
                        if (!empty(Arr::get($result, "result.sample"))) {
                            return Image::fromUrl(Arr::get($result, "result.sample"));
                        }
                        break;
                }
            }
        }

        return null;
    }

    /**
     * Generate a dall-e image
     *
     * @param string $prompt
     * @param string $model
     * @return Image|null
     */
    private function generateDallEImage(string $prompt, string $model): ?Image
    {
        // get model id
        $modelData = Image::imageModelToProviderAndModel($model);
        $model = Arr::get($modelData, "model", "flux-dev");

        // make the settings
        $payload = [
            "model" => $model,
            "quality" => "standard",
            "size" => "1024x1024",
        ];

        if ($model === "dall-e-3") {
            $payload["size"] = "1792x1024";
        } elseif ($model === "gpt-image-1") {
            $payload["size"] = "auto";

            $payload["quality"] = Arr::get($modelData, "params.quality", "auto");
            $payload["moderation"] = "low";
        }

        $api = new OpenAiClient();

        $result = $api->image($prompt, $payload);

        if (!empty(Arr::get($result, "data.0.b64_json"))) {
            return Image::fromBase64(Arr::get($result, "data.0.b64_json"));
        }

        return null;
    }

    /**
     * Make a username from a name
     *
     * @param string $name
     * @return string
     */
    private function makeUsername(string $name): string
    {
        $username = $name;
        $index = 1;

        while (username_exists($username)) {
            $username = $name . '.' . $index;
            $index++;
        }

        return $username;
    }

    /**
     * Get a random writing style
     *
     * @return string
     */
    private function getRandomWritingStyle(): string
    {
        $values = [
            "courteous",
            "creative",
            "curious",
            "dynamic",
            "funny",
            "effective",
            "empathetic",
            "enthusiastic",
            "jovial",
            "methodical",
            "motivated",
            "organized",
            "versatile",
            "professional",
            "sensitive",
            "sociable"
        ];

        return $values[array_rand($values)];
    }
}
