<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Migration\Step\UrlRewrite;

use Migration\App\Step\RollbackInterface;
use Migration\App\Step\StageInterface;
use Migration\Reader\MapInterface;
use Migration\Step\DatabaseStage;
use Migration\Step\UrlRewrite\Model;
use Migration\Step\UrlRewrite\Model\Version11410to2000\ProductRewritesWithoutCategories;
use Migration\Step\UrlRewrite\Model\Version11410to2000\ProductRewritesIncludedIntoCategories;
use Migration\Step\UrlRewrite\Model\Version11410to2000\CategoryRewrites;
use Migration\Step\UrlRewrite\Model\Version11410to2000\CmsPageRewrites;
use Migration\Step\UrlRewrite\Model\Version11410to2000\RedirectsRewrites;
use Migration\Step\UrlRewrite\Model\VersionCommerce\TemporaryTable;
use Migration\Step\UrlRewrite\Model\VersionCommerce\TableName;

/**
 * Class Version11410to2000
 * @SuppressWarnings(PHPMD)
 */
class Version11410to2000 extends DatabaseStage implements StageInterface, RollbackInterface
{
    /**
     * @var TableName
     */
    protected $tableName;

    /**
     * @var TemporaryTable
     */
    protected $temporaryTable;

    /**
     * @var array
     */
    protected $duplicateIndex;

    /**
     * @var array
     */
    protected $resolvedDuplicates = [];

    /**
     * ResourceModel of source
     *
     * @var \Migration\ResourceModel\Source
     */
    protected $source;

    /**
     * ResourceModel of destination
     *
     * @var \Migration\ResourceModel\Destination
     */
    protected $destination;

    /**
     * Record Factory
     *
     * @var \Migration\ResourceModel\RecordFactory
     */
    protected $recordFactory;

    /**
     * Record Collection Factory
     *
     * @var \Migration\ResourceModel\Record\CollectionFactory
     */
    protected $recordCollectionFactory;

    /**
     * LogLevelProcessor instance
     *
     * @var \Migration\App\ProgressBar\LogLevelProcessor
     */
    protected $progress;

    /**
     * Logger instance
     *
     * @var \Migration\Logger\Logger
     */
    protected $logger;

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

    /**
     * @var bool
     */
    protected static $dataInitialized = false;

    /**
     * @var array
     */
    protected $suffixData;

    /**
     * @var \Migration\Step\UrlRewrite\Helper
     */
    protected $helper;

    /**
     * @var ProductRewritesIncludedIntoCategories
     */
    private $productRewritesIncludedIntoCategories;

    /**
     * @var ProductRewritesWithoutCategories
     */
    private $productRewritesWithoutCategories;

    /**
     * @var CategoryRewrites
     */
    private $categoryRewrites;

    /**
     * @var RedirectsRewrites
     */
    private $redirectsRewrites;

    /**
     * @var CmsPageRewrites
     */
    private $cmsPageRewrites;

    /**
     * @var Model\Suffix
     */
    private $suffix;

    /**
     * @var array
     */
    protected $structure = [
        MapInterface::TYPE_SOURCE => [
            'enterprise_url_rewrite' => [
                'url_rewrite_id',
                'request_path',
                'target_path',
                'is_system',
                'guid',
                'identifier',
                'inc',
                'value_id',
                'store_id',
                'entity_type'
            ],
            'catalog_category_entity_url_key' => [
                'value_id',
                'entity_type_id',
                'attribute_id',
                'store_id',
                'entity_id',
                'value'
            ],
            'catalog_product_entity_url_key' => [
                'value_id',
                'entity_type_id',
                'attribute_id',
                'store_id',
                'entity_id',
                'value'
            ],
            'enterprise_url_rewrite_redirect' => [
                'redirect_id',
                'identifier',
                'target_path',
                'options',
                'description',
                'category_id',
                'product_id',
                'store_id'
            ],
        ],
        MapInterface::TYPE_DEST => [
            'url_rewrite' => [
                'url_rewrite_id',
                'entity_type',
                'entity_id',
                'request_path',
                'target_path',
                'redirect_type',
                'store_id',
                'description',
                'is_autogenerated',
                'metadata'
            ],
            'catalog_category_entity_varchar' => [
                'value_id',
                'attribute_id',
                'store_id',
                'entity_id',
                'value',
            ],
            'catalog_product_entity_varchar' => [
                'value_id',
                'attribute_id',
                'store_id',
                'entity_id',
                'value',
            ]
        ],
    ];

