<?php

declare(strict_types=1);

namespace Ost\ReimportDataengine\Service;

use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Media\MediaEntity;
use Shopware\Core\Content\Product\Aggregate\ProductMedia\ProductMediaEntity;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductCollection;
use Shopware\Core\Content\Seo\SeoUrl\SeoUrlCollection;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepository;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\RequestStack;

final class ProductExportFeedBuilder
{
    private const CSV_HEADERS = [
        'ordernumber',
        'category',
        'shop',
        'attr63',
        'link',
        'image_link',
        'additional_image_link_1',
        'additional_image_link_2',
        'additional_image_link_3',
        'additional_image_link_4',
    ];

    private const SHOP_CODE = 'ost';
    private const PAGE_SIZE = 100;

    /**
     * @param SalesChannelRepository<SalesChannelProductCollection> $productRepository
     * @param EntityRepository<SeoUrlCollection> $seoUrlRepository
     */
    public function __construct(
        private readonly SalesChannelRepository $productRepository,
        private readonly EntityRepository $seoUrlRepository,
        private readonly RequestStack $requestStack,
        private readonly FeedValueResolver $feedValueResolver
    ) {
    }

    /**
     * @param resource $handle
     */
    public function streamToHandle($handle, SalesChannelContext $salesChannelContext): void
    {
        fputcsv($handle, self::CSV_HEADERS);

        $offset = 0;

        while (true) {
            $criteria = $this->buildCriteria($salesChannelContext->getSalesChannelId(), $offset);
            $products = $this->productRepository->search($criteria, $salesChannelContext)->getEntities();

            if ($products->count() === 0) {
                break;
            }

            foreach ($products as $product) {
                if (!$product instanceof ProductEntity) {
                    continue;
                }

                fputcsv($handle, $this->buildRow($product, $salesChannelContext, $salesChannelContext->getContext()));
            }

            if ($products->count() < self::PAGE_SIZE) {
                break;
            }

            $offset += self::PAGE_SIZE;
        }
    }

    private function buildCriteria(string $salesChannelId, int $offset): Criteria
    {
        $criteria = new Criteria();
        $criteria->setLimit(self::PAGE_SIZE);
        $criteria->setOffset($offset);
        $criteria->addFilter(new EqualsFilter('active', true));
        $criteria->addFilter(new EqualsFilter('visibilities.salesChannelId', $salesChannelId));
        $criteria->addFilter(new RangeFilter('visibilities.visibility', [RangeFilter::GT => 0]));
        $criteria->addAssociation('categories');
        $criteria->addAssociation('media.media.thumbnails');
        $criteria->addSorting(new FieldSorting('productNumber', FieldSorting::ASCENDING));

        return $criteria;
    }

    /**
     * @return list<string>
     */
    private function buildRow(ProductEntity $product, SalesChannelContext $salesChannelContext, Context $context): array
    {
        $imageUrls = $this->resolveImageUrls($product, $salesChannelContext);

        return [
            $product->getProductNumber(),
            $this->resolveCategoryBreadcrumb($product),
            self::SHOP_CODE,
            $this->resolveAttr63($product),
            $this->resolveProductUrl($product->getId(), $salesChannelContext, $context),
            $imageUrls[0] ?? '',
            $imageUrls[1] ?? '',
            $imageUrls[2] ?? '',
            $imageUrls[3] ?? '',
            $imageUrls[4] ?? '',
        ];
    }

    private function resolveCategoryBreadcrumb(ProductEntity $product): string
    {
        $categories = $product->getCategories();
        if ($categories === null) {
            return '';
        }

        $category = $categories->first();
        if (!$category instanceof CategoryEntity) {
            return '';
        }

        $breadcrumb = $category->getBreadcrumb();
        if (!is_array($breadcrumb) || $breadcrumb === []) {
            return '';
        }

        return $this->feedValueResolver->resolveBreadcrumb($breadcrumb);
    }

    private function resolveAttr63(ProductEntity $product): string
    {
        $translated = $product->getTranslated();
        $customFields = $translated['customFields'] ?? null;
        if (is_array($customFields) && array_key_exists('attr63', $customFields)) {
            return $this->feedValueResolver->stringifyScalar($customFields['attr63']);
        }

        $productCustomFields = $product->getCustomFields();
        if (!is_array($productCustomFields)) {
            return '';
        }

        return $this->feedValueResolver->stringifyScalar($productCustomFields['attr63'] ?? '');
    }

