<?php

declare(strict_types=1);

/**
 * @author Amasty Team
 * @copyright Copyright (c) Amasty (https://www.amasty.com)
 * @package Product Feed for Magento 2
 */

namespace Amasty\Feed\Model\Export\RowCustomizer;

use Amasty\Base\Model\Serializer;
use Amasty\Feed\Model\Config;
use Amasty\Feed\Model\Config\Source\Path;
use Amasty\Feed\Model\Export\Product as ExportProduct;
use Amasty\Feed\Model\Export\Product\Attributes\FeedAttributesStorage;
use Magento\CatalogImportExport\Model\Export\RowCustomizerInterface;
use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
use Magento\Framework\Url as FrontendUrl;
use Magento\Store\Model\StoreManagerInterface;

class Url implements RowCustomizerInterface
{
    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var array
     */
    private $urlRewrites;

    /**
     * @var FrontendUrl
     */
    private $url;

    /**
     * @var int
     */
    private $storeId;

    /**
     * @var array
     */
    private $rowCategories;

    /**
     * @var ExportProduct
     */
    private $export;

    /**
     * @var Serializer
     */
    private $serializer;

    /**
     * @var Config
     */
    private $configProvider;

    public function __construct(
        StoreManagerInterface $storeManager,
        ExportProduct $export,
        FrontendUrl $url, //always get frontend url
        Serializer $serializer,
        Config $configProvider
    ) {
        $this->url = $url;
        $this->export = $export;
        $this->storeManager = $storeManager;
        $this->serializer = $serializer;
        $this->configProvider = $configProvider;
    }

    public function prepareData($collection, $productIds)
    {
        if ($this->export->getAttributesStorage()->hasAttributes(FeedAttributesStorage::PREFIX_URL_ATTRIBUTE)) {
            $this->storeId = $collection->getStoreId();
            $select = $collection->getConnection()
                ->select()
                ->from(['u' => $collection->getTable('url_rewrite')], ['u.entity_id', 'u.request_path', 'u.metadata'])
                ->where('u.is_autogenerated = 1')
                ->where('u.entity_type = ?', ProductUrlRewriteGenerator::ENTITY_TYPE)
                ->where('u.entity_id IN(?)', $productIds);
            if (!$this->storeManager->isSingleStoreMode()) {
                $select->where('u.store_id = ?', $this->storeId);
            }

            foreach ($collection->getConnection()->fetchAll($select) as $row) {
                $metadata = $this->serializer->unserialize($row['metadata']);
                $categoryId = is_array($metadata) && isset($metadata['category_id'])
                    ? $metadata['category_id']
                    : null;
                if (!isset($row['entity_id'])) {
                    $this->urlRewrites[$row['entity_id']] = [];
                }
                $this->urlRewrites[$row['entity_id']][(int)$categoryId] = $row['request_path'];
            }
            $multiRowData = $this->export->getMultirawData();
            $this->rowCategories = $multiRowData[ExportProduct::ROW_CATEGORIES_KEY];
        }

        return $this;
    }

    public function addData($dataRow, $productId)
    {
        $customData = &$dataRow[Composite::CUSTOM_DATA_KEY];
        //if the production mode + config cache is enabled.
        //Store manager returns current website url, instead of one have been set.
        //taking url directly from URL model.
        $this->url->setScope($this->storeManager->getStore($this->storeId));
        if ($this->urlRewrites && isset($this->urlRewrites[$productId])) {
            $customData[FeedAttributesStorage::PREFIX_URL_ATTRIBUTE] = $this->processUrlRewrite((int)$productId);
        } else {
            $customData[FeedAttributesStorage::PREFIX_URL_ATTRIBUTE] = $this->processUrl((int)$productId);
        }

        $customData[FeedAttributesStorage::PREFIX_URL_ATTRIBUTE]['configurable'] = $this->getConfigurableUrl(
            $customData,
            $dataRow['sku']
        );

        return $dataRow;
    }

    public function addHeaderColumns($columns)
    {
        return $columns;
    }

