<?php

namespace App\Support;

use App\Models\Objects\NirObject;
use App\Models\Objects\ObjectType;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

trait HasObjectsTrait {
    public function objects(): MorphToMany {
        return $this->morphToMany(NirObject::class, 'objectable')->withPivot(['ord', 'group'])->orderByPivot('ord')->withTimestamps();
    }

    public function objectsByGroup($group = null): MorphToMany {
        return $this->objects()->wherePivot('group', '=', $group ?? 'default');
    }


    public function getObjectAttribute() {
        return $this->objects()->first();
    }


    public function getObject($typeName, $group = null): ?Model {
        if ($type = ObjectType::byName($typeName)->first()) {
            return $this->objectsByGroup($group ?? 'default')->where(['type_id' => $type->id])->first() ?? $this->createObject($typeName, null, $group ?? 'default');
        }
        return null;
    }

    public function createObject($typeName, $ord = null, $group = null): ?Model {
        if (($type = ObjectType::byName($typeName)->first()) && ($object = NirObject::create(['type_id' => $type->id]))) {
            $this->attachObject($object, $ord, $group);
            return $object;
        }
        return null;
    }

    public function attachObject(NirObject $object, $ord = null, $group = null) {
        $ord = ($ord === null) ? $this->getMaxOrd($group) : $ord;
        $this->moveObjectsSet('forward', $ord, null, $group);
        $this->objects()->attach($object->id, ['ord' => $ord ?? 0, 'group' => $group ?? 'default']);
    }

    public function getMaxOrd($group = null): int {
        $res = $this->objectsByGroup($group)->max('ord');
        return ($res !== null) ? ($res + 1) : 0;
    }


    public function moveObject(NirObject $object, $ord, $group = null) {
        $currentGroup = $object->currentGroup($this);
        $group = $group ?? $currentGroup;
        $currentOrd = $object->currentOrd($this);
        if ($group === $currentGroup) {
            ($ord > $currentOrd) ? $this->moveObjectsSet('backward', $currentOrd, $ord, $group) : $this->moveObjectsSet('forward', $ord, $currentOrd, $group);
        } else $this->moveObjectsSet('forward', $ord, null, $group);
        $this->objects()->updateExistingPivot($object, ['ord' => $ord, 'group' => $group ?? 'default']);
        $this->trimIndexes([$group, $currentGroup]);
    }

    public function moveObjectsSet($dir = 'forward', $ordFrom = null, $ordTo = null, $group = null) {
        $query = $this->objectsByGroup($group);
        if ($ordFrom !== null) $query->wherePivot('ord', '>=', $ordFrom);
        if ($ordTo !== null) $query->wherePivot('ord', '<=', $ordTo);
        $query->get()->each(function($object) use($dir) {
            $this->objects()->updateExistingPivot($object, ['ord' => ($dir === 'forward') ? ($object->pivot->ord + 1) : ($object->pivot->ord - 1)]);
        });
    }

    public function trimIndexes($groups) {
        collect(is_array($groups) ? $groups : [$groups])->unique()->each(function($group) {
            $this->objectsByGroup($group)->each(function($object, $index) {
                if ($object->pivot->ord !== $index) $this->objects()->updateExistingPivot($object, ['ord' => $index]);
            });
        });
    }


    public function getValue($fieldName) {
        return $this->object ? $this->object->getValue($fieldName) : null;
    }
    public function setValue($fieldName, $value) {
        return $this->object ? $this->object->setValue($fieldName, $value) : null;
    }
    public function setValues(array $values) {
        return $this->object ? $this->object->setValues($values) : null;
    }
    public function addValue($fieldName, $value) {
        return $this->object ? $this->object->addValue($fieldName, $value) : null;
    }

}
