<?php
namespace Newland\Toubiz\Sync\Neos\Importer;

/*
 * 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 Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Api\ObjectAdapter\ArticleAdapterInterface;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Domain\Model\Category;
use Newland\Toubiz\Sync\Neos\Domain\Model\Medium;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;

/**
 * Article importer.
 *
 * @Flow\Scope("singleton")
 */
class ArticleImporter extends AbstractImporter
{
    /**
     * @var ArticleAdapterInterface
     */
    protected $data;

    /**
     * @var Article
     */
    protected $article;

    /**
     * @Flow\Inject
     * @var ArticleRepository
     */
    protected $articleRepository;

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

    /**
     * @var string
     */
    protected $openingTimesFormat;

    /**
     * @param ArticleAdapterInterface $data
     * @return void
     */
    public function remove(ArticleAdapterInterface $data): void
    {
        $this->initialize($data);

        if ($this->article !== null) {
            $this->articleRepository->remove($this->article);
            $this->persistenceManager->persistAll();
        }
    }

    /**
     * Import method.
     *
     * Persist given data by creating new objects or updating existing ones.
     *
     * @param ArticleAdapterInterface $data
     * @return void
     */
    public function import($data): void
    {
        $this->initialize($data);
        $persisted = $this->article !== null;

        if ($this->article === null) {
            $this->article = new Article();
        }

        $this->mapSimpleValues();
        $this->mapRatingValues();
        $this->article->setClient($this->client);
        $this->mapOpeningTimes();

        $this->mapAddresses();
        $this->mapCategories();
        $this->mapFiles();
        $this->mapMedia();
        $this->mapAttributes();
        $this->article->setUpdatedAt(new \DateTime);

        if ($persisted) {
            $this->articleRepository->update($this->article);
        } else {
            $this->articleRepository->add($this->article);
        }
    }


    /**
     * @param ArticleAdapterInterface $data
     * @return void
     */
    private function initialize(ArticleAdapterInterface $data): void
    {
        $this->data = $data;

        $language = $this->data->getLanguage();
        if ($language) {
            $this->articleRepository->setLanguage($language);
        }

        if ($this->client) {
            $article = $this->articleRepository->findOneByOriginalIdAndClient(
                $this->data->getExternalId(),
                $this->client
            );
        } else {
            $article = $this->articleRepository->findOneByOriginalId($this->data->getExternalId());
        }

        if ($article !== null) {
            $this->article = $article;
        }
    }

    /**
     * @param string $client
     */
    public function setClient(string $client): void
    {
        $this->client = $client;
    }

    public function setOpeningTimesFormat(string $openingTimesFormat): void
    {
        $this->openingTimesFormat = $openingTimesFormat;
    }

    /**
     * Maps simple values onto the article.
     *
     * @return void
     */
    protected function mapSimpleValues(): void
    {
        $this->article->setOriginalId($this->data->getExternalId());
        $this->article->setMainType($this->data->getMainType());
        $this->article->setName($this->data->getName());
        $this->article->setAbstract($this->data->getAbstract());
        $this->article->setDescription($this->data->getDescription());
        $this->article->setFacebookUri($this->data->getFacebookUri());
        $this->article->setTwitterUri($this->data->getTwitterUri());
        $this->article->setInstagramUri($this->data->getInstagramUri());
        $this->article->setYoutubeUri($this->data->getYoutubeUri());
        $this->article->setWikipediaUri($this->data->getWikipediaUri());
        $this->article->setFlickrUri($this->data->getFlickrUri());
        $this->article->setSourceName($this->data->getSourceName());
        $this->article->setAuthorName($this->data->getAuthorName());
        $this->article->setBookingUri($this->data->getBookingUri());
        $this->article->setDetailUri($this->data->getDetailUri());
        $this->article->setLanguage($this->data->getLanguage());

        $mainAddressAdapter = $this->data->getMainAddress();
        if ($mainAddressAdapter) {
            $this->article->setMainAddress(
                (new AddressImporter())->import($mainAddressAdapter)
            );
        } else {
            $this->article->setMainAddress(null);
        }
    }

    protected function mapRatingValues(): void
    {
        $this->article->setStarClassification($this->data->getStarClassification() ?? 0);
        $this->article->setAverageRating($this->data->getAverageRating() ?? 0);
        $this->article->setNumberOfRatings($this->data->getNumberOfRatings() ?? 0);
    }

    /**
     * Maps addresses onto the article.
     *
     * @return void
     */
    protected function mapAddresses(): void
    {
        $addresses = $this->article->getAddresses();
        $addresses->clear();

        foreach ($this->data->getAddresses() as $address) {
            $importer = new AddressImporter();
            $entity = $importer->import($address);
            if (!$addresses->contains($entity)) {
                $addresses->add($entity);
            }
        }

        $this->article->setAddresses($addresses);
    }

    /**
     * Maps categories onto the article.
     *
     * @return void
     */
    protected function mapCategories(): void
    {
        $categories = $this->article->getCategories();
        $categories->clear();

        foreach ($this->data->getCategories() as $entry) {
            $importer = new CategoryImporter();
            $importer->setLanguage((string) $this->data->getLanguage());
            $category = $importer->import($entry);

            if (!$categories->contains($category)) {
                $categories->add($category);
            }
        }

        $this->article->setCategories($categories);

        $sorting = array_map(
            function (Category $category) {
                return $category->getOriginalId();
            },
            $categories->toArray()
        );

        $this->article->setRelationSortingForField('categories', $sorting);
    }

    /**
     * Maps multimedia items onto the article.
     *
     * @return void
     */
    protected function mapMedia(): void
    {
        $media = $this->article->getMedia();
        $media->clear();

        foreach ($this->data->getMedia() as $entry) {
            $medium = (new MediumImporter)->import($entry);
            if (!$media->contains($medium)) {
                $media->add($medium);
            }
        }
        $this->article->setMedia($media);

        $sorting = array_map(
            function (Medium $medium) {
                return $medium->getOriginalId();
            },
            $media->toArray()
        );

        $this->article->setRelationSortingForField('media', $sorting);
    }

    /**
     * Maps files onto the article.
     *
     * @return void
     */
    protected function mapFiles(): void
    {
        $files = $this->article->getFiles();
        $files->clear();

        foreach ($this->data->getFiles() as $entry) {
            $file = (new FileImporter)->import($entry);
            if (!$files->contains($file)) {
                $files->add($file);
            }
        }
        $this->article->setFiles($files);
    }

    public function mapOpeningTimes(): void
    {
        if ($this->openingTimesFormat) {
            $this->article->setOpeningTimesFormat($this->openingTimesFormat);
            $this->article->setOpeningTimes($this->data->getOpeningTimes() ?? '');
        }
    }

    public function deleteOldRecords(\DateInterval $timeToLive, int $mainType = null): int
    {
        return $this->articleRepository->deleteOldRecords($timeToLive, $mainType);
    }

    /**
     * Maps attributes onto the article.
     *
     * @return void
     */
    protected function mapAttributes(): void
    {
        $attributes = $this->article->getAttributes();
        $attributes->clear();

        if ($this->data->hasAttributes()) {
            foreach ($this->data->getAttributes() as $attributeData) {
                $importer = new AttributeImporter();
                $importer->setArticle($this->article);
                $attribute = $importer->import($attributeData);
                if (!$attributes->contains($attribute)) {
                    $attributes->add($attribute);
                }
            }
        }

        $this->article->setAttributes($attributes);
    }
}
