<?php

namespace FiloBlu\Refilo\Remote\Entity\Provider;

use FiloBlu\Refilo\Remote\Entity\EntityProviderInterface;
use FiloBlu\Refilo\Remote\Entity\Product;
use Generator;
use LogicException;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Module\Manager;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Api\Data\WebsiteInterface;
use Magento\Store\Model\ScopeInterface;
use Zend_Db;

/**
 * Class RatingProvider
 * @package FiloBlu\Refilo\Remote\Entity\Provider
 */
class RatingProvider extends BaseProvider
{
    /** @var string  */
    const XML_CONFIG_REVIEW_ENABLED = 'catalog/review/active';

    /** @var int */
    protected $website;

    /** @var Manager */
    protected $moduleManager;

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

    /** @var array */
    private $lockupTemplate = [];

    /** @var */
    private $store;

    /** @var int */
    protected $currentPage = 0;

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

    /**
     * @param $name
     * @return string|string[]
     */
    public function normalizeName($name)
    {
        $name = strtolower($name);
        return str_replace(' ', '_', $name);
    }

    /**
     *
     */
    #[\ReturnTypeWillChange]
    public function rewind()
    {
        $this->currentPage = 0;
        $this->items = [];
        if (!$this->scopeConfig->getValue(self::XML_CONFIG_REVIEW_ENABLED, ScopeInterface::SCOPE_STORE, $this->store)) {
            return;
        }

        $this->extractRating();

        if (!$this->hasReadHandler()) {
            return;
        }

        // TODO: Handle pagination
        $this->getReadHandler()->onRead($this->items, $this->getBulkSize(), $this->getReadHandlerArguments());
    }

    /**
     * @param array $ids
     * @return $this|EntityProviderInterface
     */
    public function withIds(array $ids): EntityProviderInterface
    {
        $this->ids = $ids;
        return $this;
    }

    /**
     * @param StoreInterface $store
     * @return $this|EntityProviderInterface
     */
    public function forStore(StoreInterface $store): EntityProviderInterface
    {
        $this->store = $store->getId();
        return $this;
    }

    /**
     * @param WebsiteInterface $website
     * @return $this|EntityProviderInterface
     */
    public function forWebsite(WebsiteInterface $website): EntityProviderInterface
    {
        throw new LogicException('Count not filter by website');
    }

    /**
     * @return bool
     */
    #[\ReturnTypeWillChange]
    public function valid()
    {

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

        $this->items = [];

        $this->extractRating();

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


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

    public function extractRating(){
        $ratingTable = $this->resourceConnection->getTableName('rating');
        $ratingOptionVoteAggregated = $this->resourceConnection->getTableName('rating_option_vote_aggregated');
        $catalogProductEntity = $this->resourceConnection->getTableName('catalog_product_entity');

        $storeId = (int)$this->store;
        $bulkSize = (int)$this->getBulkSize();
        $offset = (int)($bulkSize * $this->currentPage);
        $sql = "SELECT
                    IFNULL(rova.entity_pk_value,cpe.entity_id) as entity_pk_value,
                    sum(rova.vote_count) as vote_count,
                    ROUND(avg(rova.percent_approved),0) as percent_approved,
                    IFNULL(rova.store_id,{$storeId}) as store_id,
                    'global'as rating_code
                FROM
                    {$catalogProductEntity} cpe
                LEFT JOIN
                    {$ratingOptionVoteAggregated} rova ON
                    cpe.entity_id = rova.entity_pk_value AND rova.store_id = {$storeId}
                LEFT JOIN {$ratingTable} r ON
                    rova.rating_id = r.rating_id AND r.is_active = 1
                WHERE r.entity_id = 1
                GROUP BY cpe.entity_id
                LIMIT {$bulkSize}
                OFFSET {$offset}";

        // TODO: add filter per store e per id
        $rows = $this->resourceConnection
            ->getConnection()
            ->fetchAll($sql, [], Zend_Db::FETCH_ASSOC);

        $this->currentPage++;

        $items = [];
        foreach ($rows as $row) {
            if($row['rating_code']!=null){
                $items[$row['entity_pk_value']][$this->normalizeName($row['rating_code'])] = [
                    'review_count'   => (int)$row['vote_count'],
                    'review_summary' => (int)$row['percent_approved'],
                ];
            }
            else {
                $this->items[$row['entity_pk_value']]['removethis'] = [];
            }
        }

        foreach ($items as $id => $rating)
        {
            if(isset($rating['removethis'])){
                unset($rating['removethis']);
            }

            $this->items[(int)$id] = new Product(
                [
                    Product::ID => (int)$id,
                    Product::RATING => array_merge($this->lockupTemplate, $rating)
                ]
            );
        }

    }

    /**
     * @return mixed
     */
    public function getSelect()
    {
        $ratingTable = $this->resourceConnection->getTableName('rating');
        $ratingOptionVoteAggregated = $this->resourceConnection->getTableName('rating_option_vote_aggregated');
        $catalogProductEntity = $this->resourceConnection->getTableName('catalog_product_entity');

        $storeId = (int)$this->store;
        return "SELECT
                    IFNULL(rova.entity_pk_value,cpe.entity_id) as entity_pk_value,
                    sum(rova.vote_count) as vote_count,
                    ROUND(avg(rova.percent_approved),0) as percent_approved,
                    IFNULL(rova.store_id,{$storeId}) as store_id,
                    'global'as rating_code
                FROM
                    {$catalogProductEntity} cpe
                LEFT JOIN
                    {$ratingOptionVoteAggregated} rova ON
                    cpe.entity_id = rova.entity_pk_value AND rova.store_id = {$storeId}
                LEFT JOIN {$ratingTable} r ON
                    rova.rating_id = r.rating_id AND r.is_active = 1
                WHERE r.entity_id = 1
                GROUP BY cpe.entity_id";
    }

    /**
     * @return Generator
     * @throws \Zend_Db_Statement_Exception
     */
    public function toGenerator(): Generator
    {
        if (!$this->scopeConfig->getValue(self::XML_CONFIG_REVIEW_ENABLED, ScopeInterface::SCOPE_STORE, $this->store)) {
            yield null;
        }

        $select = $this->getSelect();

        $statement = $this->resourceConnection->getConnection()->query($select);

        while (($row = $statement->fetch(Zend_Db::FETCH_ASSOC))) {
            $product = new Product([
                Product::ID => (int)$row['entity_pk_value'],
                Product::RATING => [
                    $this->normalizeName($row['rating_code']) => [
                        'review_count'   => (int)$row['vote_count'],
                        'review_summary' => (int)$row['percent_approved'],
                    ]
                ]
            ]);

            yield $product;
        }
    }

    public function release(): EntityProviderInterface
    {
        $this->items = [];
        return $this;
    }
}
