<?php

namespace Comitium5\DesignerBundle\Form\DataTransformer;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use ForceUTF8\Encoding;
use Symfony\Component\Form\DataTransformerInterface;

/**
 * Js file associated is external-db-suggest.js. Init search requires a json string if multiple option is
 * defined or a id if is not defined. Reverse transform get an string with a single id or with
 * multiples id separated by commas. Fetch entity must be by id always.
 *
 * Class ExternalDataBaseSuggestTransformer
 *
 * @author Oscar Jiménez <oscarjg19.developer@gmail.com>
 * @package ComitiumSuite\Bundle\AdminBundle\Form\DataTransformer
 */
class ExternalDataBaseSuggestTransformer implements DataTransformerInterface
{
    /**
     * @var bool
     */
    private $multiple = false;

    /**
     * @var Connection
     */
    private $connection;

    /**
     * @var string
     */
    private $stringConnection;
    /**
     * @var array
     */
    private $matchQuery;
    /**
     * @var array
     */
    private $suggestQuery;

    /**
     * @var string
     */
    private $matchProperty;

    /**
     * @var string
     */
    private $separator;

    /**
     * @param mixed $value
     * @return string
     * @throws \Doctrine\DBAL\DBALException
     * @throws \Exception
     */
    public function transform($value)
    {
        if (empty($value)) {
            return "";
        }

        if (!isset($this->matchQuery["query"]) && !isset($this->matchQuery["text_key"])) {
            throw new \Exception("query and text_key keys must be provided");
        }

        $matchQuery = preg_replace("/(:".$this->matchProperty.")/", str_replace($this->separator, ",", $value), $this->matchQuery["query"]);

        $this->connection->query($matchQuery);
        $stmt = $this->connection->prepare($matchQuery);
        $success = $stmt->execute();

        if ($success !== true) {
            return "";
        }

        $idKey  = empty($this->matchQuery["id_key"]) ? "id" : $this->matchQuery["id_key"];
        $textKey = $this->matchQuery["text_key"];
        $values = [];

        if ($this->multiple === false) {
            $result = $stmt->fetch();

            $values = [
                'id'   => empty($result[$idKey])   ? null : $result[$idKey],
                'text' => empty($result[$textKey]) ? null : Encoding::fixUTF8($result[$textKey]),
            ];

        } else {
            $results = $stmt->fetchAll();

            $ids = explode($this->separator, $value);

            foreach ($results as $result) {
                $indexes = array_keys($ids, $result[$idKey]);

                $values[$indexes[0]] = [
                    'id'   => empty($result[$idKey])   ? null : $result[$idKey],
                    'text' => empty($result[$textKey]) ? null : Encoding::fixUTF8($result[$textKey]),
                ];
            }

            ksort($values);

        }

        $this->connection->close();

        return json_encode($values);
    }

    /**
     * @param mixed $data
     * @return mixed
     */
    public function reverseTransform($data)
    {
        return $data;
    }

    /**
     * @return mixed
     */
    public function getSuggestQuery()
    {
        return $this->suggestQuery;
    }

    /**
     * @param $suggestQuery
     * @return $this
     */
    public function setSuggestQuery($suggestQuery)
    {
        $this->suggestQuery = $suggestQuery;

        return $this;
    }

    /**
     * @return array
     */
    public function getMatchQuery()
    {
        return $this->matchQuery;
    }

    /**
     * @param array $matchQuery
     * @return $this
     */
    public function setMatchQuery(array $matchQuery)
    {
        $this->matchQuery = $matchQuery;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getStringConnection()
    {
        return $this->stringConnection;
    }

    /**
     * @param mixed $stringConnection
     * @return $this
     */
    public function setStringConnection($stringConnection)
    {
        $this->stringConnection = $stringConnection;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getConnection()
    {
        return $this->connection;
    }

    /**
     * @param mixed $connection
     * @return $this
     */
    public function setConnection($connection)
    {
        $this->connection = $connection;

        return $this;
    }

    /**
     * @param null $stringConnection
     * @return $this
     * @throws \Doctrine\DBAL\DBALException
     */
    public function createConnection($stringConnection = null)
    {
        $stringConnection = $stringConnection === null ? $this->stringConnection : $stringConnection;

        $this->connection = DriverManager::getConnection([
            "url" => $stringConnection
        ]);

        return $this;
    }

    /**
     * @return boolean
     */
    public function isMultiple()
    {
        return $this->multiple;
    }

    /**
     * @param boolean $multiple
     * @return $this
     */
    public function setMultiple($multiple)
    {
        $this->multiple = $multiple;

        return $this;
    }

    /**
     * @return string
     */
    public function getMatchProperty()
    {
        return $this->matchProperty;
    }

    /**
     * @param string $matchProperty
     * @return $this
     */
    public function setMatchProperty($matchProperty)
    {
        $this->matchProperty = $matchProperty;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getSeparator()
    {
        return $this->separator;
    }

    /**
     * @param mixed $separator
     * @return $this
     */
    public function setSeparator($separator)
    {
        $this->separator = $separator;

        return $this;
    }
}
