<?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\AddressAdapterInterface;
use Newland\Toubiz\Api\Service\GeoLocation\GeoLocationServiceInterface;
use Newland\Toubiz\Sync\Neos\Domain\Model\Address;
use Newland\Toubiz\Sync\Neos\Domain\Repository\AddressRepository;
use Newland\Toubiz\Sync\Neos\Orm\Uuid\UuidGenerator;
use Newland\Toubiz\Sync\Neos\Service\GeoLocationFactory;

/**
 * Address importer.
 *
 * @Flow\Scope("singleton")
 *
 * @property AddressAdapterInterface $data
 */
class AddressImporter extends AbstractImporter
{
    /**
     * @Flow\Inject()
     * @var AddressRepository
     */
    protected $addressRepository;


    /**
     * @var GeoLocationServiceInterface
     */
    protected $geoLocationService;

    public function injectGeoLocationService(GeoLocationFactory $factory): void
    {
        $this->geoLocationService = $factory->get('Newland.Toubiz.Sync.Neos.services.GeoLocation');
    }

    /**
     * Import method.
     *
     * Persist given data by creating new objects or updating existing ones.
     *
     * @param AddressAdapterInterface $data
     * @return Address
     */
    public function import($data)
    {

        $address = $this->findAddress($data);
        $persisted = $address !== null;
        /** @var Address $address */
        $address = $address ?: new Address();
        $address->setOriginalId($data->getExternalId());
        $address->setName($data->getTitle());
        $address->setFirstName($data->getFirstName());
        $address->setLastName($data->getLastName());
        $address->setStreet($data->getStreet());
        $address->setZip($this->getZip($address, $data));
        $address->setCity($data->getCity());
        $address->setPhoneNumber($data->getPhoneNumber());
        $address->setFaxNumber($data->getFaxNumber());
        $address->setEmailAddress($data->getEmailAddress());
        $address->setWebsiteAddress($data->getWebsiteAddress());
        $address->setLanguage($this->language);
        $this->mapCoordinates($address, $data);
        if ($persisted) {
            $this->addressRepository->update($address);
        } else {
            $this->addressRepository->add($address);
        }

        $this->persistenceManager->persistAll();
        return $address;
    }

    /**
     * @param AddressAdapterInterface $data
     * @return Address|null
     */
    protected function findAddress($data): ?Address
    {
        return $this->addressRepository->withLanguage(
            $this->language,
            function () use ($data) {
                $uuid = UuidGenerator::uuidFromProperties(
                    [
                        $data->getExternalId(),
                        $this->language
                    ]
                )->toString();
                return $this->addressRepository->findByIdentifier($uuid);
            }
        );
    }

    private function mapCoordinates(Address $address, AddressAdapterInterface $data): void
    {
        $latitude = (float) $data->getLatitude();
        $longitude = (float) $data->getLongitude();
        if (abs($latitude) > .1 && abs($longitude) > .1) {
            $address->setLatitude($latitude);
            $address->setLongitude($longitude);
        } else {
            $address->setLatitude(null);
            $address->setLongitude(null);
        }
    }

    private function getZip(Address $address, AddressAdapterInterface $data): ?string
    {
        $zip = $data->getZip();

        // Some addresses have no zip code.
        // Zip codes can then be looked up once if needed.
        if (empty($zip) && empty($address->getZip()) && $data->isLookup()) {
            $zip = $this->geoLocationService->getZipFromGeoLocation(
                $data->getLatitude(),
                $data->getLongitude()
            );
        }

        return $zip;
    }
}
