<?php

declare(strict_types=1);

namespace FiloBlu\Refilo\Remote;

use Exception;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Indexer\ActionInterface;
use Magento\Framework\Mview\ActionInterface as MviewActionInterface;

/**
 * FlowStockIndexerBridge
 *
 * Bridge indexer action that maps product entity ids to inventory source items and
 * writes affected entity ids into the `filoblu_flow_stock_cl` change log table.
 *
 * This class implements both the indexer action interface and the mview action
 * interface so it can be used for full/partial/indexer row updates.
 */
class FlowStockIndexerBridge implements ActionInterface, MviewActionInterface
{
    /**
     * Resource connection instance.
     *
     * @var ResourceConnection
     */
    private $connection;

    /**
     * Metadata pool to retrieve entity link field metadata.
     *
     * @var MetadataPool
     */
    private $metadataPool;

    /**
     * Construct the bridge with required dependencies.
     *
     * @param ResourceConnection $connection
     * @param MetadataPool $metadataPool
     */
    public function __construct(
        ResourceConnection $connection,
        MetadataPool $metadataPool
    ) {
        $this->connection = $connection;
        $this->metadataPool = $metadataPool;
    }

    /**
     * Execute a full indexing run.
     *
     * Intentionally left empty: this bridge only performs partial updates
     * triggered by entity changes.
     *
     * @return void
     */
    public function executeFull()
    {
        // This method is intentionally left empty because we only need partial updates
    }

    /**
     * Execute indexing for a list of entity ids.
     *
     * Delegates to execute(array $ids).
     *
     * @param int[] $ids
     * @return void
     * @throws Exception
     */
    public function executeList(array $ids)
    {
        $this->execute($ids);
    }

    /**
     * Execute indexing for the given entity ids.
     *
     * This method finds related inventory source items by joining product tables
     * and inserts affected entity ids into the `filoblu_flow_stock_cl` table using
     * an INSERT IGNORE from a select to avoid duplicates.
     *
     * @param int[] $ids
     * @return void
     * @throws Exception
     */
    public function execute($ids)
    {
        if (empty($ids)) {
            return;
        }

        $connection = $this->connection->getConnection();
        $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
        $tableCpei = $this->connection->getTableName('catalog_product_entity_int');
        $tableCpe = $this->connection->getTableName('catalog_product_entity');
        $tableIsi = $this->connection->getTableName('inventory_source_item');

        $select = $connection->select()->distinct()
            ->from(['cpei' => $tableCpei], [])
            ->join(
                ['cpe' => $tableCpe],
                "cpei.$linkField = cpe.$linkField",
                []
            )
            ->join(
                ['isi' => $tableIsi],
                'cpe.sku = isi.sku',
                ['source_item_id']
            )
            ->where('cpe.entity_id IN (?)', $ids);

        $query = $connection->insertFromSelect(
            $select,
            $connection->getTableName('filoblu_flow_stock_cl'),
            ['entity_id'],
            AdapterInterface::INSERT_IGNORE
        );
        $connection->query($query);
    }

    /**
     * Execute indexing for a single entity id.
     *
     * Delegates to execute(array $ids) with a single-element array.
     *
     * @param int $id
     * @return void
     * @throws Exception
     */
    public function executeRow($id)
    {
        $this->execute([$id]);
    }
}
