<?php

namespace FiloBlu\Flow\Model\From;

use Exception;
use FiloBlu\Flow\Block\Adminhtml\Channel\Edit\Tab\Config;
use FiloBlu\Flow\Helper\Data;
use FiloBlu\Flow\Helper\EmailNotification;
use FiloBlu\Flow\Helper\LoggerProvider;
use InvalidArgumentException;
use Magento\Catalog\Model\ProductFactory;
use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\CatalogInventory\Model\Stock as CatalogInventoryStock;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\StateException;
use Magento\Framework\Indexer\ActionFactory;
use Magento\Framework\Indexer\IndexerRegistry;
use Magento\Framework\Indexer\StateInterface;
use Magento\Framework\Model\Context;
use Magento\Framework\Mview\View\CollectionFactory;
use Magento\Framework\Registry;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\Validation\ValidationException;
use Magento\Indexer\Cron\UpdateMview;
use Magento\Inventory\Model\ResourceModel\SourceItem\SaveMultiple;
use Magento\Inventory\Model\ResourceModel\StockSourceLink\Collection;
use Magento\Inventory\Model\StockSourceLink;
use Magento\InventoryApi\Api\Data\SourceItemInterface;
use Magento\InventoryApi\Api\SourceItemsSaveInterface;
use Magento\InventoryApi\Api\StockRepositoryInterface;
use Magento\InventoryIndexer\Model\Queue\ReservationDataFactory;
use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus;
use Magento\InventoryReservationsApi\Model\ReservationInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Zend_Db;
use Zend_Db_Expr;
use Zend_Db_Statement_Exception;
use Magento\Framework\ObjectManagerInterface;
use function in_array;
use function is_array;

/**
 * @see https://devdocs.magento.com/guides/v2.3/inventory/
 * Class Stock
 * @package FiloBlu\Flow\Model\From
 * @method getMetaId()
 * @method getMetaFile()
 */
class StockAdjustment extends AbstractFrom
{
    /**
     * @var string[]
     */
    const SOURCES = ['snatt', 'default', 'magento', 've'];

    /**
     * @var string
     */
    const STOCK_CONFIG_XML_PATH = 'cataloginventory/item_options/manage_stock';

    /**
     * @var Data
     */
    protected $helperFlow;

    /**
     * @var ProductFactory
     */
    protected $productFactory;

    /**
     * @var StockRegistryInterface
     */
    protected $stockRegistry;

    /**
     * @var \FiloBlu\Flow\Helper\Stock
     */
    protected $_stock_qty;

    /**
     * @var ProductMetadataInterface
     */
    protected $_productMetadata;

    /**
     * @var EmailNotification
     */
    protected $emailNotification;

    /**
     * @var ScopeConfigInterface
     */
    protected $scopeConfig;

    /** @var */
    protected $connection;

    /**
     * @var StockConfigurationInterface
     */
    protected $stockConfiguration;

    /**
     * @var SourceItemsSaveInterface
     */
    protected $sourceItemSave;

    /**
     * @var IndexerRegistry
     */
    protected $indexerRegistry;

    /**
     * @var ActionFactory
     */
    protected $actionFactory;

    /**
     * @var ObjectManager
     */
    protected $objectManager;


    protected $sourceItems;

    /**
     * @var SearchCriteriaBuilder
     */
    protected $searchCriteriaBuilder;

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

    /**
     * @var ManagerInterface
     */
    private $eventManager;

