<?php
namespace Newland\Toubiz\Sync\Neos\Domain\Model\Traits;

/*
 * This file is part of the "toubiz-sync-neos" package.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Neos\Flow\Property\Exception\InvalidPropertyException;

trait SortableRelationsTrait
{
    /**
     * @ORM\Column(type="array", nullable=true)
     * @var array|null
     */
    protected $relationSorting = [];

    public function getSortedRelations(string $propertyFieldName, string $sortByFieldName): array
    {
        $sortingAll = (array) $this->relationSorting;

        $relatedObjects = $this->getRelatedObjects($propertyFieldName);

        if (!array_key_exists($propertyFieldName, $sortingAll)) {
            // If no sorting is specified, we return the objects as they are.
            return $relatedObjects;
        }

        $sorting = (array) $sortingAll[$propertyFieldName];

        $relatedObjects = $this->sortObjects($relatedObjects, $sortByFieldName, $sorting);

        return $relatedObjects;
    }

    /**
     * @param string $fieldName
     * @param string[] $sorting
     */
    public function setRelationSortingForField(string $fieldName, array $sorting)
    {
        $this->relationSorting = (array) $this->relationSorting;
        $this->relationSorting[$fieldName] = $sorting;
    }

    private function getRelatedObjects(string $propertyFieldName): array
    {
        $getter = 'get' . ucfirst($propertyFieldName);
        if (!method_exists($this, $getter)) {
            throw new InvalidPropertyException(
                self::class . ' has no property "' . $propertyFieldName . '".',
                1556120715
            );
        }

        /** @var mixed $relatedObjects */
        $relatedObjects = $this->$getter();
        if ($relatedObjects instanceof Collection) {
            $relatedObjects = $relatedObjects->toArray();
        }
        $relatedObjects = (array) $relatedObjects;

        return $relatedObjects;
    }

    public function sortObjects(array $relatedObjects, string $sortByFieldName, array $sorting): array
    {
        $count = \count($relatedObjects);

        usort(
            $relatedObjects,
            function ($objectA, $objectB) use ($sorting, $count, $sortByFieldName) {
                $getter = 'get' . ucfirst($sortByFieldName);
                $indexA = array_search($objectA->$getter(), $sorting, true);
                if ($indexA === false) {
                    $indexA = $count;
                }
                $indexB = array_search($objectB->$getter(), $sorting, true);
                if ($indexB === false) {
                    $indexB = $count;
                }
                return $indexA - $indexB;
            }
        );

        return $relatedObjects;
    }
}