    /**
     * @param \Migration\App\ProgressBar\LogLevelProcessor $progress
     * @param \Migration\Logger\Logger $logger
     * @param \Migration\Config $config
     * @param \Migration\ResourceModel\Source $source
     * @param \Migration\ResourceModel\Destination $destination
     * @param \Migration\ResourceModel\Record\CollectionFactory $recordCollectionFactory
     * @param \Migration\ResourceModel\RecordFactory $recordFactory
     * @param Helper $helper
     * @param ProductRewritesWithoutCategories $productRewritesWithoutCategories
     * @param ProductRewritesIncludedIntoCategories $productRewritesIncludedIntoCategories
     * @param CategoryRewrites $categoryRewrites
     * @param CmsPageRewrites $cmsPageRewrites
     * @param RedirectsRewrites $redirectsRewrites
     * @param Model\Suffix $suffix
     * @param TemporaryTable $temporaryTable
     * @param TableName $tableName
     * @param $stage
     * @throws \Migration\Exception
     */
    public function __construct(
        \Migration\App\ProgressBar\LogLevelProcessor $progress,
        \Migration\Logger\Logger $logger,
        \Migration\Config $config,
        \Migration\ResourceModel\Source $source,
        \Migration\ResourceModel\Destination $destination,
        \Migration\ResourceModel\Record\CollectionFactory $recordCollectionFactory,
        \Migration\ResourceModel\RecordFactory $recordFactory,
        Helper $helper,
        ProductRewritesWithoutCategories $productRewritesWithoutCategories,
        ProductRewritesIncludedIntoCategories $productRewritesIncludedIntoCategories,
        CategoryRewrites $categoryRewrites,
        CmsPageRewrites $cmsPageRewrites,
        RedirectsRewrites $redirectsRewrites,
        Model\Suffix $suffix,
        TemporaryTable $temporaryTable,
        TableName $tableName,
        $stage
    ) {
        $this->progress = $progress;
        $this->logger = $logger;
        $this->source = $source;
        $this->destination = $destination;
        $this->recordCollectionFactory = $recordCollectionFactory;
        $this->recordFactory = $recordFactory;
        $this->temporaryTable = $temporaryTable;
        $this->tableName = $tableName;
        $this->stage = $stage;
        $this->helper = $helper;
        $this->productRewritesWithoutCategories = $productRewritesWithoutCategories;
        $this->productRewritesIncludedIntoCategories = $productRewritesIncludedIntoCategories;
        $this->categoryRewrites = $categoryRewrites;
        $this->cmsPageRewrites = $cmsPageRewrites;
        $this->redirectsRewrites = $redirectsRewrites;
        $this->suffix = $suffix;
        parent::__construct($config);
    }

    /**
     * @inheritdoc
     */
    public function perform()
    {
        if (!method_exists($this, $this->stage)) {
            throw new \Migration\Exception('Invalid step configuration');
        }

        return call_user_func([$this, $this->stage]);
    }

    /**
     * Data migration
     *
     * @return bool
     * @throws \Migration\Exception
     */
    protected function data()
    {
        $this->destination->clearDocument($this->tableName->getDestinationTableName());
        $this->destination->clearDocument($this->tableName->getDestinationProductCategoryTableName());
        $this->temporaryTable->initTemporaryTable(
            $this->productRewritesWithoutCategories,
            $this->productRewritesIncludedIntoCategories,
            $this->categoryRewrites,
            $this->cmsPageRewrites,
            $this->redirectsRewrites
        );
        $this->temporaryTable->migrateRewrites();
        return true;
    }

    /**
     * @inheritdoc
     */
    protected function integrity()
    {
        $errors = false;
        $this->progress->start(0);
        foreach ($this->structure as $resourceName => $documentList) {
            $resource = $resourceName == MapInterface::TYPE_SOURCE ? $this->source : $this->destination;
            foreach ($documentList as $documentName => $documentFields) {
                $document = $resource->getDocument($documentName);
                if ($document === false) {
                    $message = sprintf('%s table does not exist: %s', ucfirst($resourceName), $documentName);
                    $this->logger->error($message);
                    $errors = true;
                    continue;
                }
                $documentFields = $this->helper->processFields($resourceName, $documentName, $documentFields);
                $structure = array_keys($document->getStructure()->getFields());
                if (!(empty(array_diff($structure, $documentFields))
                    && empty(array_diff($documentFields, $structure)))
                ) {
                    $message = sprintf(
                        '%s table structure does not meet expectation: %s',
                        ucfirst($resourceName),
                        $documentName
                    );
                    $this->logger->error($message);
                    $errors = true;
                }
            }
        }
        if (!$errors) {
            $this->progress->finish();
        }
        $this->temporaryTable->initTemporaryTable(
            $this->productRewritesWithoutCategories,
            $this->productRewritesIncludedIntoCategories,
            $this->categoryRewrites,
            $this->cmsPageRewrites,
            $this->redirectsRewrites
        );
        return !$errors && !$this->processDuplicatesList();
    }

    /**
     * Process duplicates list
     *
     * @return bool
     */
    private function processDuplicatesList()
    {
        $errors = false;
        $data = $this->temporaryTable->getDuplicatesList();
        if (!empty($data)) {
            $duplicates = [];
            foreach ($data as $row) {
                $duplicates[] = sprintf(
                    'Request path: %s Store ID: %s Target path: %s',
                    $row['request_path'],
                    $row['store_id'],
                    $row['target_path']
                );
            }

            $message = sprintf(
                'There are duplicates in URL rewrites:%s',
                PHP_EOL . implode(PHP_EOL, $duplicates)
            );

            if (!empty($this->configReader->getOption('auto_resolve_urlrewrite_duplicates'))) {
                $this->logger->addInfo($message);
            } else {
                $this->logger->error($message);
                $errors = true;
            }
        }
        return $errors;
    }

    /**
     * @inheritdoc
     */
    protected function volume()
    {
        $this->progress->start(1);

        $this->progress->advance();
        $result = $this->source->getRecordsCount($this->temporaryTable->getName())
            == $this->destination->getRecordsCount('url_rewrite');
        if (!$result) {
            $this->logger->error('Mismatch of entities in the document: url_rewrite');
        }
        $this->progress->finish();
        return $result;
    }

    /**
     * Perform rollback
     *
     * @return void
     */
    public function rollback()
    {
        $this->destination->clearDocument($this->tableName->getDestinationTableName());
        $this->destination->clearDocument($this->tableName->getDestinationProductCategoryTableName());
    }
}
