<?php

namespace External\MSSQLBundle\Util;

use Doctrine\DBAL\Query\QueryBuilder;

class MoneyConnection
{
    private $settings;

    private $connection;

    public function __construct(array $settings)
    {
        $this->settings = $settings;
    }

    public function getConnection()
    {
        $settings = $this->getSettings();

        $dbname = $settings['db_name'];

        if (isset($this->connection[$dbname])) {
            return $this->connection[$dbname];
        }

        $config = new \Doctrine\DBAL\Configuration();
        $connectionParams = [
            'dbname' => $dbname,
            'user' => $settings['db_user'],
            'password' => $settings['db_pass'],
            'host' => $settings['db_host'],
            'port' => $settings['db_port'],
            'driver' => 'pdo_sqlsrv',
            'charset' => 'utf8',
            'driverOptions' => [
                'TrustServerCertificate' => true,
            ],
        ];

        if (array_key_exists('pooling', $settings)) {
            $connectionParams['driverOptions']['ConnectionPooling'] = $settings['pooling'];
        }

        if ($settings['timeout'] ?? false) {
            $connectionParams['driverOptions']['LoginTimeout'] = $settings['timeout'];
            $connectionParams['driverOptions'][\PDO::SQLSRV_ATTR_QUERY_TIMEOUT] = $settings['timeout'];
        }

        return $this->connection[$dbname] = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config);
    }

    public function close(): void
    {
        $this->getConnection()->close();
        $this->connection = null;
    }

    public function getQueryBuilder(): QueryBuilder
    {
        return $this->getConnection()->createQueryBuilder();
    }

    public function update(string $tableName, array $data, array $IDField): int
    {
        [$field, $value] = $IDField;

        $qb = $this->getQueryBuilder()
            ->update($tableName)
            ->andWhere($field.' = :ID')
            ->setParameter('ID', $value);

        foreach ($data as $field => $value) {
            $parameterName = 'u_'.$field;
            $qb->set($field, ':'.$parameterName)
                ->setParameter($parameterName, $value);
        }

        return $qb->execute();
    }

    public function insertPure(string $tableName, array $data): void
    {
        $qb = $this->getQueryBuilder()
            ->insert($tableName);

        $this->bindDataToQueryBuilder($qb, $data)
            ->execute();
    }

    public function insert(string $tableName, string $uniqueField, array $data, ?string $uniqueFieldValue = null): ?string
    {
        $qb = $this->getQueryBuilder()
            ->insert($tableName)
            ->setValue($uniqueField, $uniqueFieldValue ? ':uniqueFieldValue' : 'NEWID()');

        if ($uniqueFieldValue) {
            $qb->setParameter('uniqueFieldValue', $uniqueFieldValue);
        }

        $qb = $this->bindDataToQueryBuilder($qb, $data);

        $SQL = $qb->getSQL();
        $tmp = explode('VALUES', $SQL);
        $tmp[0] .= " OUTPUT INSERTED.{$uniqueField} ";
        $SQL = join(' VALUES ', $tmp);

        if ($result = $this->getConnection()->executeQuery($SQL, $qb->getParameters(), $qb->getParameterTypes())) {
            if ($id = $result->fetchColumn()) {
                return $id;
            }
        }

        return null;
    }

    protected function getSettings(): array
    {
        return $this->settings;
    }

    private function bindDataToQueryBuilder(QueryBuilder $qb, array $data): QueryBuilder
    {
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                [$value, $bind] = $value;
                if ($bind) {
                    $qb->setValue($key, ':'.$key)
                        ->setParameter($key, $value);
                } else {
                    $qb->setValue($key, $value);
                }
            } else {
                $qb->setValue($key, ':'.$key)
                    ->setParameter($key, $value);
            }
        }

        return $qb;
    }
}
