<?php

declare(strict_types=1);

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

namespace Amasty\LazyLoad\Model\Output;

use Amasty\LazyLoad\Model\Output\LazyConfig\LazyConfig;
use Amasty\LazyLoad\Model\Output\LazyConfig\LazyConfigFactory;
use Amasty\PageSpeedTools\Model\Image\ImagesDataByPattern;
use Amasty\PageSpeedTools\Model\Image\ImagesDataByPatternFactory;
use Amasty\PageSpeedTools\Model\Image\ReplacePatternGroupsPool;
use Amasty\PageSpeedTools\Model\Image\ReplacePatterns\ReplaceConfigInterface;
use Amasty\PageSpeedTools\Model\Image\Utils\ParsedImagesStorage;
use Amasty\PageSpeedTools\Model\Image\Utils\PathsResolver;
use Amasty\PageSpeedTools\Model\Output\OutputProcessorInterface;
use Magento\Framework\DataObject;

class LazyLoadImageParserProcessor implements OutputProcessorInterface
{
    /**
     * @var DataObject|null
     */
    private ?DataObject $lazyConfig = null;

    public function __construct(
        private readonly LazyConfigFactory $lazyConfigFactory,
        private readonly ReplacePatternGroupsPool $replacePatternsResolver,
        private readonly PathsResolver $imagePathsResolver,
        private readonly ParsedImagesStorage $parsedImagesStorage,
        private readonly ImagesDataByPatternFactory $imagesDataByPatternFactory
    ) {
    }

    public function process(string &$output): bool
    {
        if ($this->getLazyConfig()->getData(LazyConfig::IS_LAZY)) {
            $this->processImages($output);
        }

        return true;
    }

    public function processImages(string $output): void
    {
        // Fixed an issue that occurred when reaching the backtrack limit due to large HTML code size.
        $pcreBacktrackLimit = ini_get('pcre.backtrack_limit');
        $pcreBacktrackLimitForOutput =  ceil(strlen($output) / $pcreBacktrackLimit) * $pcreBacktrackLimit;
        // phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged
        ini_set('pcre.backtrack_limit', $pcreBacktrackLimitForOutput);

        $imagesData = [];
        $tempOutput = preg_replace('/<script[^>]*>(?>.*?<\/script>)/is', '', $output);
        $replacePatterns = $this->replacePatternsResolver->getByKey(
            LazyLoadProcessor::REPLACE_PATTERNS_GROUP_KEY
        );
        foreach ($replacePatterns as $replacePattern) {
            if (preg_match_all('/' . $replacePattern->getPattern() . '/is', $tempOutput, $images)) {
                $resolvedImages = $this->parseImages($replacePattern, $images);
                $imagesData[] = $this->imagesDataByPatternFactory->create()
                    ->setReplacePattern($replacePattern)
                    ->setResolvedImages($resolvedImages)
                    ->setImages($images);
            }
        }

        if ($imagesData) {
            $this->parsedImagesStorage->setImagesDataByPatterns($imagesData);
        }

        // phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged
        ini_set('pcre.backtrack_limit', $pcreBacktrackLimit);
    }

    public function setLazyConfig(DataObject $lazyConfig): void
    {
        $this->lazyConfig = $lazyConfig;
    }

    private function getLazyConfig(): DataObject
    {
        if ($this->lazyConfig === null) {
            $this->lazyConfig = $this->lazyConfigFactory->create();
        }

        return $this->lazyConfig;
    }

    /**
     * @param array $images [0 => [full_image_match_1, full_image_match_2 ...], additional data matches...]
     * @return array [key => ['image' => full_image_match, 'img_paths' => resolved_img_path], ...]
     */
    private function parseImages(
        ReplaceConfigInterface $replacePattern,
        array $images
    ): array {
        $resolvedImages = [];

        foreach ($images[0] as $key => $image) {
            $imagePaths = $this->imagePathsResolver->resolve($replacePattern, $images, $key);
            $resolvedImages[$key] = [
                ImagesDataByPattern::IMAGE_STRING_WITH_TAG => $image,
                ImagesDataByPattern::IMG_PATHS => $imagePaths
            ];
        }

        return $resolvedImages;
    }
}