    public function getAdditionalRowsCount($additionalRowsCount, $productId)
    {
        return $additionalRowsCount;
    }

    private function processUrl(int $productId): array
    {
        $lastCategoryId = $dataRow[Composite::CUSTOM_DATA_KEY][FeedAttributesStorage::PREFIX_CATEGORY_ID_ATTRIBUTE]
            ?? null;
        $routeParamsShort = [
            '_nosid' => true,
            '_query' => array_merge($this->export->getFeedProfile()->getUtmParams(), ['___store' => null]),
            '_scope_to_url' => true,
            'id' => $productId,
            's' => $dataRow['url_key'] ?? '',
            '_scope' => $this->url->getScope(),
        ];
        $routeParamsWithCategory = array_merge($routeParamsShort, ['category' => $lastCategoryId]);

        return [
            'short' => $this->url->getUrl('catalog/product/view', $routeParamsShort),
            'with_category' => $this->url->getUrl('catalog/product/view', $routeParamsWithCategory)
        ];
    }

    private function processUrlRewrite(int $productId): array
    {
        $urlRewrites = $this->urlRewrites[$productId];
        $urlWithCategory = $this->getUrlWithCategory($urlRewrites, (int)$productId);
        $routeParamsShort = [
            '_direct' => $urlRewrites[0] ?? end($urlRewrites),
            '_nosid' => true,
            '_query' => array_merge($this->export->getFeedProfile()->getUtmParams(), ['___store' => null]),
            '_scope_to_url' => true, //as in  \Magento\Store\Model\Store::getUrl()
            '_scope' => $this->url->getScope(),
        ];
        $routeParamsWithCategory = [
            '_direct' => $urlWithCategory,
            '_nosid' => true,
            '_query' => array_merge($this->export->getFeedProfile()->getUtmParams(), ['___store' => null]),
            '_scope_to_url' => true, //as in  \Magento\Store\Model\Store::getUrl()
            '_scope' => $this->url->getScope(),
        ];

        return [
            'short' => $this->url->getUrl('', $routeParamsShort),
            'with_category' => $this->url->getUrl('', $routeParamsWithCategory),
        ];
    }

    private function getConfigurableUrl(array $customData, string $sku): string
    {
        $parentConfiguration = $customData[Relation::CUSTOM_DATA_KEY]['configurable_variations'][$sku] ?? [];
        $configurableUrl = $customData[Relation::CUSTOM_DATA_KEY][Composite::CUSTOM_DATA_KEY]['url']['short'] ?? '';
        if ($parentConfiguration && $configurableUrl) {
            $config = [];
            foreach ($parentConfiguration as $attrId => $attrValueId) {
                $config[] = $attrId . '=' . $attrValueId;
            }
            if ($config) {
                $configurableUrl = rtrim($configurableUrl, '/') . '#' . implode('&', $config);
            }
        } else {
            $configurableUrl = $customData[FeedAttributesStorage::PREFIX_URL_ATTRIBUTE]['short'];
        }

        return $configurableUrl;
    }

    private function getUrlWithCategory(array $urlRewrites, int $productId)
    {
        $pathMode = $this->configProvider->getCategoryPath();
        if ($pathMode !== Path::USE_DEFAULT && count($urlRewrites) > 1) {
            $categoryRewrites = array_slice($urlRewrites, 1);
            if ($pathMode === Path::USE_SHORTEST) {
                uasort(
                    $categoryRewrites,
                    static function (string $a, string $b) {
                        return strlen($a) > strlen($b) ? 1 : -1;
                    }
                );
            } else {
                uasort(
                    $categoryRewrites,
                    static function (string $a, string $b) {
                        return strlen($a) < strlen($b) ? 1 : -1;
                    }
                );
            }
            $urlWithCategory = reset($categoryRewrites);
        } else {
            $categories = $this->rowCategories[$productId] ?? [];
            $lastCategoryId = count($categories) > 0 ? end($categories) : null;
            $urlWithCategory = $urlRewrites[$lastCategoryId] ?? end($urlRewrites);
        }

        return $urlWithCategory;
    }
}
