<?php declare(strict_types=1);

namespace Newland\Toubiz\Sync\Neos\Tests\Integration;

use Newland\NeosTestingHelpers\IntegrationTestCase;
use Newland\Toubiz\Api\Service\GeoLocation\NullService;
use Newland\Toubiz\Api\Service\Outdooractive\ObjectAdapter\TourAdapter as OutdooractiveTourAdapter;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\DbService\CategoryMap;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\ObjectAdapter\CityApiService\CityAdapter;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\ObjectAdapter\DbService\PointOfInterestAdapter;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\ObjectAdapter\DirectMarketerApiService\DirectMarketerAdapter;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\ObjectAdapter\EventApiService\EventAdapter;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\ObjectAdapter\GastronomyApiService\GastronomyAdapter;
use Newland\Toubiz\Api\Service\Toubiz\Legacy\ObjectAdapter\TourApiService\TourAdapter as ToubizTourAdapter;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Importer\ArticleImporter;
use Newland\Toubiz\Sync\Neos\Importer\EventImporter;
use Newland\Toubiz\Sync\Neos\Service\GeoLocationFactory;
use Newland\Toubiz\Sync\Neos\Service\UuidPredictionService;
use Newland\Toubiz\Sync\Neos\Tests\Unit\Importer\Mock\GeoLocationFactoryMock;
use Psr\Log\NullLogger;

/**
 * This test imports different types of data from the same city based on real-world API responses
 * and checks if the entities are correctly assigned to the city.
 */
class CityRelationMatchingTest extends IntegrationTestCase
{

    public function setUp(): void
    {
        parent::setUp();
        $this->objectManager->setInstance(
            GeoLocationFactory::class,
            new GeoLocationFactoryMock(new NullService())
        );
    }

    public function tearDown(): void
    {
        $this->objectManager->forgetInstance(GeoLocationFactory::class);
        parent::tearDown();
    }

    /** @dataProvider provideGastronomyData */
    public function testMatchesCityRelationsForGastronomy($cityData, $gastroData): void
    {
        $cityAdapter = new CityAdapter($cityData);
        $gastroAdapter = new GastronomyAdapter($gastroData);

        /** @var ArticleImporter $importer */
        $importer = $this->objectManager->get(ArticleImporter::class);
        $importer->setUuidPredictionService($this->objectManager->get(UuidPredictionService::class));

        $city = $importer->import($cityAdapter);
        $gastro = $importer->import($gastroAdapter);

        $cityIds = $gastro->getCities()
            ->map(function(Article $city) { return $city->getPersistenceObjectIdentifier(); })
            ->toArray();

        $this->assertContains($city->getPersistenceObjectIdentifier(), $cityIds);
    }

    public function provideGastronomyData(): array
    {
        $data = [];
        $cityData = json_decode(
            file_get_contents(__DIR__ . '/CityRelationMatching/gastro/city.json'),
            true
        );
        foreach (glob(__DIR__ . '/CityRelationMatching/gastro/gastro*') as $file) {
            $data[basename($file)] = [ $cityData, json_decode(file_get_contents($file), true) ];
        }
        return $data;
    }


    /** @dataProvider providePoiData */
    public function testMatchesCityRelationsForPoi($cityData, $poiData): void
    {
        $cityAdapter = new CityAdapter($cityData);
        $poiAdapter = new PointOfInterestAdapter($poiData);
        $poiAdapter->setCategoryMap(new CategoryMap());
        $poiAdapter->setLogger(new NullLogger());

        /** @var ArticleImporter $importer */
        $importer = $this->objectManager->get(ArticleImporter::class);
        $importer->setUuidPredictionService($this->objectManager->get(UuidPredictionService::class));

        $city = $importer->import($cityAdapter);
        $poi = $importer->import($poiAdapter);

        $cityIds = $poi->getCities()
            ->map(function(Article $city) { return $city->getPersistenceObjectIdentifier(); })
            ->toArray();

        $this->assertContains($city->getPersistenceObjectIdentifier(), $cityIds);
    }

    public function providePoiData(): array
    {
        $data = [];
        $cityData = json_decode(
            file_get_contents(__DIR__ . '/CityRelationMatching/poi/city.json'),
            true
        );
        foreach (glob(__DIR__ . '/CityRelationMatching/poi/poi*') as $file) {
            $data[basename($file)] = [ $cityData, json_decode(file_get_contents($file), true) ];
        }
        return $data;
    }


