<?php

namespace FiloBlu\Storelocator\Console\Command;

use Exception;
use FiloBlu\Flow\Helper\Data;
use FiloBlu\Flow\Model\Document;
use FiloBlu\Storelocator\Model\Images as ImageModel;
use FiloBlu\Storelocator\Model\ImagesFactory;
use FiloBlu\Storelocator\Model\ResourceModel\Images;
use FiloBlu\Storelocator\Model\ResourceModel\StoreImage;
use FiloBlu\Storelocator\Model\ResourceModel\Stores\Collection;
use FiloBlu\Storelocator\Model\ResourceModel\Stores\CollectionFactory;
use FiloBlu\Storelocator\Model\StoreImage as StoreImageModel;
use FiloBlu\Storelocator\Model\StoreImageFactory;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
use Magento\Framework\Module\Manager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;

/**
 *
 */
class BulkImageImport extends Command
{
    /**
     *
     */
    const FLOW_ID = 'inboundFlowId';

    /**
     * @var Manager
     */
    private $manager;

    /**
     * @var CollectionFactory
     */
    private $storeCollectionFactory;

    /**
     * @var ImagesFactory
     */
    private $imagesFactory;

    /**
     * @var Images
     */
    private $images;

    /**
     * @var StoreImage
     */
    private $storeImage;

    /**
     * @var StoreImageFactory
     */
    private $storeImageFactory;

    /**
     * BulkImageImport constructor.
     * @param Manager $manager
     * @param CollectionFactory $storeCollectionFactory
     * @param ImagesFactory $imagesFactory
     * @param Images $images
     * @param StoreImageFactory $storeImageFactory
     * @param StoreImage $storeImage
     * @param string|null $name
     */
    public function __construct(
        Manager $manager,
        CollectionFactory $storeCollectionFactory,
        ImagesFactory $imagesFactory,
        Images $images,
        StoreImageFactory $storeImageFactory,
        StoreImage $storeImage,
        string $name = null
    ) {
        parent::__construct($name);
        $this->manager = $manager;
        $this->storeCollectionFactory = $storeCollectionFactory;
        $this->imagesFactory = $imagesFactory;
        $this->images = $images;
        $this->storeImage = $storeImage;
        $this->storeImageFactory = $storeImageFactory;
    }

    /**
     * @inheritDoc
     */
    protected function configure()
    {
        $this->setName('filoblu:storelocator:imageimport');
        $this->setDescription(
            'Bulk store images import command. Use field store_filename_prefix to link image and store. Pass --inboundFlowId to link that imported images to store'
        );
        $this->addOption(
            self::FLOW_ID,
            null,
            InputOption::VALUE_REQUIRED,
            'Inbound Flow Id ( required )'
        );

        parent::configure();
    }

    /**
     * Execute the command
     *
     * @param InputInterface $input
     * @param OutputInterface $output
     *
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if (!$this->checkIfFlowIsInstalled()) {
            $output->writeln('<error>Missing required module: FiloBlu_Flow.</error>');
            return;
        }

        if ($flowId = $input->getOption(self::FLOW_ID)) {
            $output->writeln('<info>Provided inbound flow id is `' . $flowId . '`</info>');
        } else {
            $output->writeln('<error>Missing required argument --inboundFlowId.</error>');
            return;
        }

        $obj = ObjectManager::getInstance();
        /** @var Data $helperData */
        $helperData = $obj->create(Data::class);

