<?php

namespace FiloBlu\Refilo\Remote\IndexerConfiguration;

use FiloBlu\Core\Framework\Model\AbstractSqlRepository;
use FiloBlu\Core\Framework\Model\RepositoryItem;
use FiloBlu\Refilo\Remote\Connector\ConnectorConfigurationInterface;
use FiloBlu\Refilo\Remote\Connector\ConnectorConfigurationRepositoryInterface;
use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory;
use Magento\Framework\Api\SearchResultsInterfaceFactory;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\NoSuchEntityException;
use RuntimeException;
use Zend_Db;

/**
 * Class IndexerRepository
 * @package FiloBlu\Refilo\Remote
 */
class IndexerConfigurationRepository extends AbstractSqlRepository implements IndexerConfigurationRepositoryInterface
{
    /** @var string */
    const TABLE_NAME = 'filoblu_refilo_indexer_configuration';

    /**
     * @var IndexerConfigurationInterfaceFactory
     */
    private $indexerConfigurationFactory;
    /**
     * @var ConnectorConfigurationRepositoryInterface
     */
    private $connectorConfigurationRepository;

    /**
     * IndexerConfigurationRepository constructor.
     * @param ResourceConnection $resourceConnection
     * @param ConnectorConfigurationRepositoryInterface $connectorConfigurationRepository
     * @param SearchCriteriaInterfaceFactory $searchCriteriaFactory
     * @param SearchResultsInterfaceFactory $searchResultsFactory
     * @param IndexerConfigurationInterfaceFactory $indexerConfigurationFactory
     */
    public function __construct(
        ResourceConnection $resourceConnection,
        ConnectorConfigurationRepositoryInterface $connectorConfigurationRepository,
        SearchCriteriaInterfaceFactory $searchCriteriaFactory,
        SearchResultsInterfaceFactory $searchResultsFactory,
        IndexerConfigurationInterfaceFactory $indexerConfigurationFactory
    ) {
        parent::__construct($resourceConnection, $searchCriteriaFactory, $searchResultsFactory);
        $this->indexerConfigurationFactory = $indexerConfigurationFactory;
        $this->connectorConfigurationRepository = $connectorConfigurationRepository;
    }

    /**
     * @param IndexerConfigurationInterface $indexerConfiguration
     * @return IndexerConfigurationInterface
     */
    public function save(IndexerConfigurationInterface $indexerConfiguration)
    {
        $connection = $this->resourceConnection->getConnection();
        $table = $connection->getTableName(self::TABLE_NAME);

        $indexerConfigurationId = $indexerConfiguration->getId();

        $bindings[IndexerConfigurationInterface::INDEX_NAME]    = $indexerConfiguration->getIndexName();
        $bindings[IndexerConfigurationInterface::INDEXER_ID]    = $indexerConfiguration->getIndexerId();
        $bindings[IndexerConfigurationInterface::ENABLED]       = $indexerConfiguration->getEnabled();
        $bindings[IndexerConfigurationInterface::ALLOW_DELETE]  = $indexerConfiguration->getAllowDelete();
        $bindings[IndexerConfigurationInterface::DELETE_ORDER]  = $indexerConfiguration->getDeleteOrder();
        $bindings[IndexerConfigurationInterface::ENABLED_MEMORY_OPTIMIZATION] = $indexerConfiguration->getEnabledMemoryOptimization();

        $bindings[IndexerConfigurationInterface::CONNECTOR_CONFIGURATION] = null;

        if ($indexerConfiguration->hasConnectorConfiguration()) {
            $bindings[IndexerConfigurationInterface::CONNECTOR_CONFIGURATION] = $indexerConfiguration->getConnectorConfiguration()->getId();
        }

        if ($indexerConfigurationId) {
            $connection->update($table, $bindings, $connection->quoteInto(sprintf('%s = ?', IndexerConfigurationInterface::ID), $indexerConfigurationId));
        } else {
            $connection->insert($table, $bindings);
            $indexerConfiguration->setId($connection->lastInsertId($table));
        }

        return $indexerConfiguration;
    }

    /**
     * @param int $id
     */
    public function deleteById($id)
    {
        throw new RuntimeException();
    }

    /**
     * @return string
     */
    public function getIdFieldName(): string
    {
        return IndexerConfigurationInterface::ID;
    }

    /**
     * @param string $indexerId
     * @return RepositoryItem
     * @throws NoSuchEntityException
     */
    public function getByIndexerId($indexerId): RepositoryItem
    {
        $connection = $this->resourceConnection->getConnection();
        $table = $connection->getTableName($this->getTable());

        $select = $connection
            ->select()
            ->from($table)
            ->where(sprintf('%s = :%s', IndexerConfiguration::INDEXER_ID, IndexerConfiguration::INDEXER_ID));

        $item = $connection->fetchRow($select, [IndexerConfiguration::INDEXER_ID => $indexerId], Zend_Db::FETCH_ASSOC);

        if (($item === null) || empty($item)) {
            throw new NoSuchEntityException(__("Could not find item with id = {$indexerId}"));
        }

        return $this->fromRow($item);
    }

    /**
     * @return string
     */
    public function getTable(): string
    {
        return self::TABLE_NAME;
    }

    /**
     * @param array $row
     * @return IndexerConfigurationInterface
     */
    public function fromRow(array $row)
    {
        /** @var IndexerConfigurationInterface $indexerConfiguration */
        $indexerConfiguration = $this->indexerConfigurationFactory->create();

        $indexerConfiguration
            ->setEnabled((bool)$row[IndexerConfigurationInterface::ENABLED])
            ->setIndexerId($row[IndexerConfigurationInterface::INDEXER_ID])
            ->setDeleteOder((int)$row[IndexerConfigurationInterface::DELETE_ORDER])
            ->setAllowDelete((bool)$row[IndexerConfigurationInterface::ALLOW_DELETE])
            ->setEnabledMemoryOptimization((bool)$row[IndexerConfigurationInterface::ENABLED_MEMORY_OPTIMIZATION])
            ->setId($row[IndexerConfigurationInterface::ID]);

        if (isset($row[IndexerConfigurationInterface::INDEX_NAME]) && $row[IndexerConfigurationInterface::INDEX_NAME] !== null) {
            $indexerConfiguration->setIndexName($row[IndexerConfigurationInterface::INDEX_NAME]);
        }

        if ($row[IndexerConfigurationInterface::CONNECTOR_CONFIGURATION]) {
            try {
                /** @var ConnectorConfigurationInterface $connectorConfiguration */
                $connectorConfiguration = $this->connectorConfigurationRepository
                    ->getById($row[IndexerConfigurationInterface::CONNECTOR_CONFIGURATION]);
                $indexerConfiguration->setConnectorConfiguration($connectorConfiguration);
            } catch (NoSuchEntityException $exception) {
                $indexerConfiguration->setConnectorConfiguration(null);
            }
        }
        return $indexerConfiguration;
    }
}
