<?php
namespace boru\ai\Tests\Persistence\Db;

use boru\ai\Persistence\Db\DbConfig;
use boru\ai\Persistence\Db\DbConnection;
use boru\ai\Persistence\Db\DbQueryException;
use PHPUnit\Framework\TestCase;

class DbConnectionTest extends TestCase
{
    public function testRunReturnsStatementWhenDhdbDoesNotReportError()
    {
        $statement = new FakeDbStatement(false, null);
        $connection = new TestDbConnection(new FakeDbHandle($statement));

        $result = $connection->run('select 1');

        $this->assertSame($statement, $result);
    }

    public function testRunThrowsWhenDhdbStatementReportsError()
    {
        $error = new FakeDbError('Unknown column title', '42S22', 'insert into table values ("x")');
        $connection = new TestDbConnection(new FakeDbHandle(new FakeDbStatement(true, $error)));

        try {
            $connection->run('insert into table values (?)', array('x'));
            $this->fail('Expected DbQueryException was not thrown');
        } catch (DbQueryException $e) {
            $this->assertSame('Unknown column title', $e->getMessage());
            $this->assertSame(42, $e->getCode());
            $this->assertSame('insert into table values (?)', $e->sql());
            $this->assertSame(array('x'), $e->params());
            $this->assertSame('insert into table values ("x")', $e->interpolatedSql());
        }
    }
}

class TestDbConnection extends DbConnection
{
    /** @var object */
    protected $handle;

    public function __construct($handle)
    {
        parent::__construct(new DbConfig());
        $this->handle = $handle;
    }

    public function db($dbConfig = null)
    {
        return $this->handle;
    }
}

class FakeDbHandle
{
    /** @var FakeDbStatement */
    protected $statement;

    public function __construct(FakeDbStatement $statement)
    {
        $this->statement = $statement;
    }

    public function run($sql, $params = array(), $options = array())
    {
        return $this->statement;
    }
}

class FakeDbStatement
{
    /** @var bool */
    protected $error;

    /** @var mixed */
    protected $errorObject;

    public function __construct($error, $errorObject)
    {
        $this->error = $error ? true : false;
        $this->errorObject = $errorObject;
    }

    public function isError()
    {
        return $this->error;
    }

    public function getError()
    {
        return $this->errorObject;
    }
}

class FakeDbError
{
    protected $message;
    protected $code;
    protected $interpolated;

    public function __construct($message, $code, $interpolated)
    {
        $this->message = $message;
        $this->code = $code;
        $this->interpolated = $interpolated;
    }

    public function getMessage()
    {
        return $this->message;
    }

    public function getCode()
    {
        return $this->code;
    }

    public function getInterpolated()
    {
        return $this->interpolated;
    }
}
