<?php

namespace WPD\Database;

final class ReadOnlyDatabase
{
    private $connection;

    public function connect(array $credentials)
    {
        if (!extension_loaded('mysqli')) {
            throw new DatabaseException('extension_missing');
        }

        list($host, $port, $socket) = $this->parseHost($credentials['host']);
        mysqli_report(MYSQLI_REPORT_OFF);
        $connection = mysqli_init();
        if ($connection === false) {
            throw new DatabaseException('unclassified');
        }
        $connection->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5);
        $ok = @$connection->real_connect(
            $host,
            $credentials['user'],
            $credentials['password'],
            $credentials['name'],
            $port,
            $socket
        );
        if (!$ok) {
            throw new DatabaseException($this->categoryForCode((int) $connection->connect_errno));
        }
        if (!$connection->set_charset('utf8mb4')) {
            $connection->close();
            throw new DatabaseException('unclassified');
        }
        $this->connection = $connection;
    }

    public function rows($sql)
    {
        if (!is_string($sql) || preg_match('/^\s*SELECT\b/i', $sql) !== 1 || strpos($sql, ';') !== false) {
            throw new \InvalidArgumentException('Solo se permiten consultas SELECT simples.');
        }
        if (!$this->connection instanceof \mysqli) {
            throw new DatabaseException('unclassified');
        }
        $result = $this->connection->query($sql);
        if ($result === false) {
            throw new DatabaseException($this->categoryForCode((int) $this->connection->errno));
        }
        $rows = array();
        while ($row = $result->fetch_assoc()) {
            $rows[] = $row;
        }
        $result->free();
        return $rows;
    }

    public function quote($value)
    {
        if (!$this->connection instanceof \mysqli) {
            throw new DatabaseException('unclassified');
        }
        return "'" . $this->connection->real_escape_string((string) $value) . "'";
    }

    public function close()
    {
        if ($this->connection instanceof \mysqli) {
            $this->connection->close();
        }
        $this->connection = null;
    }

    private function parseHost($value)
    {
        $host = (string) $value;
        $port = null;
        $socket = null;

        if (strpos($host, ':') !== false) {
            list($base, $suffix) = explode(':', $host, 2);
            if ($suffix !== '' && ctype_digit($suffix)) {
                $host = $base;
                $port = (int) $suffix;
            } elseif ($suffix !== '') {
                $host = $base;
                $socket = $suffix[0] === '/' ? $suffix : '/' . ltrim($suffix, '/');
            }
        }
        return array($host, $port, $socket);
    }

    private function categoryForCode($code)
    {
        if (in_array($code, array(1044, 1045), true)) {
            return 'credentials_rejected';
        }
        if ($code === 1049) {
            return 'database_unavailable';
        }
        if ($code === 1146) {
            return 'table_unavailable';
        }
        if (in_array($code, array(2002, 2003, 2005, 2006, 2013), true)) {
            return 'server_unavailable';
        }
        return 'unclassified';
    }
}