    /**
     * Stock constructor.
     * @param Context $context
     * @param Registry $registry
     * @param Data $helper_flow
     * @param LoggerProvider $loggerProvider
     * @param ProductFactory $productFactory
     * @param StockRegistryInterface $stockRegistry
     * @param StockConfigurationInterface $stockConfiguration
     * @param \FiloBlu\Flow\Helper\Stock $stock_qty
     * @param ProductMetadataInterface $productMetadata
     * @param EmailNotification $emailNotification
     * @param ScopeConfigInterface $scopeConfig
     * @param IndexerRegistry $indexerRegistry
     * @param ActionFactory $actionFactory
     * @param CollectionFactory $mviewCollectionFactory
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param array $data
     */
    public function __construct(
        Context                     $context,
        Registry                    $registry,
        Data                        $helper_flow,
        LoggerProvider              $loggerProvider,
        ProductFactory              $productFactory,
        StockRegistryInterface      $stockRegistry,
        StockConfigurationInterface $stockConfiguration,
        \FiloBlu\Flow\Helper\Stock  $stock_qty,
        ProductMetadataInterface    $productMetadata,
        EmailNotification           $emailNotification,
        ScopeConfigInterface        $scopeConfig,
        ObjectManagerInterface      $objectManager,
        IndexerRegistry             $indexerRegistry,
        ActionFactory               $actionFactory,
        CollectionFactory           $mviewCollectionFactory,
        SearchCriteriaBuilder       $searchCriteriaBuilder,
        array                       $data = []
    )
    {
        parent::__construct($context, $registry, null, null, $data);
        $this->mviewCollectionFactory = $mviewCollectionFactory;
        $this->helperFlow = $helper_flow;
        $this->_logger = $loggerProvider->getLogger();
        $this->productFactory = $productFactory;
        $this->stockRegistry = $stockRegistry;
        $this->_stock_qty = $stock_qty;
        $this->_productMetadata = $productMetadata;
        $this->emailNotification = $emailNotification;
        $this->scopeConfig = $scopeConfig;
        $this->stockConfiguration = $stockConfiguration;
        $this->indexerRegistry = $indexerRegistry;
        $this->actionFactory = $actionFactory;
        $this->objectManager = ObjectManager::getInstance();
        $this->eventManager = $context->getEventDispatcher();
        if (interface_exists(\Magento\InventoryApi\Api\SourceItemRepositoryInterface::class)) {
            $this->sourceItems = $objectManager->get(\Magento\InventoryApi\Api\SourceItemRepositoryInterface::class);
        }
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
    }

    /**
     * @noinspection MagicMethodsValidityInspection
     * @return void
     */
    public function _construct()
    {
        $this->_init(\FiloBlu\Flow\Model\ResourceModel\From\StockAdjustment::class);
    }

    /**
     * @return $this
     * @throws Exception
     */
    public function process()
    {
        if (version_compare($this->_productMetadata->getVersion(), '2.3.0', '>=')) {
            $this->process23();
            return $this;
        }
        
        $this->process22();

        return $this;
    }

    /**
     *  Set meta_processed = 2 for products != simple
     */
    public function removeUnwantedProductTypes($file)
    {
        $select = $this->getConnection()
            ->select()
            ->joinInner(
                [
                    'cpe' => 'catalog_product_entity'
                ],
                'ffs.sku = cpe.sku',
                [
                    'meta_processed' => new Zend_Db_Expr('2'),
                    'sku'            => 'cpe.sku'
                ])
            ->where("ffs.meta_file = {$file->getId()} AND cpe.type_id NOT IN ('simple', 'giftcard','virtual')");

        $query = $this->getConnection()->updateFromSelect($select, ['ffs' => 'flow_from_stock']);
        $this->getConnection()->query($query);
    }

    /**
     * @return false|AdapterInterface
     */
    protected function getConnection()
    {
        if ($this->connection) {
            return $this->connection;
        }

        return ($this->connection = $this->getResource()->getConnection());
    }

    /**
     * @param $sku
     * @return array
     */
    public function getCurrentQuantityForSkuAndSource($sku) {
        $searchCriteria = $this->searchCriteriaBuilder
            ->addFilter('sku', $sku)
            ->create();
        $sourceItemData = $this->sourceItems->getList($searchCriteria);
        $quantityLookup = [];
        foreach ($sourceItemData->getItems() as $sourceItem){
            $quantityLookup[$sourceItem->getSourceCode()] = $sourceItem->getQuantity();
        }
        return $quantityLookup;
    }

