<?php

namespace WPD\WordPress;

final class ConfigParser
{
    private $guard;

    public function __construct(PathGuard $guard)
    {
        $this->guard = $guard;
    }

    public function parse($wordpressRoot)
    {
        $path = $this->guard->safeFile(rtrim($wordpressRoot, '/\\') . '/wp-config.php');
        if ($path === null || !is_readable($path)) {
            return array('values' => array(), 'table_prefix' => null, 'complete' => false, 'error' => 'No se puede leer wp-config.php.');
        }

        $source = @file_get_contents($path, false, null, 0, 1048576);
        if (!is_string($source)) {
            return array('values' => array(), 'table_prefix' => null, 'complete' => false, 'error' => 'No se pudo cargar wp-config.php.');
        }

        $tokens = token_get_all($source);
        $values = array();
        $prefix = null;
        $count = count($tokens);
        for ($i = 0; $i < $count; $i++) {
            $token = $tokens[$i];
            if (is_array($token) && $token[0] === T_STRING && strtolower($token[1]) === 'define') {
                $parsed = $this->parseDefine($tokens, $i);
                if ($parsed !== null && in_array($parsed[0], array('DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST'), true)) {
                    $values[$parsed[0]] = $parsed[1];
                }
            }
            if (is_array($token) && $token[0] === T_VARIABLE && $token[1] === '$table_prefix') {
                $parsedPrefix = $this->parseAssignment($tokens, $i);
                if ($parsedPrefix !== null) {
                    $prefix = $parsedPrefix;
                }
            }
        }

        $complete = count(array_intersect(array('DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST'), array_keys($values))) === 4
            && is_string($prefix)
            && preg_match('/^[A-Za-z0-9_]+$/', $prefix) === 1;

        return array(
            'values' => $values,
            'table_prefix' => $prefix,
            'complete' => $complete,
            'error' => $complete ? null : 'La configuración usa valores que no se pueden interpretar de forma estática.',
        );
    }

    private function parseDefine(array $tokens, $start)
    {
        $i = $this->nextSignificant($tokens, $start + 1);
        if ($i === null || $tokens[$i] !== '(') {
            return null;
        }
        $i = $this->nextSignificant($tokens, $i + 1);
        if ($i === null || !is_array($tokens[$i]) || $tokens[$i][0] !== T_CONSTANT_ENCAPSED_STRING) {
            return null;
        }
        $name = $this->decodeString($tokens[$i][1]);
        $i = $this->nextSignificant($tokens, $i + 1);
        if ($i === null || $tokens[$i] !== ',') {
            return null;
        }
        $i = $this->nextSignificant($tokens, $i + 1);
        if ($i === null || !is_array($tokens[$i]) || $tokens[$i][0] !== T_CONSTANT_ENCAPSED_STRING) {
            return null;
        }
        $value = $this->decodeString($tokens[$i][1]);
        $i = $this->nextSignificant($tokens, $i + 1);
        return $i !== null && $tokens[$i] === ')' ? array($name, $value) : null;
    }

    private function parseAssignment(array $tokens, $start)
    {
        $i = $this->nextSignificant($tokens, $start + 1);
        if ($i === null || $tokens[$i] !== '=') {
            return null;
        }
        $i = $this->nextSignificant($tokens, $i + 1);
        if ($i === null || !is_array($tokens[$i]) || $tokens[$i][0] !== T_CONSTANT_ENCAPSED_STRING) {
            return null;
        }
        return $this->decodeString($tokens[$i][1]);
    }

    private function nextSignificant(array $tokens, $start)
    {
        $count = count($tokens);
        for ($i = $start; $i < $count; $i++) {
            if (is_array($tokens[$i]) && in_array($tokens[$i][0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT), true)) {
                continue;
            }
            return $i;
        }
        return null;
    }

    private function decodeString($literal)
    {
        $quote = substr($literal, 0, 1);
        $value = substr($literal, 1, -1);
        if ($quote === "'") {
            return str_replace(array("\\\\", "\\'"), array("\\", "'"), $value);
        }
        return stripcslashes($value);
    }
}
