<?php
declare(strict_types=1);

namespace FiloBlu\Refilo\Remote\Entity\Provider;

use FiloBlu\Refilo\Remote\Entity\EntityInterface;
use FiloBlu\Refilo\Remote\Entity\EntityProviderInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Select;
use Zend_Db;
use Zend_Db_Select;
use Zend_Db_Select_Exception;

/**
 *
 */
abstract class AbstractFromQueryProvider extends BaseProvider
{

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;

    /**
     * @var Select
     */
    private $select;

    /**
     * AbstractFromCollectionProvider constructor.
     * @param ResourceConnection $resourceConnection
     * @param ScopeConfigInterface $scopeConfig
     */
    public function __construct(
        ResourceConnection   $resourceConnection,
        ScopeConfigInterface $scopeConfig
    )
    {
        parent::__construct($scopeConfig);
        $this->resourceConnection = $resourceConnection;
    }

    /**
     * @return bool
     * @throws Zend_Db_Select_Exception
     */
    public function valid()
    {
        if (current($this->items) !== false) {
            return true;
        }
        $this->load();

        return current($this->items) !== false;
    }

    /**
     * @throws Zend_Db_Select_Exception
     */
    public function load()
    {
        $this->items = [];

        $isFirstPage = false;
        $nextPage = 1;
        $pageSize = $this->getBulkSize();

        if ($this->select === null) {
            $this->select = $this->getSelect();
            $isFirstPage = true;
        }

        if ($isFirstPage === false) {
            $offset = $this->select->getPart(Zend_Db_Select::LIMIT_OFFSET);
            $nextPage = 2 + ($offset / $pageSize);
        }

        $this->select->limitPage($nextPage, $pageSize);
        $rows = $this->getResourceConnection()->getConnection()->fetchAll($this->select, [], Zend_Db::FETCH_ASSOC);

        $this->items = [];
        foreach ($rows as $row) {
            $this->items[] = $this->adapt($row);
        }

        if (!empty($this->items) && $this->hasReadHandler()) {
            $this->getReadHandler()->onRead($this->items, $this->getBulkSize(), $this->getReadHandlerArguments());
        }
    }

    /**
     * @return Select
     */
    abstract public function getSelect(): Select;

    /**
     * @return ResourceConnection
     */
    public function getResourceConnection(): ResourceConnection
    {
        return $this->resourceConnection;
    }

    /**
     * @param array $row
     * @return EntityInterface
     */
    abstract public function adapt(array $row): EntityInterface;

    /**
     * @throws Zend_Db_Select_Exception
     */
    #[\ReturnTypeWillChange]
    public function rewind()
    {
        $this->select = null;
        $this->load();
    }

    /**
     * @return \Generator
     * @throws Zend_Db_Select_Exception
     * @throws \Zend_Db_Statement_Exception
     */
    public function toGenerator(): \Generator
    {
        $isFirstPage = false;
        $nextPage = 1;
        $pageSize = $this->getBulkSize();

        if ($this->select === null) {
            $this->select = $this->getSelect();
            $isFirstPage = true;
        }

        if ($isFirstPage === false) {
            $offset = $this->select->getPart(Zend_Db_Select::LIMIT_OFFSET);
            $nextPage = 2 + ($offset / $pageSize);
        }

        $this->select->limitPage($nextPage, $pageSize);

        $statement = $this->resourceConnection->getConnection()->query($this->select);
        while (($item = $statement->fetch(Zend_Db::FETCH_ASSOC))) {
            yield $this->adapt($item);
        }
    }

    /**
     * @return EntityProviderInterface
     */
    public function release(): EntityProviderInterface
    {
        $this->select = null;
        $this->items = [];
        return $this;
    }

}
