<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Magento\Catalog\Test\Unit\Model\Product\ProductFrontendAction;

use Magento\Catalog\Api\Data\ProductFrontendActionInterface;
use Magento\Catalog\Model\ResourceModel\ProductFrontendAction\Collection;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;

/**
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */
class SynchronizerTest extends \PHPUnit\Framework\TestCase
{
    /** @var \Magento\Catalog\Model\Product\ProductFrontendAction\Synchronizer */
    protected $model;

    /** @var ObjectManagerHelper */
    protected $objectManagerHelper;

    /** @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject */
    protected $sessionMock;

    /** @var \Magento\Customer\Model\Visitor|\PHPUnit_Framework_MockObject_MockObject */
    protected $visitorMock;

    /** @var \Magento\Catalog\Model\ProductFrontendActionFactory|\PHPUnit_Framework_MockObject_MockObject */
    protected $productFrontendActionFactoryMock;

    /** @var \Magento\Framework\EntityManager\EntityManager|\PHPUnit_Framework_MockObject_MockObject */
    protected $entityManagerMock;

    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $collectionFactoryMock;

    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $frontendStorageConfigurationPoolMock;

    protected function setUp()
    {
        $this->sessionMock = $this->getMockBuilder(\Magento\Customer\Model\Session::class)
            ->disableOriginalConstructor()
            ->getMock();
        $this->visitorMock = $this->getMockBuilder(\Magento\Customer\Model\Visitor::class)
            ->disableOriginalConstructor()
            ->getMock();
        $this->productFrontendActionFactoryMock = $this
            ->getMockBuilder(\Magento\Catalog\Model\ProductFrontendActionFactory::class)
            ->disableOriginalConstructor()
            ->setMethods(['create'])
            ->getMock();
        $this->entityManagerMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityManager::class)
            ->disableOriginalConstructor()
            ->getMock();
        $this->collectionFactoryMock = $this
            ->getMockBuilder(\Magento\Catalog\Model\ResourceModel\ProductFrontendAction\CollectionFactory::class)
            ->disableOriginalConstructor()
            ->setMethods(['create'])
            ->getMock();
        $this->frontendStorageConfigurationPoolMock = $this
            ->getMockBuilder(\Magento\Catalog\Model\FrontendStorageConfigurationPool::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->objectManagerHelper = new ObjectManagerHelper($this);
        $this->model = $this->objectManagerHelper->getObject(
            \Magento\Catalog\Model\Product\ProductFrontendAction\Synchronizer::class,
            [
                'session' => $this->sessionMock,
                'visitor' => $this->visitorMock,
                'productFrontendActionFactory' => $this->productFrontendActionFactoryMock,
                'entityManager' => $this->entityManagerMock,
                'collectionFactory' => $this->collectionFactoryMock,
                'frontendStorageConfigurationPool' => $this->frontendStorageConfigurationPoolMock
            ]
        );
    }

    /**
     * @dataProvider filterProductActionsDataProvider
     *
     * @param array $productsData
     * @param bool $correct
     * @return void
     */
    public function testFilterProductActions(array $productsData, bool $correct)
    {
        $frontendConfiguration = $this->createMock(\Magento\Catalog\Model\FrontendStorageConfigurationInterface::class);
        $frontendConfiguration->expects($this->once())
            ->method('get')
            ->willReturn([
                'lifetime' => 2,
            ]);
        $this->frontendStorageConfigurationPoolMock->expects($this->once())
            ->method('get')
            ->with('recently_compared_product')
            ->willReturn($frontendConfiguration);
        $action1 = $this->getMockBuilder(ProductFrontendActionInterface::class)
            ->disableOriginalConstructor()
            ->getMockForAbstractClass();
        $action2 = $this->getMockBuilder(ProductFrontendActionInterface::class)
            ->getMockForAbstractClass();

        $collection = $this->getMockBuilder(Collection::class)
            ->disableOriginalConstructor()
            ->getMock();
        $this->sessionMock->expects($this->any())
            ->method('getCustomerId')
            ->willReturn(1);
        $this->visitorMock->expects($this->exactly(2))
            ->method('getId')
            ->willReturn(34);
        $this->collectionFactoryMock->expects($this->once())
            ->method('create')
            ->willReturn($collection);
        $collection->expects($this->once())
            ->method('addFilterByUserIdentities')
            ->with(1, 34);

        if ($correct) {
            $frontendAction = $this->createMock(ProductFrontendActionInterface::class);
            $iterator = new \IteratorIterator(new \ArrayIterator([$frontendAction]));
            $collection->expects($this->any())
                ->method('addFieldToFilter')
                ->withConsecutive(['type_id'], ['product_id']);
            $collection->expects($this->once())
                ->method('getIterator')
                ->willReturn($iterator);
            $this->entityManagerMock->expects($this->once())
                ->method('delete')
                ->with($frontendAction);
            $this->entityManagerMock->expects($this->exactly(2))
                ->method('save')
                ->withConsecutive([$action1], [$action2]);
            $this->productFrontendActionFactoryMock->expects($this->exactly(2))
                ->method('create')
                ->withConsecutive(
                    [
                        [
                            'data' => [
                                'visitor_id' => null,
                                'customer_id' => 1,
                                'added_at' => 12,
                                'product_id' => 1,
                                'type_id' => 'recently_compared_product',
                            ],
                        ],
                    ],
                    [
                        [
                            'data' => [
                                'visitor_id' => null,
                                'customer_id' => 1,
                                'added_at' => 13,
                                'product_id' => 2,
                                'type_id' => 'recently_compared_product',
                            ],
                        ],
                    ]
                )
                ->willReturnOnConsecutiveCalls($action1, $action2);
        } else {
            $this->entityManagerMock->expects($this->never())
                ->method('delete');
            $this->entityManagerMock->expects($this->never())
                ->method('save');
        }

        $this->model->syncActions($productsData, 'recently_compared_product');
    }

    /**
     * @return array
     */
    public function filterProductActionsDataProvider(): array
    {
        return [
            [
                'productsData' => [
                    1 => [
                        'added_at' => 12,
                        'product_id' => 1,
                    ],
                    2 => [
                        'added_at' => 13,
                        'product_id' => 2,
                    ],
                    3 => [
                        'added_at' => 14,
                        'product_id' => 3,
                    ],
                ],
                'correct' => true,
            ],
            [
                'productsData' => [
                    1 => [
                        'added_at' => 12,
                        'product_id' => 'test',
                    ],
                ],
                'correct' => false,
            ],
        ];
    }
}
