<?php
declare(strict_types=1);

namespace Autogedal\Manufacturers\Block\Widget;

use Amasty\ShopbySeo\Model\SeoOptions;
use Autogedal\Manufacturers\Model\Config;
use Magento\Catalog\Helper\Image as ImageHelper;
use Magento\Catalog\Model\Category;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\Product\Visibility as ProductVisibility;
use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Eav\Model\Config as EavConfig;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory as OptionCollectionFactory;
use Magento\Framework\DataObject;
use Magento\Framework\DB\Select;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use Magento\Widget\Block\BlockInterface;

class TopCategories extends Template implements BlockInterface
{
    private const CATEGORY_IMAGE_ID = 'category_page_grid';
    private const CATEGORY_IMAGE_SIZE = 270;
    private const DEFAULT_LIMIT = 5;

    protected $_template = 'Autogedal_Manufacturers::widget/top_categories.phtml';

    private array $categoryBestsellerIds = [];
    private ?array $categoryImageUrls = null;
    private ?DataObject $manufacturerOption = null;

    public function __construct(
        Context $context,
        private readonly CategoryCollectionFactory $categoryCollectionFactory,
        private readonly ProductCollectionFactory $productCollectionFactory,
        private readonly OptionCollectionFactory $optionCollectionFactory,
        private readonly Config $config,
        private readonly EavConfig $eavConfig,
        private readonly SeoOptions $seoOptions,
        private readonly ProductVisibility $productVisibility,
        private readonly ImageHelper $imageHelper,
        array $data = []
    ) {
        parent::__construct($context, $data);
    }

    public function getTopCategories(): CategoryCollection|array
    {
        $this->categoryBestsellerIds = $this->fetchCategoryBestsellerIds();
        $categoryIds = array_keys($this->categoryBestsellerIds);
        if (!$categoryIds) {
            return [];
        }

        $collection = $this->categoryCollectionFactory->create();
        $collection->addAttributeToSelect(['name', 'url_path'])
            ->addAttributeToFilter('entity_id', ['in' => $categoryIds])
            ->addIsActiveFilter();
        $collection->getSelect()->order(
            'FIELD(e.entity_id, ' . implode(',', array_map('intval', $categoryIds)) . ')'
        );

        return $collection;
    }

    public function getAttributeValueLabel()
    {
        return $this->getManufacturerOption()->getValue();
    }

    public function getCategoryUrl(Category $category): string
    {
        $slug = $this->getSeoSlug() ?: strtolower(str_replace(' ', '-', $this->getManufacturerOption()->getValue()));
        $baseUrl = rtrim($this->_urlBuilder->getBaseUrl(), '/');
        $categoryPath = str_replace($baseUrl, '', $category->getUrl());

        return $baseUrl . str_replace('.html', '/', $categoryPath) . 'manufacturer-' . $slug . '.html';
    }

    public function getCategoryImageUrl(Category $category): string
    {
        $this->categoryImageUrls ??= $this->loadCategoryImageUrls();

        return $this->categoryImageUrls[$category->getId()] ?? '';
    }

    public function getCategoryImageSize(): int
    {
        return self::CATEGORY_IMAGE_SIZE;
    }

    private function fetchCategoryBestsellerIds(): array
    {
        $store = $this->_storeManager->getStore();
        $storeId = (int)$store->getId();
        $rootCategoryId = $store->getRootCategoryId();

        $productCollection = $this->productCollectionFactory->create();
        $productCollection->addStoreFilter($storeId)
            ->setVisibility($this->productVisibility->getVisibleInCatalogIds())
            ->addAttributeToFilter('status', Status::STATUS_ENABLED)
            ->addAttributeToFilter($this->config->getProductAttributeId(), $this->getData('attribute_value'));

        $connection = $productCollection->getConnection();
        $soldQty = $connection->select()
            ->from(
                ['bestsellers' => $productCollection->getTable('sales_bestsellers_aggregated_yearly')],
                ['SUM(bestsellers.qty_ordered)']
            )
            ->where('bestsellers.product_id = cp.product_id')
            ->where('bestsellers.store_id = ?', $storeId);

        $productCollection->getSelect()
            ->reset(Select::COLUMNS)
            ->join(
                ['cp' => $productCollection->getTable('catalog_category_product')],
                'e.entity_id = cp.product_id',
                [
                    'category_id' => 'cp.category_id',
                    'product_id' => 'cp.product_id',
                    'product_count' => new \Zend_Db_Expr('COUNT(*) OVER (PARTITION BY cp.category_id)'),
                    'sales_position' => new \Zend_Db_Expr(
                        'ROW_NUMBER() OVER (PARTITION BY cp.category_id'
                        . ' ORDER BY (' . $soldQty . ') DESC, cp.product_id DESC)'
                    )
                ]
            )
            ->join(
                ['ce' => $productCollection->getTable('catalog_category_entity')],
                'cp.category_id = ce.entity_id',
                []
            )
            ->where('cp.category_id != ?', $rootCategoryId)
            ->where('ce.parent_id = ?', $rootCategoryId);

        $bestsellers = $connection->select()
            ->from(['ranked' => $productCollection->getSelect()], ['category_id', 'product_id'])
            ->where('ranked.sales_position = 1')
            ->order(['ranked.product_count DESC', 'ranked.category_id ASC'])
            ->limit((int)$this->getData('limit') ?: self::DEFAULT_LIMIT);

        return $connection->fetchPairs($bestsellers);
    }

    private function loadCategoryImageUrls(): array
    {
        $products = $this->productCollectionFactory->create()
            ->addIdFilter($this->categoryBestsellerIds)
            ->addAttributeToSelect('small_image');

        $imageUrls = [];
        foreach ($this->categoryBestsellerIds as $categoryId => $productId) {
            $product = $products->getItemById($productId);
            if ($product) {
                $imageUrls[$categoryId] = $this->imageHelper
                    ->init($product, self::CATEGORY_IMAGE_ID)
                    ->keepFrame(true)
                    ->resize(self::CATEGORY_IMAGE_SIZE, self::CATEGORY_IMAGE_SIZE)
                    ->getUrl();
            }
        }

        return $imageUrls;
    }

    private function getManufacturerOption(): DataObject
    {
        return $this->manufacturerOption ??= $this->optionCollectionFactory->create()
            ->setAttributeFilter($this->config->getProductAttributeId())
            ->setStoreFilter()
            ->addFieldToFilter('main_table.option_id', $this->getData('attribute_value'))
            ->getFirstItem();
    }

    private function getSeoSlug()
    {
        try {
            $attributeCode = $this->eavConfig
                ->getAttribute(Product::ENTITY, $this->config->getProductAttributeId())
                ->getAttributeCode();

            return $this->seoOptions->getData()[$attributeCode][$this->getData('attribute_value')] ?? null;
        } catch (\Exception $e) {
            $this->_logger->error('Error getting SEO slug for TopCategories: ' . $e->getMessage());
        }

        return null;
    }
}
