<?php

/** @noinspection PhpUnhandledExceptionInspection */

namespace DefStudio\GameEngine\Http\Livewire\Runs\Concerns;

use DefStudio\GameEngine\Models\Mission;
use DefStudio\GameEngine\Enums\StorytellingType;
use DefStudio\GameEngine\Decorators\ModelDecorator;
use DefStudio\GameEngine\Exceptions\RunnerException;
use DefStudio\GameEngine\Decorators\Runs\PlayableMission;
use DefStudio\GameEngine\Models\Contracts\WithStorytelling;

/**
 * @property-read PlayableMission $mission
 */
trait RunsMissions
{
    protected function start_runs_missions(): void
    {
        if (!$this->is_mission_active()) {
            return;
        }

        $this->show_storytelling($this->mission, StorytellingType::activation);
    }

    public function get_missions(): array
    {
        if (!$this->is_map_active()) {
            return [];
        }

        return $this->map->missions->map(function(Mission $mission): array {

            $mission = PlayableMission::build($mission)->setup($this->run);

            return [
                'id' => $mission->id,
                'placeholder_image' => $mission->current_placeholder()->getUrl(),
                'pos_x' => $mission->pos_x,
                'pos_y' => $mission->pos_y,
                'visible' => $mission->visible(),

            ];
        })->toArray();
    }

    protected function reset_mission_state(): void
    {
        $this->run->set_state('mission', ['index' => -1]);
        $this->run->set_state('task', ['index' => -1]);

        $this->reset_storytellings('mission');
        $this->reset_storytellings('task');

        $this->forgetComputed('mission');

    }

    protected function is_mission_active(): bool
    {
        return $this->current_mission_index() >= 0;
    }

    public function current_mission_index(): int
    {
        return $this->run->get_state('mission.index', -1);
    }

    public function getMissionProperty(): PlayableMission
    {
        $mission = $this->map->missions
            ->sortBy('order')->values()
            ->get($this->current_mission_index());

        throw_if($mission === null, RunnerException::no_active_mission());

        return PlayableMission::build($mission)->setup($this->run);
    }

    public function next_mission(): void
    {
        if ($this->is_mission_active()) {
            throw_unless($this->mission->completed(), RunnerException::incomplete_mission($this->mission));

            if ($this->current_mission_index() >= $this->map->missions->count() - 1) {
                $this->complete_map();

                return;
            }
        }

        $this->activate_mission($this->current_mission_index() + 1);

        $this->save();
    }

    protected function activate_mission(int $index): void
    {
        $this->reset_mission_state();

        $this->run->set_state('mission.index', $index);

        if ($this->run->get_state("missions.{$this->mission->id}.extracted_tasks") === null) {
            $extracted = game_engine()->task_extractor()->extract($this->run, $this->mission->model);

            $this->run->set_state("missions.{$this->mission->id}.extracted_tasks", $extracted->pluck('id')->toArray());
        }

        $this->forgetComputed('mission');

        $this->emit('mission:activated');

        $this->setup_storytellings($this->mission, true);

        if (!$this->show_storytelling($this->mission, StorytellingType::activation) && $this->mission->should_complete_on_activation()) {
            $this->complete_mission();

            return;
        }

        $this->save();
    }

    public function start_mission(int $index): void
    {
        if ($this->current_mission_index() !== $index) {
            throw RunnerException::mission_not_active($this->map->missions->sortBy('order')->values()->get($index));
        }

        if ($this->mission->all_tasks_completed()) {
            $this->complete_mission();

            return;
        }

        if (!$this->show_storytelling($this->mission, StorytellingType::prologue)) {
            $this->open_mission_modal();
        }
    }

    protected function complete_storytelling_runs_missions(string|ModelDecorator|WithStorytelling $scope, StorytellingType|string $type): void
    {
        $type = StorytellingType::build_from($type);
        $scope = $this->get_storytelling_scope($scope);

        if ($scope !== 'mission') {
            return;
        }

        if ($type === StorytellingType::activation) {
            if ($this->mission->should_complete_on_activation()) {
                $this->complete_mission();
            }

            return;
        }

        if ($type === StorytellingType::prologue) {
            $this->open_mission_modal();

            return;
        }

        $this->complete_mission();
    }

    protected function open_mission_modal(): void
    {
        $this->emit('openModal', 'game-engine.runner.mission', [
            'run_id' => $this->run->id,
            'mission_id' => $this->mission->id,
        ]);
    }

    public function complete_mission(): void
    {
        throw_if(!$this->mission->all_tasks_completed(), RunnerException::incomplete_mission($this->mission));

        if ($this->show_storytelling($this->mission, StorytellingType::epilogue)) {
            return;
        }

        $this->run->set_state("missions.{$this->mission->id}.completed", true);
        $this->run->save();

        $this->next_mission();

        if ($this->storytellings_waiting_to_open()->isEmpty()) {
            $this->emit('closeModal');
        }
    }
}
