<?php

declare(strict_types=1);

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

namespace Amasty\ImageOptimizer\Model\Image;

use Amasty\ImageOptimizer\Api\Data\ImageSettingInterface;
use Amasty\ImageOptimizer\Api\ImageQueueServiceInterface;
use Amasty\ImageOptimizer\Model\Image\Directory\Reader;
use Amasty\ImageOptimizer\Model\ImageProcessor;
use Amasty\ImageOptimizer\Model\Queue\ImageQueueService;
use Amasty\ImageOptimizer\Model\Queue\Queue;
use Magento\Framework\Filesystem\Io\File;

class GenerateQueue
{
    /**
     * @var ImageQueueService
     */
    private $imageQueueService;

    /**
     * @var File
     */
    private $file;

    /**
     * @var ImageProcessor
     */
    private $imageProcessor;

    /**
     * @var Reader
     */
    private $mediaReader;

    public function __construct(
        ImageQueueServiceInterface $imageQueueService,
        File $file,
        ImageProcessor $imageProcessor,
        Reader $mediaReader
    ) {
        $this->imageQueueService = $imageQueueService;
        $this->file = $file;
        $this->imageProcessor = $imageProcessor;
        $this->mediaReader = $mediaReader;
    }

    /**
     * @deprecated Queue must be generated by single type, not multiple
     * @see GenerateQueue::generateQueueByType()
     * @param ImageSettingInterface[] $imageSettings
     * @param array $queueTypes
     *
     * @return int
     */
    public function generateQueue(array $imageSettings, array $queueTypes = [Queue::MANUAL]): int
    {
        return $this->generateQueueByType($imageSettings, reset($queueTypes));
    }

    /**
     * @deprecated Now ImageSettings could be processed manually and by cron.
     * @see GenerateQueue::process
     */
    public function processFiles(array $imageSettings): void
    {
        $this->process($imageSettings);
    }

    /**
     * @param ImageSettingInterface[] $imageSettings
     * @param string $queueType
     * @return int
     */
    public function generateQueueByType(array $imageSettings, string $queueType = Queue::MANUAL): int
    {
        return $this->process($imageSettings, $queueType);
    }

    /**
     * @param ImageSettingInterface[] $imageSettings
     * @param string $queueType
     * @return int
     */
    private function process(array $imageSettings, string $queueType = Queue::MANUAL): int
    {
        $folders = [];
        /** @var ImageSettingInterface $item */
        foreach ($imageSettings as $item) {
            foreach ($item->getFolders() as $folder) {
                $folders[$folder] = $item;
            }
        }
        [$queueFilesToDelete, $queueToAdd] = $this->processImageFolders($folders, $queueType);

        if (!empty($queueFilesToDelete)) {
            $this->imageQueueService->deleteByFilenamesAndType(
                array_unique($queueFilesToDelete),
                Queue::CRON
            );
        }

        foreach ($queueToAdd as $queue) {
            $this->imageQueueService->addToQueue($queue);
        }

        return count($queueToAdd);
    }

    private function processImageFolders(array $folders, string $queueType): array
    {
        $existingQueueFiles = $queueFilesToDelete = $queueToAdd = [];
        if (!empty($folders)) {
            $existingQueueFiles = $this->imageQueueService->getFilenames();
        }
        foreach ($folders as $imageDirectory => $imageSetting) {
            $imageDirectoryFiles = $this->mediaReader->execute($imageDirectory);

            if ($queueType === Queue::MANUAL) { // should remove files from queue if manual processing triggered
                $toDelete = array_intersect($imageDirectoryFiles, $existingQueueFiles);
                array_push($queueFilesToDelete, ...$toDelete);
            }

            foreach ($imageDirectoryFiles as $file) {
                $pathInfo = $this->file->getPathInfo($file);
                if ($pathInfo['dirname'] !== $imageDirectory && isset($imageFolders[$pathInfo['dirname']])) {
                    continue;
                }
                if ($queue = $this->imageProcessor->prepareQueue($file, $imageSetting)) {
                    $queue->setQueueType($queueType);
                    $queueToAdd[] = $queue;
                }
            }
        }

        return [$queueFilesToDelete, $queueToAdd];
    }
}
