<?php

/** @noinspection PhpPossiblePolymorphicInvocationInspection */

/** @noinspection PhpUnhandledExceptionInspection */

namespace DefStudio\GameEngine\Http\Livewire\Tasks;

use ReflectionClass;
use Livewire\WithFileUploads;
use Illuminate\Contracts\View\View;
use LivewireUI\Modal\ModalComponent;
use DefStudio\GameEngine\Models\Task;
use DefStudio\GameEngine\Actions\Tasks\SaveTask;
use DefStudio\GameEngine\Exceptions\TaskException;
use DefStudio\GameEngine\Decorators\Tasks\TaskDecorator;
use DefStudio\GameEngine\Attributes\CallableFromLivewire;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use DefStudio\GameEngine\Http\Livewire\Concerns\ImprovedModal;
use DefStudio\GameEngine\Http\Livewire\Concerns\RedirectsBack;
use DefStudio\GameEngine\Http\Livewire\Concerns\UploadsAssetsFromEditor;

class Edit extends ModalComponent
{
    use ImprovedModal;
    use WithFileUploads;
    use AuthorizesRequests;
    use RedirectsBack;
    use UploadsAssetsFromEditor;

    /** @var TaskDecorator|Task */
    public $task;

    public array $rules = [
        'task.title' => 'required|string|max:255',
        'task.type' => 'required|string|max:255',
        'task.mission_id' => 'required',
        'task.configuration' => 'nullable',
    ];

    public function mount(int|null $task_id = null, int|null $mission_id = null): void
    {
        throw_if($task_id === null && $mission_id === null, TaskException::must_be_bound_to_a_mission());

        $this->task = $task_id !== null
            ? game_engine()->taskQuery()->findOrFail($task_id)->decorate()
            : game_engine()->taskQuery()->make()->forceFill(['mission_id' => $mission_id]);

        $this->authorize('save', $this->task);
    }

    public function render(): View
    {
        return view('game-engine::livewire.admin.tasks.edit');
    }

    public function hydrateTask(Task|TaskDecorator $task): void
    {
        if ($task instanceof TaskDecorator) {
            return;
        }

        if (!$task->exists) {
            return;
        }

        $this->task = $task->decorate();
    }

    public function dehydrateTask(Task|TaskDecorator $value): void
    {

        if ($value instanceof Task) {
            return;
        }

        $this->task = $value->model;
    }

    public function save(): void
    {
        $this->validate();

        if ($this->task instanceof TaskDecorator) {
            $this->task->saving_from_livewire_editor($this);
        }

        $this->task = SaveTask::run($this->task)->decorate();

        if ($this->task->wasRecentlyCreated) {
            $this->emit('task.created', $this->task->id);
        } else {
            $this->emit('task.updated', $this->task->id);
        }

        $this->emit('task.saved', $this->task->id);

        if ($this->isModal) {
            if (!$this->task->wasRecentlyCreated) {
                $this->closeModal();
            }

            return;
        }

        $this->redirect_back();
    }

    public function callMethod($method, $params = [], $captureReturnValueCallback = null): void
    {
        if ($this->is_decorator_method($method)) {
            $returned = $this->task->{$method}(...$params);
            $captureReturnValueCallback && $captureReturnValueCallback($returned);
        } else {
            parent::callMethod($method, $params, $captureReturnValueCallback);
        }
    }

    private function is_decorator_method($method): bool
    {
        if (!$this->task instanceof TaskDecorator) {
            return false;
        }

        if (!method_exists($this->task, $method)) {
            return false;
        }

        $reflection = new ReflectionClass($this->task);
        $method_reflection = $reflection->getMethod($method);

        return $method_reflection->getAttributes(CallableFromLivewire::class) !== [];
    }
}
