<?php

namespace OtomaticAi\Jobs;

use DOMDocument;
use Exception;
use OtomaticAi\Models\Contracts\Publishable;
use OtomaticAi\Models\Netlinking;
use OtomaticAi\Models\Publication;
use OtomaticAi\Models\SingleModeGeneration;
use OtomaticAi\Models\ThumbnailTask;
use OtomaticAi\Modules\Module;
use OtomaticAi\Modules\ProcessAmazonModule;
use OtomaticAi\Modules\ProcessCustomFieldsModule;
use OtomaticAi\Modules\ProcessFaqModule;
use OtomaticAi\Modules\ProcessFinalizeModule;
use OtomaticAi\Modules\ProcessImageModule;
use OtomaticAi\Modules\ProcessSingleModeFinalizeModule;
use OtomaticAi\Modules\ProcessTextModule;
use OtomaticAi\Modules\ProcessToolboxModule;
use OtomaticAi\Modules\ProcessWordpressModule;
use OtomaticAi\Modules\ProcessYoutubeModule;
use OtomaticAi\Services\PublicationGenerationState;
use OtomaticAi\Utils\Analytics;
use OtomaticAi\Vendors\Illuminate\Support\Arr;
use OtomaticAi\Vendors\Illuminate\Database\Capsule\Manager as Database;
use Throwable;

class ProcessPublicationStepJob extends Job
{
    const HOOK = "process_publication_step";

    private Publishable $publishable;

    public function __construct($attrs)
    {
        $id = Arr::get($attrs, "id");
        $modelClass = Arr::get($attrs, "model_class", Publication::class);

        $this->publishable = $modelClass::find($id);
    }

    public function handle()
    {
        try {
            $this->increaseTimeout();

            // quit if no publication
            if (!$this->publishable) {
                return;
            }

            // get the generation state
            $generationState = new PublicationGenerationState($this->publishable);

            // get the current step
            $currentStep = $generationState->getCurrentStep();

            // quit if no current step
            if (!$currentStep) {
                return;
            }

            // quit if the current step is not idle
            if (Arr::get($currentStep, "status") !== "idle") {
                return;
            }

            // set the step status to running
            $generationState->setStepStatus($currentStep["name"], "running");

            // set the current user
            $this->setCurrentUser();

            // get the step module
            $module = $this->getStepModule($currentStep);

            $job = new $module($this->publishable, $generationState);
            $job->handle();

            $generationState->setStepStatus($currentStep["name"], "success");

            $nextStep = $generationState->getNextStep();
            if ($nextStep) {
                $generationState->setCurrentStep($nextStep["name"]);

                // dispatch the next step job
                ProcessPublicationStepJob::dispatch(["id" => $this->publishable->id, "model_class" => $this->publishable->getMorphClass()], 5);
            } else {
                // set the publication status to success
                $generationState->setStatus("completed");
                $this->publishable->status = "success";
                $this->publishable->save();
            }
        } catch (Throwable $e) {
            return $this->fail($e);
        }
    }

    private function increaseTimeout()
    {
        global $wpdb;

        if (function_exists("set_time_limit")) {
            set_time_limit(1200);
        }
        Database::update("SET SESSION wait_timeout=1200;");
        Database::update("SET SESSION interactive_timeout=1200;");
        $wpdb->query("SET SESSION wait_timeout=1200;");
        $wpdb->query("SET SESSION interactive_timeout=1200;");
    }

    private function setCurrentUser()
    {
        $authorId = null;
        if ($this->publishable instanceof Publication) {
            $authorId = Arr::get($this->publishable->project->modules, 'wordpress.author_id');
        } elseif ($this->publishable instanceof Netlinking) {
            $authorId = Arr::get($this->publishable->modules, 'wordpress.author_id');
        }

        if (!empty($authorId)) {
            wp_set_current_user($authorId);
        } else {
            $firstUser = Arr::get(get_users(["fields" => 'ID']), 0);
            if (!empty($firstUser)) {
                wp_set_current_user($firstUser);
            }
        }
    }

    private function getStepModule(array $step): string
    {
        $modules = Module::getList();

        foreach ($modules as $module) {
            if ($module::SLUG_NAME === $step["name"]) {
                return $module;
            }
        }

        throw new Exception("Module not found");
    }

    private function fail(Throwable $error)
    {
        $this->publishable->addLog("Publication failed. " . $error->getMessage(), "_default", "error", $error->getTrace());

        // track the failure of the job
        if ($this->publishable instanceof Publication) {
            Analytics::trackEvent("publish-job-failed", [
                "title" => $this->publishable->title,
                "type" => $this->publishable->type,
                "model" => Arr::get($this->publishable->modules, "models.text", 'gpt-4o-mini'),
                "image_model" => Arr::get($this->publishable->modules, "models.image", 'dall-e-3'),
                "message" => $error->getMessage(),
            ]);
        } elseif ($this->publishable instanceof SingleModeGeneration) {
            Analytics::trackEvent("generate-single-mode-failed", [
                "title" => $this->publishable->title,
                "type" => $this->publishable->type,
                "model" => Arr::get($this->publishable->modules, "models.text", 'gpt-4o-mini'),
                "image_model" => Arr::get($this->publishable->modules, "models.image", 'dall-e-3'),
                "message" => $error->getMessage(),
            ]);
        } elseif ($this->publishable instanceof Netlinking) {
            Analytics::trackEvent("generate-netlinking-failed", [
                "uuid" => $this->publishable->uuid,
                "model" => Arr::get($this->publishable->modules, "models.text", 'gpt-4o-mini'),
                "image_model" => Arr::get($this->publishable->modules, "models.image", 'dall-e-3'),
                "message" => $error->getMessage(),
            ]);
        } elseif ($this->publishable instanceof ThumbnailTask) {
            Analytics::trackEvent("generate-thumbnail-failed", [
                "image_model" => Arr::get($this->publishable->modules, "models.image", 'dall-e-3'),
                "message" => $error->getMessage(),
            ]);
        }

        $this->publishable->status = "failed";
        $this->publishable->save();
    }
}