    /**
     * @return $this
     */
    protected function process23()
    {
        $channelConfig = $this->getChannel()->getChannelConfig();
        //$processReservedStock = $channelConfig->getProcessReservedStock();
        //$orderStatuses = $channelConfig->getProcessReservedStockStatuses();
        $sku = $this->getData('sku');
        $sourceCode = $this->getData('warehouse');
        $adjustment = (int)$this->getData('adjustment');
        try {
            $quantityLookup = $this->getCurrentQuantityForSkuAndSource($sku);
            $originalQuantity = $quantity = $quantityLookup[$sourceCode];
            $sourceItem = $this->createSourceItemInterface();
            $sourceItem->setSourceCode($sourceCode);
            $sourceItem->setSku($sku);

            $quantity += $adjustment;
            //TODO: check if this need to be enabled
//            if($quantity<0) {
//                $quantity = 0;
//            }

            $sourceItem->setQuantity($quantity);

            // Safely put in out ouf stock items with stock less than 0
            // do not touch items with stock > 0 because we need to consider reservations and other stuff
            // so lets Magento take care about this.
            // Putting in stock items without proper checks can break stock.

            if ($originalQuantity <= 0) {
                $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK);
            } else {
                $sourceItem->setStatus(SourceItemInterface::STATUS_IN_STOCK);
            }

            $this->getSourceItemsSave()->execute([$sourceItem]);
            $this->_logger->info("Process STOCK - qty {$quantity} SKU: {$sku}");

            $this->getConnection()->update('flow_from_stock_adjustment', ['final_quantity' => $quantity], ['meta_id = ?' => $this->getMetaId()]);
        } catch (Exception $e) {
            $this->getConnection()->update('flow_from_stock_adjustment', ['meta_processed' => 2], ['meta_id = ?' => $this->getMetaId()]);
            $this->_logger->info('Errore nel salvataggio dello StockAdjustment. ' . $e->getMessage());
        }

