<?php

namespace Xentral\Components\Database\Parser;

use Xentral\Components\Database\Exception\MissingParameterException;

/**
 * Responsibility of this class is to make sql statement and bind values compatible with mysqli
 *
 * (Mysqli does not support named parameters)
 *
 * It does this by:
 * - Replacing named parameters (:param) by ?-Placeholder (in statement)
 * - Rearranging bind values in order of appearance of named parameters
 */
final class MysqliNamedParameterParser implements ParserInterface
{
    /**
     * @param string $statement
     * @param array  $values
     *
     * @return array
     */
    public function rebuild($statement, array $values = [])
    {
        return $this->replaceNamedParameters($statement, $values);
    }

    /**
     * @param string $statement
     * @param array  $values
     *
     * @throws MissingParameterException
     *
     * @return array
     */
    private function replaceNamedParameters($statement, array $values = [])
    {
        $result = [
            'statement' => $statement,
            'values'    => [],
            'params'    => [],
        ];

        if (empty($values)) {
            return $result;
        }

        // Split statement on named parameters
        $parts = preg_split('/(:[a-zA-Z0-9_]+)/um', $statement, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

        foreach ($parts as &$part) {
            if (strpos($part, ':') !== 0) {
                continue; // SQL part does not contain named parameter
            }

            $parameterName = substr_replace($part, '', 0, 1);
            if (!array_key_exists($parameterName, $values)) {
                throw new MissingParameterException(sprintf(
                    'Parameter "%s" is missing from the bound values',
                    $parameterName
                ));
            }

            // Push values in same order of parameters for binding
            $result['values'][] = $values[$parameterName];
            $result['params'][] = $parameterName; // For debugging only

            // Replace named parameter by ?-Placeholder
            $part = '?';
        }
        unset($part);

        // Rebuild statement from (changed) parts
        $result['statement'] = implode('', $parts);

        return $result;
    }
}