    private function resolveProductUrl(string $productId, SalesChannelContext $salesChannelContext, Context $context): string
    {
        $criteria = new Criteria();
        $criteria->setLimit(1);
        $criteria->addFilter(new EqualsFilter('foreignKey', $productId));
        $criteria->addFilter(new EqualsFilter('routeName', 'frontend.detail.page'));
        $criteria->addFilter(new EqualsFilter('salesChannelId', $salesChannelContext->getSalesChannelId()));
        $criteria->addFilter(new EqualsFilter('isCanonical', true));

        $seoUrl = $this->seoUrlRepository->search($criteria, $context)->first();
        $baseUrl = $this->getBaseUrl($salesChannelContext);
        $scheme = $this->getScheme($salesChannelContext);

        if ($seoUrl !== null && method_exists($seoUrl, 'getSeoPathInfo')) {
            $path = $seoUrl->getSeoPathInfo();
            if (is_string($path) && $path !== '') {
                return $this->feedValueResolver->toAbsoluteUrl($path, $baseUrl, $scheme);
            }
        }

        return $this->feedValueResolver->toAbsoluteUrl('/detail/' . $productId, $baseUrl, $scheme);
    }

    /**
     * @return list<string>
     */
    private function resolveImageUrls(ProductEntity $product, SalesChannelContext $salesChannelContext): array
    {
        $productMediaCollection = $product->getMedia();
        if ($productMediaCollection === null || $productMediaCollection->count() === 0) {
            return [];
        }

        $items = array_values($productMediaCollection->getElements());
        usort($items, function (ProductMediaEntity $left, ProductMediaEntity $right): int {
            $leftName = $left->getMedia()?->getFileName() ?? '';
            $rightName = $right->getMedia()?->getFileName() ?? '';

            return $leftName <=> $rightName;
        });

        $urls = [];
        foreach ($items as $item) {
            $media = $item->getMedia();
            if (!$media instanceof MediaEntity) {
                continue;
            }

            $url = $this->resolveMediaUrl($media, $salesChannelContext);
            if ($url === '') {
                continue;
            }

            $urls[] = $url;
            if (count($urls) === 5) {
                break;
            }
        }

        return $urls;
    }

    private function resolveMediaUrl(MediaEntity $media, SalesChannelContext $salesChannelContext): string
    {
        $baseUrl = $this->getBaseUrl($salesChannelContext);
        $scheme = $this->getScheme($salesChannelContext);
        $thumbnailCollection = $media->getThumbnails();

        if ($thumbnailCollection !== null) {
            foreach ($thumbnailCollection as $thumbnail) {
                if ($thumbnail->getWidth() === 1280 && $thumbnail->getHeight() === 1280) {
                    $thumbnailUrl = $thumbnail->getUrl();
                    if (is_string($thumbnailUrl) && $thumbnailUrl !== '') {
                        return $this->feedValueResolver->toAbsoluteUrl($thumbnailUrl, $baseUrl, $scheme);
                    }
                }
            }
        }

        $mediaUrl = $media->getUrl();
        if (!is_string($mediaUrl) || $mediaUrl === '') {
            return '';
        }

        return $this->feedValueResolver->toAbsoluteUrl($mediaUrl, $baseUrl, $scheme);
    }

    private function getBaseUrl(SalesChannelContext $salesChannelContext): string
    {
        $requestBaseUrl = $this->getCurrentBaseUrl();
        if ($requestBaseUrl !== '') {
            return $requestBaseUrl;
        }

        $domains = $salesChannelContext->getSalesChannel()->getDomains();
        if ($domains === null) {
            return '';
        }

        foreach ($domains as $domain) {
            $url = $domain->getUrl();
            if (is_string($url) && $url !== '') {
                return rtrim($url, '/');
            }
        }

        return '';
    }

    private function getCurrentBaseUrl(): string
    {
        $request = $this->requestStack->getCurrentRequest();
        if ($request === null) {
            return '';
        }

        return rtrim($request->getSchemeAndHttpHost() . $request->getBasePath(), '/');
    }

    private function getScheme(SalesChannelContext $salesChannelContext): string
    {
        $requestScheme = $this->requestStack->getCurrentRequest()?->getScheme() ?? '';
        if ($requestScheme !== '') {
            return $requestScheme;
        }

        $baseUrl = $this->getBaseUrl($salesChannelContext);
        if (str_starts_with($baseUrl, 'http://')) {
            return 'http';
        }

        return 'https';
    }
}