        return $this;
    }

    /**
     * Create a SourceItem
     * @return SourceItemInterface
     */
    public function createSourceItemInterface()
    {
        return ObjectManager::getInstance()->create(SourceItemInterface::class);
    }

    /**
     * @return SaveMultiple
     */
    public function getSourceItemsSave()
    {
        if ($this->sourceItemSave === null) {
            $this->sourceItemSave = ObjectManager::getInstance()->create(SaveMultiple::class);
        }

        return $this->sourceItemSave;
    }


    /**
     * @param $file
     * @return mixed|void
     * @throws MailException
     * @throws NoSuchEntityException
     */
    public function sendErrorNotifications($file)
    {

        // Se sono stati rilevati errori e salvati i relativi messaggi si procede con la composizione della mail
        if (!empty($this->getEmailNotifyError())) {

            // Recupero gli indirizzi email dei destinatari
            $emailAddressString = $this->scopeConfig->getValue(self::XML_PATH_EMAIL_ADDRESS_NOTIFY_ERROR);

            if ($emailAddressString && $emailAddressString !== '') {
                $emailAddress = explode(',', $emailAddressString);
                $emailAddress = array_map('trim', $emailAddress);

                $storeName = $this->scopeConfig->getValue(self::XML_PATH_STORE_NAME);
                // Compongo il testo del messaggio con i vari errori
                $errorString = '<p>' . implode('</p><p>', $this->getEmailNotifyError()) . '</p>';

                // Array con i dati che serviranno a comporre la mail
                $data = [
                    'mail_subject' => 'Reporting breakdowns in stock loading for ' . $storeName,
                    'file_name'    => $file->getName(),
                    'errors'       => $errorString
                ];

                // Invio email di notifica
                $this->emailNotification->notify($emailAddress, $data);
            }
        }
    }

    /**
     * @param $query
     * @return bool
     */
    protected function postprocessFileRows($query)
    {
        return $this->postprocessFileRows23();
    }

    /**
     * Workaround until stock reindex will be fixed
     */
    protected function postprocessFileRows23()
    {
        if (class_exists(UpdateIndexSalabilityStatus::class)) {
            $this->updateIndexSalabilityStatus($this->getMetaFile());
        }

        //TODO: handle in appropriate helper
//        $channelConfig = $this->getChannel()->getChannelConfig();
//
//        // Remove products != simple form inventory_source_item
//
//        if ($channelConfig->getFixInventorySourceItems()) {
//            $connection = $this->getConnection();
//            $query = $connection->deleteFromSelect(
//                $connection->select()
//                    ->from(['isi' => $connection->getTableName('inventory_source_item')], ['sku'])
//                    ->joinLeft(['cpe' => $connection->getTableName('catalog_product_entity')], 'isi.sku = cpe.sku')
//                    ->where('cpe.type_id NOT IN (\'simple\', \'giftcard\',\'virtual\')'),
//                'isi'
//            );
//
//            $connection->query($query);
//        }

        return true;
    }

    /**
     * @param $metaFileId
     * @throws StateException
     */
    protected function updateIndexSalabilityStatus($metaFileId)
    {
        $connection = $this->getConnection();
        $select = $connection->select()
            ->from('flow_from_stock', ['sku', 'sku'])
            ->where("meta_file='$metaFileId' AND fisico_disponibile <> final_quantity");

        $skus = array_keys($connection->fetchPairs($select));
        $stockRepository = $this->createStockRepositoryInterface();
        $updateSaleability = $this->objectManager->get(UpdateIndexSalabilityStatus::class);
        $stockList = $stockRepository->getList();

        foreach ($stockList->getItems() as $stock) {
            $data = $this->objectManager->get(ReservationDataFactory::class)->create(
                [
                    'skus'  => $skus,
                    'stock' => $stock->getStockId()
                ]);
            $updateSaleability->execute($data);
        }
    }

    public function createStockRepositoryInterface()
    {
        return ObjectManager::getInstance()->create(StockRepositoryInterface::class);
    }

    /**
     *
     */
    protected function sendToChangelog()
    {
        // TODO: To implement to force all product even not changed
        // Insert into cataloginventory_stock_cl select entity_id from catalog_product_entity where sku IN (select sku from flow_from_stock where meta_file = ..)
        // Insert into inventory_cl select select source_item_id from inventory_source_item where sku IN (select sku from flow_from_stock where meta_file = ..)
    }

    protected function resetReindex($indexers)
    {
        foreach ($indexers as $name) {
            try {
                $indexer = $this->indexerRegistry->get($name);
                $state = $indexer->getState();
                $state->setStatus(StateInterface::STATUS_INVALID);
                $state->save();
                $indexer->setState($state);
            } catch (InvalidArgumentException $e) {
                continue;
            }
        }
    }

    /**
     * 1] $this->stockConfiguration->getBackorders($this->getStoreId()) = 0
     *      backorders = 0 && use_config_backorders = 1
     *
     * 2]  backorders = 0 && use_config_backorders = 0
     */
    protected function updateConfigurablesStockStatus()
    {
        $connection = $this->getConnection();
        $entityField = $this->getEntityField();

        $sql = $connection->select()
            ->from(['csi' => 'cataloginventory_stock_item'], [])
            ->joinLeft(['cpe' => 'catalog_product_entity'], 'cpe.entity_id = csi.product_id', [])
            ->joinLeft(['cpsl' => 'catalog_product_super_link'], 'cpe.entity_id = cpsl.product_id', ['cpsl.parent_id'])
            ->where('cpsl.parent_id is not null')
            ->group('cpsl.parent_id');

        $outOfStockSql = clone $sql;
        $inStockSql = clone $sql;

        $outOfStockSql->having('SUM(csi.qty) <= 0');
        $inStockSql->having('SUM(csi.qty) > 0');

        /* 1] Update Out Of Stock configurables */
        $products = $connection->select()
            ->from('catalog_product_entity', ['entity_id'])
            ->where('type_id = ?', ['configurable'])
            ->where("{$entityField} IN (?)", [$outOfStockSql])
            ->query()->fetchAll(Zend_Db::FETCH_NUM);

        $configurableIds = [];

        foreach ($products as $row) {
            $configurableIds[] = $row[0];
        }

        $ids = implode(',', $configurableIds);
        $connection->query("UPDATE cataloginventory_stock_item SET is_in_stock = 0 WHERE `product_id` IN ({$ids})");

        /* 2] Update In Stock configurables */
        $products = $connection->select()
            ->from('catalog_product_entity', ['entity_id'])
            ->where('type_id = ?', ['configurable'])
            ->where("{$entityField} IN (?)", [$inStockSql])
            ->query()->fetchAll(Zend_Db::FETCH_NUM);

        $configurableIds = [];

        foreach ($products as $row) {
            $configurableIds[] = $row[0];
        }

        $ids = implode(',', $configurableIds);
        $connection->query("UPDATE cataloginventory_stock_item SET is_in_stock = 1 WHERE `product_id` IN ({$ids})");
    }

    /**
     * @return string
     */
    protected function getEntityField()
    {
        $edition = $this->_productMetadata->getEdition();
        return ($edition === 'Enterprise' || $edition === 'B2B') ? 'row_id' : 'entity_id';
    }

    /**
     * @param $query
     * @param $file
     * @return mixed
     * @throws CouldNotSaveException
     * @throws InputException
     * @throws LocalizedException
     * @throws ValidationException
     * @throws Zend_Db_Statement_Exception
     */
    protected function preprocessFileRows($query, $file)
    {
        $this->removeUnwantedProductTypes($file);
        $this->discardNonExistingStock($file);
        $this->discardPreorder($file);
        $this->discardNoManageStock($file);
        $this->setNotSpecifiedSkuQtyToZero($file);
        $this->preprocessNotReservedSku($query, $file);
        return $this->preprocessFileRows23($query, $file);

    }

    /**
     * @param $file
     */
    public function discardNonExistingStock($file)
    {
        $connection = $this->getConnection();
        $ffs = $connection->getTableName('flow_from_stock');
        $cpe = $connection->getTableName('catalog_product_entity');
        $query = "UPDATE {$ffs} SET meta_processed = 2 WHERE {$ffs}.sku NOT IN (SELECT sku FROM {$cpe}) AND {$ffs}.meta_file = '{$file->getId()}'";
        $connection->query($query);
    }

    /**
     * @param $file
     * @throws LocalizedException
     * @throws Exception
     */
    private function discardPreorder($file)
    {
        /** @var \FiloBlu\Flow\Model\Channel\Config $channelConfig */
        $channelConfig = $this->getChannel()->getChannelConfig();

        if (!$channelConfig->getPreorderDiscard()) {
            return;
        }

        if (!$channelConfig->getPreorderAttributeCode()) {
            return;
        }

        $value = $channelConfig->getPreorderAttributeValue();

        $attribute = $this->helperFlow->getProductAttributeByCode($channelConfig->getPreorderAttributeCode());

        if (!$attribute) {
            return;
        }

        $linkField = $this->helperFlow->getProductLinkField();
        $attributeId = $attribute->getAttributeId();

        $select = $this->getConnection()
            ->select()
            ->joinInner(['cpe' => 'catalog_product_entity'], 'ffs.sku = cpe.sku', [
                'meta_processed' => new Zend_Db_Expr('2'), 'sku' => 'cpe.sku'
            ])
            ->joinInner(['cpa' => "catalog_product_entity_{$attribute->getBackendType()}"],
                "cpa.{$linkField} = cpe.{$linkField} AND cpa.attribute_id = {$attributeId} AND cpa.value = {$value}",
                []
            );

        $query = $this->getConnection()->updateFromSelect($select, ['ffs' => 'flow_from_stock']);

        $this->getConnection()->query($query);
    }

    /**
     * @param $file
     */
    public function discardNoManageStock($file)
    {
        $channelConfig = $this->getChannel()->getChannelConfig();
        if (!$channelConfig->getDiscardNoManagedStock()) {
            return;
        }

        $connection = $this->getConnection();
        $configManaged = $this->scopeConfig->getValue(self::STOCK_CONFIG_XML_PATH);
        $ffs = $connection->getTableName('flow_from_stock');
        $cpe = $connection->getTableName('catalog_product_entity');
        $csi = $connection->getTableName('cataloginventory_stock_item');
        $query = "SELECT
                    ffs.meta_id,
                    cpe.sku,
                    csi.manage_stock,
                    csi.use_config_manage_stock
                    FROM
                    {$cpe} cpe
                    INNER JOIN {$csi} csi ON
                    cpe.entity_id = csi.product_id
                    INNER JOIN {$ffs} ffs ON
                    ffs.sku = cpe.sku
                    WHERE ffs.meta_processed = 0 AND ffs.meta_file = '{$file->getId()}'";

        // default config manage stock a no
        if ($configManaged == '0') {
            $query .= ' AND ((csi.use_config_manage_stock = 0 AND csi.manage_stock = 0) OR csi.use_config_manage_stock = 1)';
        } else {
            $query .= ' AND (csi.use_config_manage_stock = 0 AND csi.manage_stock = 0)';
        }

        $rows = $connection->fetchAll($query);

        $ids = [];
        foreach ($rows as $row) {
            $ids[] = $row['meta_id'];
        }
        if (count($ids)) {
            $idToUpdate = implode(',', $ids);
            $updateQuery = "UPDATE {$ffs} SET meta_processed = 3 WHERE {$ffs}.meta_id IN (" . $idToUpdate . ')';
            $connection->query($updateQuery);
        }
    }

    /**
     * @param $query
     * @param $file
     * @return array|void
     * @throws CouldNotSaveException
     * @throws InputException
     * @throws ValidationException
     * @throws Zend_Db_Statement_Exception
     * @description This function will process (faster) all the SKUs that are not inside any reserved order
     */
    public function preprocessNotReservedSku($query, $file)
    {
        $channelConfig = $this->getChannel()->getChannelConfig();
        $processReservedStock = $channelConfig->getProcessReservedStock();

        if (!$processReservedStock) {
            return;
        }

        $orderStatuses = $channelConfig->getProcessReservedStockStatuses();
        $orderStatusesString = $this->implodeQuoting(',', $orderStatuses);

        $connection = $this->getConnection();

        $sql = "SELECT SOI.sku
                FROM sales_order SO
                INNER JOIN sales_order_item SOI ON SOI.order_id = SO.entity_id
                WHERE SO.status IN ({$orderStatusesString})
                GROUP BY SOI.sku";
        $skus_with_reserved_quantities = $connection->query($sql)->fetchAll(); //multidimensional array of one element
        $skus_with_reserved_quantities = array_map('current', $skus_with_reserved_quantities); //one dimension array

        // If I have some to skip
        $sku_filter = '';
        if (count($skus_with_reserved_quantities) > 0) {
            $skus_with_reserved_quantities_string = $this->implodeQuoting(',', $skus_with_reserved_quantities);
            $sku_filter = " AND sku NOT IN ({$skus_with_reserved_quantities_string})";
        }

        $selectRows = $connection->select()->from('flow_from_stock')->where(new Zend_Db_Expr("meta_processed = 0 AND meta_file = {$file->getId()} {$sku_filter}"));
        $flowFromStockRows = $connection->fetchAll($selectRows, [], Zend_Db::FETCH_ASSOC);

        $sourceItemsToSave = [];
        $counter = 0;
        $sourceItemObject = $this->createSourceItemInterface();
        $processedIds = [];
        foreach ($flowFromStockRows as $row) {
            $sourceItem = clone $sourceItemObject;
            $sourceItem->setSourceCode($row['magazzino']);
            $sourceItem->setSku($row['sku']);
            $sourceItem->setQuantity($row['fisico_disponibile']);
            $status = SourceItemInterface::STATUS_OUT_OF_STOCK;
            if ($row['fisico_disponibile'] > 0) {
                $status = SourceItemInterface::STATUS_IN_STOCK;
            }
            $sourceItem->setStatus($status);
            $sourceItemsToSave[] = $sourceItem;
            $processedIds[] = $row['meta_id'];

            if (++$counter % 1000 == 0) {
                $this->saveSourceItems($sourceItemsToSave, $processedIds);
                $processedIds = [];
                $sourceItemsToSave = [];
            }
        }

        if (empty($sourceItemsToSave)) {
            return;
        }

        $this->saveSourceItems($sourceItemsToSave, $processedIds);
    }

    /**
     * @param $file
     * @return void
     */
    public function setNotSpecifiedSkuQtyToZero($file)
    {
        $channelConfig = $this->getChannel()->getChannelConfig();
        if (!$channelConfig->getNotSpecifiedSkuQtyToZero()) {
            return;
        }

        $connection = $this->getConnection();
        $select = $connection
            ->select()
            ->joinLeft(
                [
                    'ffs' => 'flow_from_stock'
                ],
                "isi.sku = ffs.sku
                    AND  ffs.meta_file = {$file->getId()}
                    AND ffs.meta_processed != 0
                    AND source_code = ffs.magazzino",
                [
                    'quantity' => new \Zend_Db_Expr('0')
                ])
            ->where("ffs.sku IS NULL");

        $query = $connection->updateFromSelect($select, ['isi' => 'inventory_source_item']);
        $connection->query($query);

    }

    /**
     * @param $glue
     * @param $array
     * @return string
     */
    public function implodeQuoting($glue, $array)
    {
        return implode(
            $glue,
            array_map(
                static function ($i) {
                    return "'$i'";
                },
                $array
            )
        );
    }

    /**
     * @param array $sourceItemsToSave
     * @param array $flowRowIds
     * @throws CouldNotSaveException
     * @throws InputException
     * @throws ValidationException
     */
    public function saveSourceItems(array $sourceItemsToSave, array $flowRowIds)
    {
        $this->getSourceItemsSave()->execute($sourceItemsToSave);
        $ids = implode(',', $flowRowIds);
        $sql = "UPDATE flow_from_stock SET meta_processed = 1, final_quantity = fisico_disponibile, meta_process_time = NOW() WHERE meta_id IN ({$ids});";
        $this->getConnection()->query($sql);
    }

    /**
     * @param $query
     * @param $file
     * @return mixed
     */
    protected function preprocessFileRows23($query, $file)
    {
        $this->setMviewState(['inventory', 'cataloginventory_stock']);
        return $query;
    }

    /**
     * @param $indexers
     * @param $state @see \Magento\Framework\Mview\View\StateInterface
     * @return bool
     */
    protected function setMviewState($indexers, $state = \Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED)
    {
        // This has to be executed only if indexer strategy is full reset
        $channelConfig = $this->getChannel()->getChannelConfig();
        if ($channelConfig->getIndexerStrategy() !== Config::INDEXER_STRATEGY_FULL) {
            return true;
        }

        $mviews = $this->mviewCollectionFactory->create()->getItems();

        foreach ($mviews as $mview) {
            if (!in_array($mview->getId(), $indexers, false)) {
                continue;
            }

            switch ($state) {
                case \Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED:
                    $mview->suspend();
                    break;

                case \Magento\Framework\Mview\View\StateInterface::STATUS_IDLE:
                    $mview->resume();
                    break;
            }
        }

        return true;
    }

    /**
     * @return bool
     * @throws Exception
     */
    protected function process22()
    {
        $sku = $this->getData('sku');
        $productId = $this->productFactory->create()->getIdBySku($sku);
        if (!$productId) {
            return $this->error(false, "Product not found! product_code: '{$sku}'");
        }

        try {
            $stock = $this->stockRegistry->getStockItem($productId);
            $originalQty = $stock->getQty();
            $adjustment = (int)$this->getData('adjustment');
            $newQuantity = $originalQty + $adjustment;
            $quantity = $this->_stock_qty->getRealQty($productId, $newQuantity);

            if (!is_numeric($quantity)) {
                $this->addEmailNotifyError($quantity);
                $quantity = 0;
            }

            $stock->setQty($quantity);
            $this->setData('final_quantity', $quantity);
            $this->save();

            if ($stock->getBackorders() === CatalogInventoryStock::BACKORDERS_NO) {
                $quantity > $stock->getMinQty()
                    ? $stock->setIsInStock(true)
                    : $stock->setIsInStock(false);
            }

            $this->process22UpdateStock($stock);
            $this->_logger->info("Process STOCK - qty {$newQuantity} ID: {$productId} SKU: {$sku}");
        } catch (Exception $error) {
            $this->_logger->info('Error occurred while saving StockAdjustment. ' . $error->getMessage());
        }

        return true;
    }

    /**
     * @param $stockItem
     * @return void
     */
    public function process22UpdateStock($stockItem)
    {
        $connection = $this->getConnection();
        $stockData = $stockItem->getData();
        if (isset($stockData['type_id'])) {
            unset($stockData['type_id']);
        }
        if (isset($stockData['stock_status_changed_automatically_flag'])) {
            unset($stockData['stock_status_changed_automatically_flag']);
        }

        $connection->insertOnDuplicate('cataloginventory_stock_item', $stockData);
    }
}
