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

use boru\ai\Persistence\Db\DbSchemaManager;
use boru\dhdb\Query;
use boru\dhdb\Schema;
use PHPUnit\Framework\TestCase;

class DbSchemaManagerTest extends TestCase
{
    public function testCopySchemaMetaIgnoresNullLikeTableMetaValues()
    {
        $manager = new TestDbSchemaManagerForMetaCopy();

        $current = new Schema('ai_turns');
        $current->meta('engine', ' none ');
        $current->meta('collate', 'null');
        $current->meta('comment', '');

        $expected = new Schema('ai_turns');
        $expected->meta('engine', 'InnoDB');
        $expected->meta('collate', 'utf8mb4_unicode_ci');
        $expected->meta('comment', 'existing');

        $manager->copySchemaMetaPublic($current, $expected);

        $this->assertSame(0, $expected->version());
        $this->assertSame('InnoDB', $this->getSchemaMetaValue($expected, 'engine'));
        $this->assertSame('utf8mb4_unicode_ci', $this->getSchemaMetaValue($expected, 'collate'));
        $this->assertSame('existing', $this->getSchemaMetaValue($expected, 'comment'));
    }

    public function testCopySchemaMetaPreservesNormalizedLiveTableMetaValues()
    {
        $manager = new TestDbSchemaManagerForMetaCopy();

        $current = new Schema('ai_turns');
        $current->version(7);
        $current->meta('engine', ' InnoDB ');
        $current->meta('collate', ' utf8mb4_unicode_ci ');
        $current->meta('comment', ' table comment ');

        $expected = new Schema('ai_turns');

        $manager->copySchemaMetaPublic($current, $expected);

        $this->assertSame(0, $expected->version());
        $this->assertSame('InnoDB', $this->getSchemaMetaValue($expected, 'engine'));
        $this->assertSame('utf8mb4_unicode_ci', $this->getSchemaMetaValue($expected, 'collate'));
        $this->assertSame('table comment', $this->getSchemaMetaValue($expected, 'comment'));
    }

    public function testBuildSchemaPatchQueryReturnsFalseForMatchingExistingSchemaWithNullLikeMeta()
    {
        $manager = new TestDbSchemaManagerForMetaCopy();

        $current = new Schema('ai_turns');
        $current->add('id', 'bigint(20)', array('null' => false, 'auto_increment' => true));
        $current->add('status', 'varchar(100)', array('null' => false, 'default' => 'pending'));
        $current->addIndex('PRIMARY', array('id'), 'primary');
        $current->meta('engine', 'none');
        $current->meta('collate', 'null');
        $current->meta('comment', '');

        $expected = new Schema('ai_turns');
        $expected->add('id', 'bigint(20)', array('null' => false, 'auto_increment' => true));
        $expected->add('status', 'varchar(100)', array('null' => false, 'default' => 'pending'));
        $expected->addIndex('PRIMARY', array('id'), 'primary');
        $expected->meta('engine', 'InnoDB');

        $query = $manager->buildSchemaPatchQueryPublic($current, $expected);

        $this->assertFalse($query);
    }

    public function testBuildSchemaPatchQueryGeneratesAlterStatementForMissingColumn()
    {
        $manager = new TestDbSchemaManagerForMetaCopy();

        $current = new Schema('ai_turns');
        $current->add('id', 'bigint(20)', array('null' => false, 'auto_increment' => true));
        $current->addIndex('PRIMARY', array('id'), 'primary');
        $current->meta('engine', 'InnoDB');

        $expected = new Schema('ai_turns');
        $expected->add('id', 'bigint(20)', array('null' => false, 'auto_increment' => true));
        $expected->add('status', 'varchar(100)', array('null' => false, 'default' => 'pending'));
        $expected->addIndex('PRIMARY', array('id'), 'primary');
        $expected->meta('engine', 'InnoDB');

        $query = $manager->buildSchemaPatchQueryPublic($current, $expected);

        $this->assertInstanceOf(Query::class, $query);
        $this->assertStringStartsWith('ALTER TABLE `ai_turns`', $query->getQuery());
        $this->assertStringContainsString('ADD COLUMN `status` varchar(100) NOT NULL DEFAULT "pending" AFTER `id`', $query->getQuery());
    }

    protected function getSchemaMetaValue(Schema $schema, $metaKey)
    {
        $meta = $schema->getMeta();
        if (!is_array($meta) || !isset($meta[$metaKey]) || !is_array($meta[$metaKey])) {
            return null;
        }

        if (!array_key_exists('value', $meta[$metaKey])) {
            return null;
        }

        return $meta[$metaKey]['value'];
    }
}

class TestDbSchemaManagerForMetaCopy extends DbSchemaManager
{
    public function __construct()
    {
    }

    public function copySchemaMetaPublic(Schema $current, Schema $expected)
    {
        return $this->copySchemaMeta($current, $expected);
    }

    public function buildSchemaPatchQueryPublic(Schema $current, Schema $expected)
    {
        return $this->buildSchemaPatchQuery($current, $expected);
    }
}
