<?php
/**
 * Created by PhpStorm.
 * User: Shokhaa
 * Date: 18/05/21
 * Time: 14:09
 */

namespace App\Model;


use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Identifier;

class MultipleInsert
{

    protected Identifier $table;
    protected array $columns = [];
    protected array $valueSets = [];
    protected ?array $types = [];

    protected ?int $lastInsertId = null;
    protected ?int $numInsertedRows = null;


    public function __construct(private Connection $connection)
    {
    }


    public function setTable(string $table):void
    {
        $this->table = new Identifier($table);

    }


    public static function init(Connection $connection, string $tableName):self
    {
        $db = new self($connection);
        $db->setTable($tableName);
        return $db;
    }


    public function setColumns(array $columns):self
    {
        $this->columns = $columns;
        return $this;
    }


    public function setValues(array $valueSets, array $types = null):self
    {
        $this->valueSets = $valueSets;
        $this->types = $types;
        return $this;
    }


    public function execute():self
    {
        $sql = $this->getSQL();
        $parameters = array_reduce($this->valueSets, function (array $flattenedValues, array $valueSet) {
            return array_merge($flattenedValues, array_values($valueSet));
        }, []);
        $this->connection->executeQuery($sql, $parameters, $this->getPositionalTypes());
        $this->lastInsertId = $this->connection->lastInsertId();
        $this->numInsertedRows = count($this->valueSets);
        return $this;
    }


    public function getLastInsertIds():array
    {
        $lastInsertIds = [];
        if (null !== $this->lastInsertId && $this->numInsertedRows > 0) {
            $lastInsertIds = range(
                $this->lastInsertId,
                $this->lastInsertId + $this->numInsertedRows - 1
            );
        }
        return $lastInsertIds;
    }

    public function getInsertRows(): ?int
    {
        return count($this->getLastInsertIds());
    }


    protected function getSQL():string
    {
        $platform = $this->connection->getDatabasePlatform();
        $escapedColumns = array_map(function (string $column) use ($platform) {
            return (new Identifier($column))->getQuotedName($platform);
        }, $this->columns);
        // (id, name, ..., date)
        $columnString = empty($this->columns) ? '' : '(' . implode(', ', $escapedColumns) . ')';
        // (?, ?, ?, ... , ?)
        $singlePlaceholder = '(' . implode(', ', array_fill(0, count($this->columns), '?')) . ')';
        // (?, ?), ... , (?, ?)
        $placeholders = implode(', ', array_fill(0, count($this->valueSets), $singlePlaceholder));
        $sql = sprintf("INSERT INTO %s %s VALUES %s;",
            $this->table->getQuotedName($platform), $columnString, $placeholders);
        return $sql;
    }

    /**
     *
     * @return int[] PDO::PARAM_*
     */
    protected function getPositionalTypes()
    {
        if (empty($this->types)) {
            return [];
        }
        $types = array_values($this->types);
        $repeat = count($this->valueSets);
        $positionalTypes = [];
        for ($i = 1; $i <= $repeat; $i++) {
            $positionalTypes = array_merge($positionalTypes, $types);
        }
        return $positionalTypes;
    }

}