<?php
/**
 * @author Daniel J. Summers <daniel@bitbadger.solutions>
 * @license MIT
 */

declare(strict_types=1);

namespace Test\Integration\PostgreSQL;

use BitBadger\PDODocument\{Custom, Definition, DocumentException, DocumentIndex};
use BitBadger\PDODocument\Mapper\ExistsMapper;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

/**
 * PostgreSQL integration tests for the Definition class
 */
#[TestDox('Definition (PostgreSQL integration)')]
class DefinitionTest extends TestCase
{
    /** @var string Database name for throwaway database */
    private string $dbName;

    protected function setUp(): void
    {
        parent::setUp();
        $this->dbName = ThrowawayDb::create(withData: false);
    }

    protected function tearDown(): void
    {
        ThrowawayDb::destroy($this->dbName);
        parent::tearDown();
    }

    /**
     * Does the given named object exist in the database?
     *
     * @param string $name The name of the object whose existence should be verified
     * @return bool True if the object exists, false if not
     * @throws DocumentException If any is encountered
     */
    private function itExists(string $name): bool
    {
        return Custom::scalar('SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name)',
            [':name' => $name], new ExistsMapper());
    }

    public function testEnsureTableSucceeds(): void
    {
        $this->assertFalse($this->itExists('ensured'), 'The table should not exist already');
        $this->assertFalse($this->itExists('idx_ensured_key'), 'The key index should not exist already');
        Definition::ensureTable('ensured');
        $this->assertTrue($this->itExists('ensured'), 'The table should now exist');
        $this->assertTrue($this->itExists('idx_ensured_key'), 'The key index should now exist');
    }

    public function testEnsureFieldIndexSucceeds(): void
    {
        $this->assertFalse($this->itExists('idx_ensured_test'), 'The index should not exist already');
        Definition::ensureTable('ensured');
        Definition::ensureFieldIndex('ensured', 'test', ['name', 'age']);
        $this->assertTrue($this->itExists('idx_ensured_test'), 'The index should now exist');
    }

    public function testEnsureDocumentIndexSucceedsForFull(): void
    {
        $docIdx = 'idx_' . ThrowawayDb::TABLE . '_document';
        Definition::ensureTable(ThrowawayDb::TABLE);
        $this->assertFalse($this->itExists($docIdx), 'The document index should not exist');
        Definition::ensureDocumentIndex(ThrowawayDb::TABLE, DocumentIndex::Full);
        $this->assertTrue($this->itExists($docIdx), 'The document index should now exist');
    }

    public function testEnsureDocumentIndexSucceedsForOptimized(): void
    {
        $docIdx = 'idx_' . ThrowawayDb::TABLE . '_document';
        Definition::ensureTable(ThrowawayDb::TABLE);
        $this->assertFalse($this->itExists($docIdx), 'The document index should not exist');
        Definition::ensureDocumentIndex(ThrowawayDb::TABLE, DocumentIndex::Optimized);
        $this->assertTrue($this->itExists($docIdx), 'The document index should now exist');
    }
}