<?php

namespace FiloBlu\Flow\Model\Channel\In;

use Exception;
use FiloBlu\Flow\Api\DocumentInterfaceFactory;
use FiloBlu\Flow\Helper\LoggerProvider;
use FiloBlu\Flow\Model\Channel\ConfigFactory;
use Magento\App\Dir;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Filesystem\DirectoryList;
use Magento\Framework\Model\ResourceModel\IteratorFactory;
use Magento\Framework\ObjectManagerInterface;
use Zend_Db;
use Zend_Db_Statement_Exception;
use Magento\Catalog\Model\Product\Action;
use Magento\Catalog\Model\ProductFactory;

/**
 * Class Document
 * @package FiloBlu\Flow\Model\Channel\In
 */
class Document extends AbstractModel
{

    /**
     * @var string
     */
    const STATUS_FOUND = 'found';

    /**
     * @var string
     */
    const STATUS_RECEIVED = 'received';

    /**
     * @var string
     */
    const STATUS_PARSED = 'parsed';

    /**
     * @var string
     */
    const STATUS_PROCESSED = 'processed';

    /**
     * @var string
     */
    const STATUS_ERROR = 'error';

    /**
     * @var string
     */
    const PRIORITY_HIGH = 'high';

    /**
     * @var string
     */
    const PRIORITY_LOW = 'low';
    /**
     * @var string
     */
    const PRIORITY_NORMAL = 'normal';
    /**
     * string
     */
    const DOCUMENT_FOLDER = 'documents';
    /**
     * @var int
     */
    protected $errorsProcessingDocuments = 0;
    /**
     * @var int
     */
    protected $documentToProcess = 0;
    /**
     * @var DocumentInterfaceFactory
     */
    protected $document;
    /**
     * @var DirectoryList
     */
    protected $dir;
    /**
     * @var IteratorFactory
     */
    protected $iteratorFactory;
    /**
     * @var
     */
    protected $channelData;
    /**
     * @var
     */
    protected $_connection;
    /**
     * @var ResourceConnection
     */
    private $_resourceConnection;
    /**
     * @var mixed
     */
    protected $_productAction;
    /**
     * @var ProductFactory
     */
    private $productFactory;

    /**
     * Document constructor.
     * @param LoggerProvider $loggerProvider
     * @param ConfigFactory $channelConfigFactory
     * @param ObjectManagerInterface $objectManager
     * @param DocumentInterfaceFactory $document
     * @param ResourceConnection $resourceConnection
     * @param DirectoryList $dir
     * @param IteratorFactory $iteratorFactory
     */
    public function __construct(
        LoggerProvider $loggerProvider,
        ConfigFactory $channelConfigFactory,
        ObjectManagerInterface $objectManager,
        DocumentInterfaceFactory $document,
        ResourceConnection $resourceConnection,
        DirectoryList $dir,
        IteratorFactory $iteratorFactory,
        ProductFactory $productFactory
    ) {
        parent::__construct($loggerProvider, $channelConfigFactory, $objectManager);
        $this->document = $document;
        $this->dir = $dir;
        $this->iteratorFactory = $iteratorFactory;
        $this->_resourceConnection = $resourceConnection;
        $this->_productAction = ObjectManager::getInstance()->get(Action::class);
        $this->productFactory = $productFactory;
    }

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

