<?php declare(strict_types=1);
namespace Newland\Toubiz\Sync\Neos\Domain\Model;

/*
 * 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\ORM\Mapping as ORM;
use Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Api\Service\StringCleaner;
use Newland\Toubiz\Sync\Neos\Domain\Model\Traits\PersistenceObjectIdentifier;
use function Safe\json_encode;

/**
 * An attribute for an article, tour, etc.
 *
 * @Flow\Entity
 * @ORM\Table(indexes={
 *      @ORM\Index(name="newland_toubiz_domain_model_attribute_identification_hash", columns={"identificationHash"}),
 *      @ORM\Index(name="newland_toubiz_domain_model_attribute_name_and_type", columns={"name", "type"})
 * })
 */
class Attribute implements \JsonSerializable
{
    use PersistenceObjectIdentifier;
    public const TYPE_STRING = 'string';
    public const TYPE_NUMBER = 'number';
    public const TYPE_BOOL = 'bool';
    public const TYPE_JSON = 'json';
    public const BOOL_TRUE = 'true';
    public const BOOL_FALSE = 'false';

    /**
     * @param mixed $data
     */
    public static function generateIdentificationHash(Article $article, string $name, $data): string
    {
        return md5(json_encode([
               $article->getOriginalId(),
               $article->getLanguage(),
               $article->getClient(),
               $name,
               $data
           ]));
    }

    /**
     * @ORM\ManyToOne(inversedBy="attributes")
     * @var Article|null
     */
    protected $article;

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

    /**
     * @ORM\Column(type="text", nullable=true)
     * @Flow\IgnoreValidation()
     * @var string|null
     *
     * Note: Validation is ignored since the getter does not reflect the actual state of the entity.
     */
    protected $data;

    /**
     * @ORM\Column(type="string", length=10, nullable=true)
     * @var string|null
     */
    protected $type;

    /**
     * @ORM\Column(type="string", length=32, nullable=true)
     * @var string|null
     */
    protected $identificationHash;

    public function getArticle(): ?Article
    {
        return $this->article;
    }

    public function setArticle(Article $article): void
    {
        $this->article = $article;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }

    /**
     * @return mixed
     */
    public function getData()
    {
        if ($this->data === null) {
            return null;
        }

        switch ($this->type) {
            case static::TYPE_NUMBER:
                return (float) $this->data;

            case static::TYPE_BOOL:
                if (is_string($this->data) && strtolower($this->data) === static::BOOL_TRUE) {
                    return true;
                }
                if (is_string($this->data) && strtolower($this->data) === static::BOOL_FALSE) {
                    return false;
                }
                return (bool) $this->data;

            case static::TYPE_JSON:
                return json_decode($this->data, true);

            case static::TYPE_STRING:
            default:
                return $this->data;
        }
    }

    /**
     * @param mixed $data
     */
    public function setData($data): void
    {
        if (is_string($data)) {
            $this->type = static::TYPE_STRING;
            $data = StringCleaner::asString($data);
        } elseif (is_numeric($data)) {
            $this->type = static::TYPE_NUMBER;
        } elseif (is_bool($data)) {
            $data = $data ? static::BOOL_TRUE : static::BOOL_FALSE;
            $this->type = static::TYPE_BOOL;
        } else {
            $this->type = static::TYPE_JSON;
            $data = json_encode($data);
        }

        $this->data = $data;
    }

    public function getIdentificationHash(): ?string
    {
        return $this->identificationHash;
    }

    public function setIdentificationHash(string $identificationHash): void
    {
        $this->identificationHash = $identificationHash;
    }

    public function getType(): ?string
    {
        return $this->type;
    }

    public function jsonSerialize(): array
    {
        return get_object_vars($this);
    }
}