    /** @dataProvider provideEventData */
    public function testMatchesCityRelationsForEvent($cityData, $eventData): void
    {
        $cityAdapter = new CityAdapter($cityData);
        $eventAdapter = new EventAdapter($eventData);

        /** @var ArticleImporter $articleImporter */
        $articleImporter = $this->objectManager->get(ArticleImporter::class);
        $articleImporter->setUuidPredictionService($this->objectManager->get(UuidPredictionService::class));
        $city = $articleImporter->import($cityAdapter);

        /** @var EventImporter $eventImporter */
        $eventImporter = $this->objectManager->get(EventImporter::class);
        $event = $eventImporter->import($eventAdapter);

        $this->assertEquals($city->getPersistenceObjectIdentifier(), $event->getCity()->getPersistenceObjectIdentifier());
    }


    public function provideEventData(): array
    {
        $data = [];
        $cityData = json_decode(
            file_get_contents(__DIR__ . '/CityRelationMatching/event/city.json'),
            true
        );
        foreach (glob(__DIR__ . '/CityRelationMatching/event/event*') as $file) {
            $data[basename($file)] = [ $cityData, json_decode(file_get_contents($file), true) ];
        }
        return $data;
    }


    /** @dataProvider provideDirectMarketerData */
    public function testMatchesCityRelationsForDirectMarketer($cityData, $directMarketerData): void
    {
        $cityAdapter = new CityAdapter($cityData);
        $directMarketerAdapter = new DirectMarketerAdapter($directMarketerData);

        /** @var ArticleImporter $importer */
        $importer = $this->objectManager->get(ArticleImporter::class);
        $importer->setUuidPredictionService($this->objectManager->get(UuidPredictionService::class));

        $city = $importer->import($cityAdapter);
        $poi = $importer->import($directMarketerAdapter);

        $cityIds = $poi->getCities()
            ->map(function(Article $city) { return $city->getPersistenceObjectIdentifier(); })
            ->toArray();

        $this->assertContains($city->getPersistenceObjectIdentifier(), $cityIds);
    }


    public function provideDirectMarketerData(): array
    {
        $data = [];
        $cityData = json_decode(
            file_get_contents(__DIR__ . '/CityRelationMatching/directMarketer/city.json'),
            true
        );
        foreach (glob(__DIR__ . '/CityRelationMatching/directMarketer/directMarketer*') as $file) {
            $data[basename($file)] = [ $cityData, json_decode(file_get_contents($file), true) ];
        }
        return $data;
    }

    /** @dataProvider provideOutdoorActiveData */
    public function testMatchesCityRelationsForOutdooractive($cityData, $tourData): void
    {
        $cityAdapter = new CityAdapter($cityData);
        $tourAdapter = new OutdooractiveTourAdapter($tourData);

        /** @var ArticleImporter $importer */
        $importer = $this->objectManager->get(ArticleImporter::class);
        $importer->setUuidPredictionService($this->objectManager->get(UuidPredictionService::class));

        $city = $importer->import($cityAdapter);
        $tour = $importer->import($tourAdapter);

        $cityIds = $tour->getCities()
            ->map(function(Article $city) { return $city->getPersistenceObjectIdentifier(); })
            ->toArray();

        $this->assertContains($city->getPersistenceObjectIdentifier(), $cityIds);
    }

    public function provideOutdoorActiveData(): array
    {
        $data = [];
        $cityData = json_decode(
            file_get_contents(__DIR__ . '/CityRelationMatching/outdooractive/city.json'),
            true
        );
        foreach (glob(__DIR__ . '/CityRelationMatching/outdooractive/tour*') as $file) {
            $data[basename($file)] = [ $cityData, json_decode(file_get_contents($file), true) ];
        }
        return $data;
    }


    /** @dataProvider provideToubizTourData */
    public function testMatchesCityRelationsForToubizTours($cityData, $tourData): void
    {
        $cityAdapter = new CityAdapter($cityData);
        $tourAdapter = new ToubizTourAdapter($tourData);
        $tourAdapter->setLogger(new NullLogger());
        $tourAdapter->setCategoryMap(new CategoryMap());

        /** @var ArticleImporter $importer */
        $importer = $this->objectManager->get(ArticleImporter::class);
        $importer->setUuidPredictionService($this->objectManager->get(UuidPredictionService::class));

        $city = $importer->import($cityAdapter);
        $tour = $importer->import($tourAdapter);

        $cityIds = $tour->getCities()
            ->map(function(Article $city) { return $city->getPersistenceObjectIdentifier(); })
            ->toArray();

        $this->assertContains($city->getPersistenceObjectIdentifier(), $cityIds);
    }

    public function provideToubizTourData(): array
    {
        $data = [];
        $cityData = json_decode(
            file_get_contents(__DIR__ . '/CityRelationMatching/toubizTour/city.json'),
            true
        );
        foreach (glob(__DIR__ . '/CityRelationMatching/toubizTour/tour*') as $file) {
            $data[basename($file)] = [ $cityData, json_decode(file_get_contents($file), true) ];
        }
        return $data;
    }
}