        /** @var Document $model */
        $model = $helperData->getChannelByInbound($flowId);
        if ($model['type'] !== 'document') {
            $output->writeln(
                '<error>InboudFlowId=' . $flowId . ' is not valid. You should provide a Document Type Channel.</error>'
            );
            return;
        }
        if ($model['status'] !== 'processed') {
            $output->writeln(
                '<error>InboudFlowId=' . $flowId . ' is in status ' . $model['status'] . '. Please process it and try again.</error>'
            );
            return;
        }
        /** @var \FiloBlu\Flow\Model\ResourceModel\Document\Collection $documentCollection */
        $documentCollection = $obj->create(\FiloBlu\Flow\Model\ResourceModel\Document\CollectionFactory::class)->create(
        );
        $documentCollection->addFilter('meta_file', $model['id']);
        $files = $documentCollection->getItems();
        $processedFiles = 0;
        $errorFiles = 0;
        $counter = 1;
        $allFiles = \count($files);
        foreach ($files as $file) {
            try {
                if ($this->process($file, $output)) {
                    $output->writeln(
                        '<info>' . "$counter of $allFiles:" . 'File ' . $file->getName() . ' processed!</info>'
                    );
                    $counter++;
                    $processedFiles++;
                } else {
                    $output->writeln(
                        '<error>' . "$counter of $allFiles:" . 'Generic error on ' . $file->getName() . '!</error>'
                    );
                    $errorFiles++;
                    $counter++;
                }
            } catch (Exception $e) {
                $errorFiles++;
                $output->writeln(
                    '<error>' . "$counter of $allFiles:" . 'Error on ' . $file->getName() . '!' . $e->getMessage(
                    ) . '</error>'
                );
                $counter++;
            } catch (Throwable $t) {
                $errorFiles++;
                $output->writeln(
                    '<error>' . "$counter of $allFiles:" . 'Generic error on ' . $file->getName(
                    ) . '!' . $t->getMessage() . '</error>'
                );
                $counter++;
            }
        }
        $output->writeln('<comment>Overall process: </comment>');
        $output->writeln('<comment>Processed:' . $processedFiles . '</comment>');
        $output->writeln('<comment>Error(s):' . $errorFiles . '</comment>');
        $output->writeln('<comment>Totals:' . $allFiles . '</comment>');
    }

    /**
     * @return bool
     */
    private function checkIfFlowIsInstalled()
    {
        return $this->manager->isEnabled('FiloBlu_Flow') ?? false;
    }

    /**
     * @param $file
     * @param $output
     * @return bool|void
     * @throws \Magento\Framework\Exception\AlreadyExistsException
     */
    private function process($file, $output)
    {
        $filename = $file->getName();
        $destination = '/' . $filename;
        $prefix = explode('_', $filename)[0];
        $position = explode('.', (explode('_', $filename)[1]))[0];
        $store = $this->findStoreByPrefix($prefix);
        if (!$store) {
            $output->writeln(
                '<error>No store found with store_filename_prefix=' . $prefix . '.Please check db values. Import file:' . $filename . ' skipped.</error>'
            );
            return;
        }
        if (\count($store) > 1) {
            $output->writeln(
                '<error>There are more than 1 store with store_filename_prefix=' . $prefix . '.Please check db values. Import file:' . $filename . ' skipped.</error>'
            );
            return;
        }
        $storeId = reset($store)->getStoreId();

        //Save new image in filoblu_storelocator_images
        /** @var ImageModel $imageModel */
        $imageModel = $this->imagesFactory->create();
        $imageModel->setTitle($filename)->setImage($destination)->setEnabled(1);
        $newImage = $this->images->save($imageModel);

        //get image_id from the object $newImage and link it to a store
        //into filoblu_storelocator_store_image table
        $imageId = $imageModel->getImageId();
        /** @var StoreImageModel $newStoreImage */
        $newStoreImage = $this->storeImageFactory->create();
        $newStoreImage->setStoreId($storeId)->setImageId($imageId)->setPosition($position);
        $this->storeImage->save($newStoreImage);
        return true;
    }


    /**
     * @param $prefix
     * @return DataObject[]
     */
    private function findStoreByPrefix($prefix)
    {
        /** @var Collection $collection */
        $collection = $this->storeCollectionFactory->create();
        $collection->addFilter('store_filename_prefix', $prefix);
        return $collection->getItems();
    }

}