        return ($this->_connection = $this->_resourceConnection->getConnection());
    }

    /**
     * @param $file
     * @return bool|mixed
     * @throws Exception
     */
    public function receiveFile($file)
    {
        if (!$this->_connector) {
            throw new Exception('no connector joined to channel');
        }

        $meta_file_id = $file->getId();

        $local_dir = $this->_connector->getLocalDir() . DIRECTORY_SEPARATOR . self::DOCUMENT_FOLDER . DIRECTORY_SEPARATOR . $meta_file_id;

        $this->_connector->setLocalDir($local_dir);

        $model = $this->document->create();

        $collection = $model->getCollection();
        $collection->addFieldToFilter('meta_processed', 0);
        $collection->addFieldToFilter('meta_file', $meta_file_id);
        $collection->addFieldToFilter('status', 'found');
        $collection->load();

        $result = true;

        foreach ($collection as $document) {
            try {
                $received = $this->_connector->receiveResource($document->getData('name'), null, FTP_BINARY, true, false, true);
                if ($received) {
                    $document->setData('status', 'received');
                    if ($document->getData('name') !== $received) {
                        $log = $document->getData('log');
                        $log = trim("Document name fixed from '" . $document->getData('name') . "' to '{$received}'" . $log . "\n");
                        $document->setData('log', $log);
                        $document->setData('name', $received);
                    }
                    $document->save();
                } else {
                    $document->setData('status', 'found');
                    $log = $document->getData('log');
                    $log .= "Document not received\n";
                    $document->setData('log', $log);
                    $document->save();
                    $result = false;
                }
            } catch (Exception $e) {
                $log = $document->getData('log');
                $log .= $e->getMessage() . "\n";
                $document->setData('log', $log);
                $document->save();
                $document = false;
                //$this->_logger->log('FTP',$e->getMessage());
            }
        }

        return $result;
    }

    /**
     * @param $file
     * @return bool
     * @throws Exception
     */
    public function parse($file)
    {
        if (!$this->_connector) {
            throw new Exception('no connector joined to channel');
        }

        $attach_document = false;
        if($this->_config['enable_attach_document']) {
            $attach_document = true;
            $separator = $this->_config['document_filename_separator'];
        }

        $meta_file_id = $file->getId();
        $model = $this->document->create();
        $collection = $model->getCollection();
        $collection->addFieldToFilter('meta_processed', 0);
        $collection->addFieldToFilter('meta_file', $meta_file_id);
        $collection->addFieldToFilter('status', 'received');
        $collection->load();

        foreach ($collection as $document) {
            if($attach_document) {
                $filename_info = pathinfo($document->getName());
                $filename_part = explode($separator, $filename_info['filename']);
                $identifier_position = (int)$this->_config['entity_identifier_position'] - 1;
                $identifier_value = $filename_part[$identifier_position];
                $document->setEntityIdentifierValue($identifier_value);

                if($this->_config['document_attribute_by_store']) {
                    $store_position = $this->_config['document_store_position'];
                    if(strpos($store_position, ',') !== false) {
                        $store_position = explode(',', $store_position);
                        $store_ids = [];
                        foreach ($store_position as $position) {
                            $array_position = $position - 1;
                            $store_ids[] = $filename_part[$array_position];
                        }
                        if(count($store_ids) > 0) {
                            $store_id = implode(',', $store_ids);
                        }
                    }
                    else {
                        $store_position = $store_position - 1;
                        $store_id = $filename_part[$store_position];
                    }

                    $document->setEntityStoreId($store_id);
                }
            }

            $document->setStatus('parsed');
            $document->save();
        }

        return true;
    }

    /**
     * @param $file
     * @param $channel
     * @return bool
     */
    public function insertData($file)
    {
        $meta_file_id = $file->getId();

        /** @var Document $model */
        $model = $this->document->create();

        $collection = $model->getCollection();
        $collection->addFieldToFilter('meta_file', $meta_file_id);
        $collection->addFieldToFilter('status', ['in' => [self::STATUS_PARSED, self::STATUS_ERROR]]);
        $collection->load();

        $this->documentToProcess = $collection->count();

        /** @var Iterator $iterator */
        $iterator = $this->iteratorFactory->create();
        $iterator->walk($collection->getSelect(), [[$this, 'documentProcess']], ['meta_file_id' => $meta_file_id]);

        if ($this->errorsProcessingDocuments > 0) {
            return false;
        }

        return true;
    }

    /**
     * @param $args
     * @return bool
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    public function documentProcess($args)
    {
        $mediaDirectory = $this->dir->getPath('media');
        $varDirectory = $this->dir->getPath('var');

        $connector = $this->getChannelData()->getConnector();

        $file = $args['row']['name'];
        $destination = $args['row']['destination'];
        $row_id = $args['row']['id'];
        $source = $connector->getLocalDir();
        $meta_file_id = $args['meta_file_id'];
        $sourceFilePath = $source . DIRECTORY_SEPARATOR . self::DOCUMENT_FOLDER . DIRECTORY_SEPARATOR . $meta_file_id . DIRECTORY_SEPARATOR . $file;
        $destinationFilePath = $mediaDirectory . DIRECTORY_SEPARATOR . $destination . DIRECTORY_SEPARATOR . $file;

        if (file_exists($sourceFilePath)) {
            if (false === copy($sourceFilePath, $destinationFilePath)) {
                $this->setProcecessedStatus($row_id, self::STATUS_ERROR, "Unable to move {$sourceFilePath} -> {$destinationFilePath}");
                $this->errorsProcessingDocuments++;
            }
            else {
                if($this->getChannelData()->_config['enable_attach_document']) {
                    try {
                        $this->attachDocToEntity($args);
                        $this->setProcecessedStatus($row_id, self::STATUS_PROCESSED);
                    }
                    catch(Exception $e) {
                        $this->setProcecessedStatus($row_id, self::STATUS_ERROR, $e->getMessage());
                        $this->errorsProcessingDocuments++;
                    }
                }
            }
        } else {
            $this->setProcecessedStatus($row_id, self::STATUS_ERROR, "{$sourceFilePath} Does not exists");
            $this->errorsProcessingDocuments++;
        }

        return true;
    }

    /**
     * @param $document
     * @param string $status
     * @param string $reason
     */
    protected function setProcecessedStatus($row_id, $status = self::STATUS_PROCESSED, $reason = null)
    {
        $connection = $this->getConnection();
        $connection->update(
            $connection->getTableName('flow_document'),
            [
                'meta_processed' => 1,
                'status' => $status,
                'log' => $reason
            ],
            $connection->quoteInto('id = ?', $row_id)
        );
    }

    /**
     * @param $channel
     */
    public function setChannelData($channel){
        $this->channelData = $channel;
    }

    /**
     * @return mixed
     */
    public function getChannelData() {
        return $this->channelData;
    }

    /**
     * @return mixed
     */
    public function getDocumentToProcessCount(){
        return $this->documentToProcess;
    }

    /**
     * @return int
     */
    public function getProcessedDocumentErrorCount(){
        return $this->errorsProcessingDocuments;
    }

    public function attachDocToEntity($docData) {
        $attach_entity = $this->getChannelData()->_config['entity_attach_document'];
        if($attach_entity) {
            $entity_identifier = $this->getChannelData()->_config['document_entity_identifier'];
            if($attach_entity == 'product') {
                $identifier_value = $docData['row']['entity_identifier_value'];
                $prod_id = $identifier_value;
                if($entity_identifier != 'entity_id') {
                    if($entity_identifier == 'sku') {
                        $prod_id = $this->productFactory->create()->getIdBySku($identifier_value);
                    }
                    else {
                        $prod = $this->productFactory->create()->loadByAttribute($entity_identifier, $identifier_value);
                        if($prod) {
                            $prod_id = $prod->getId();
                        }
                    }
                }

                if (empty($prod_id)) {
                    throw new Exception(sprintf('Il prodotto con %s %s non esiste!', $entity_identifier, $identifier_value));
                }

                $docAttrCode = $this->getChannelData()->_config['attr_code_for_document'];
                $doc_name = $docData['row']['name'];
                $pids[] = $prod_id;
                if($this->getChannelData()->_config['document_attribute_by_store']) {
                    $store_id = $docData['row']['entity_store_id'];
                    if(strpos($store_ids, ',') !== false) {
                        $store_ids = explode(',', $store_ids);
                        foreach($store_ids as $store_id) {
                            $this->_productAction->updateAttributes($pids, [$docAttrCode => $doc_name], $store_id);
                        }
                    }
                    else {
                        $this->_productAction->updateAttributes($pids, [$docAttrCode => $doc_name], $store_id);
                    }
                }
                else {
                    $this->_productAction->updateAttributes($pids, [$docAttrCode => $doc_name], 0);
                }
            }
        }
    }
}
