<?php

namespace Comitium5\DesignerBundle\Form\DataTransformer;

use Comitium5\ApiClientBundle\ApiClient\ResourcesTypes;
use Comitium5\ApiClientBundle\Client\ClientInterface;
use Comitium5\DesignerBundle\Helper\Utils;
use Comitium5\DesignerBundle\Model\Interfaces\EnabledInterface;
use Comitium5\DesignerBundle\ValueObjects\SearchEntityObject;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;

/**
 * Class AbstractSuggestDataTransformer
 *
 * @author David Carrascosa <david@bab-soft.com>
 * @package Comitium5\DesignerBundle\Form\DataTransformer
 */
abstract class AbstractSuggestDataTransformer implements DataTransformerInterface
{
    /**
     * @var ClientInterface
     */
    protected $apiService;

    /**
     * @var string
     */
    protected $contentType = '';

    /**
     * @var string
     */
    protected $property = "id";

    /**
     * @var bool
     */
    protected $multiple = false;

    /**
     * @var string
     */
    protected $separator = '|';

    /**
     * @var null
     */
    protected $labelProperties = null;

    /**
     * AbstractSuggestDataTransformer constructor.
     *
     * @param ClientInterface $apiService
     */
    public function __construct(
        ClientInterface $apiService
    ) {
        $this->apiService = $apiService;
    }

    /**
     * @param mixed $value
     *
     * @return string|null
     */
    abstract public function transform($value): string;

    /**
     * @param mixed $value
     *
     * @return mixed
     */
    abstract public function reverseTransform($value);

    /**
     * @param string $contentType
     *
     * @return $this
     */
    public function setContentType(string $contentType): self
    {
        $this->contentType = $contentType;

        return $this;
    }

    /**
     * @param string $property
     *
     * @return $this
     */
    public function setProperty(string $property): self
    {
        $this->property = $property;

        return $this;
    }

    /**
     * @param bool $multiple
     *
     * @return $this
     */
    public function setMultiple(bool $multiple): self
    {
        $this->multiple = $multiple;

        return $this;
    }

    /**
     * @param string $separator
     *
     * @return $this
     */
    public function setSeparator(string $separator): self
    {
        $this->separator = $separator;

        return $this;
    }

    /**
     * @param string $labelProperties
     *
     * @return $this
     */
    public function setLabelProperties(string $labelProperties): self
    {
        $this->labelProperties = $labelProperties;

        return $this;
    }

    /**
     * @param $id
     *
     * @return array|null
     */
    protected function fetchEntity($id): ?array
    {
        // Found always by id not by property because maybe there are more
        // entities with same property values
        $entity = $this
            ->apiService
            ->find($this->contentType, $id);

        if (null === $entity) {
            return null;
        }

        if (empty($entity['data'])) {
            return null;
        }

        return $entity['data'];
    }

    /**
     * @param array $entity
     *
     * @return string
     */
    protected function resolveText(array $entity): string
    {
        if ($this->labelProperties !== null && $this->labelProperties !== "") {
            return self::resolveLabelProperties($entity, $this->labelProperties);
        }

        $accessor = PropertyAccess::createPropertyAccessor();

        return $accessor->getValue($entity, self::resolvePropertyAccesParameter($entity, $this->property));
    }

    /**
     * @param $entity
     * @param string $labelProperties
     *
     * @return string
     */
    public static function resolveLabelProperties($entity, string $labelProperties): string
    {
        $label = "";

        $properties = [];

        $accessor = PropertyAccess::createPropertyAccessor();

        preg_match_all('/\%([^\%]+)\%/', $labelProperties, $matches);

        if (!empty($matches[1])) {
            foreach ($matches[1] as $match) {
                $properties[] = $accessor->getValue(
                    $entity,
                    self::resolvePropertyAccesParameter($entity, $match)
                );
            }

            $label = str_replace($matches[0], $properties, $labelProperties);
        }

        return $label;
    }

    /**
     * @param $entity
     *
     * @return bool
     * @throws \Exception
     */
    public static function resolveFuture($entity): bool
    {
        $now = new \DateTime();

        if ($entity instanceof SearchEntityObject && !empty($entity->getPublishDate())) {
            return $entity->getPublishDate() > $now;
        }

        if (is_array($entity) && !empty($entity['publishDate'])) {
            $entityDate = new \DateTime($entity['publishDate']);

            return $entityDate > $now;
        }

        return false;
    }

    /**
     * @param $entity
     *
     * @return string
     */
    public static function resolveEntityDate($entity): string
    {
        if ($entity instanceof SearchEntityObject && !empty($entity->getPublishDate())) {
            return $entity
                ->getPublishDate()
                ->format("d/m/Y H:i");
        }

        if (is_array($entity) && !empty($entity['publishDate'])) {
            return $entity['publishDate'];
        }

        return '';
    }

    /**
     * @param $entity
     *
     * @return bool
     */
    public static function resolveEnabledEntity($entity): bool
    {
        if ($entity instanceof EnabledInterface) {
            return $entity->getEnabled();
        }

        if (isset($entity['searchable'])) {
            return $entity['searchable'];
        }

        return true;
    }

    /**
     * @param SearchEntityObject $entity
     *
     * @return string
     */
    public static function resolveDataDescription(SearchEntityObject $entity): string
    {
        $description = "";

        if ($entity->getContentType() === ResourcesTypes::TAG) {
            $description = Utils::cutText($entity->getDescription(), 100);
        }

        if ($entity->getContentType() === ResourcesTypes::CATEGORY && $entity->getParent()) {
            $description = $entity->getParent()['name'];
        }

        return $description;
    }

    /**
     * @param $entity
     * @param string $property
     *
     * @return string
     */
    protected static function resolvePropertyAccesParameter($entity, string $property): string
    {
        if (is_object($entity)) {
            return $property;
        }

        return sprintf('[%s]', $property);
    }
}