<?php

declare(strict_types=1);

namespace Tests\Framework\Database\Schema\Comparison;

use App\Framework\Database\Schema\Comparison\SchemaDifference;
use App\Framework\Database\Schema\Comparison\TableDifference;

test('creates a schema difference with missing and extra tables', function () {
    $missingTables = [
        'users' => [
            'columns' => [
                'id' => ['type' => 'integer', 'nullable' => false],
                'name' => ['type' => 'varchar', 'nullable' => false],
                'email' => ['type' => 'varchar', 'nullable' => false],
            ],
        ],
        'posts' => [
            'columns' => [
                'id' => ['type' => 'integer', 'nullable' => false],
                'title' => ['type' => 'varchar', 'nullable' => false],
                'content' => ['type' => 'text', 'nullable' => true],
            ],
        ],
    ];

    $extraTables = [
        'categories' => [
            'columns' => [
                'id' => ['type' => 'integer', 'nullable' => false],
                'name' => ['type' => 'varchar', 'nullable' => false],
            ],
        ],
    ];

    $difference = new SchemaDifference(
        'source_schema',
        'target_schema',
        $missingTables,
        $extraTables,
        []
    );

    expect($difference->sourceSchema)->toBe('source_schema');
    expect($difference->targetSchema)->toBe('target_schema');
    expect($difference->missingTables)->toBe($missingTables);
    expect($difference->extraTables)->toBe($extraTables);
    expect($difference->tableDifferences)->toBeEmpty();
    expect($difference->hasDifferences())->toBeTrue();
});

test('creates a schema difference with table differences', function () {
    $tableDifference = new TableDifference(
        'users',
        ['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
        ['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
        [
            'name' => [
                'source' => ['type' => 'varchar', 'nullable' => false],
                'target' => ['type' => 'varchar', 'nullable' => true],
            ],
        ], // modified columns
        [], // missing indexes
        [], // extra indexes
        [], // modified indexes
        [], // missing foreign keys
        [], // extra foreign keys
        []  // modified foreign keys
    );

    $difference = new SchemaDifference(
        'source_schema',
        'target_schema',
        [], // missing tables
        [], // extra tables
        ['users' => $tableDifference]
    );

    expect($difference->sourceSchema)->toBe('source_schema');
    expect($difference->targetSchema)->toBe('target_schema');
    expect($difference->missingTables)->toBeEmpty();
    expect($difference->extraTables)->toBeEmpty();
    expect($difference->tableDifferences)->toHaveCount(1);
    expect($difference->tableDifferences['users'])->toBe($tableDifference);
    expect($difference->hasDifferences())->toBeTrue();
});

test('creates a schema difference with no differences', function () {
    $difference = new SchemaDifference(
        'source_schema',
        'target_schema',
        [], // missing tables
        [], // extra tables
        []  // table differences
    );

    expect($difference->sourceSchema)->toBe('source_schema');
    expect($difference->targetSchema)->toBe('target_schema');
    expect($difference->missingTables)->toBeEmpty();
    expect($difference->extraTables)->toBeEmpty();
    expect($difference->tableDifferences)->toBeEmpty();
    expect($difference->hasDifferences())->toBeFalse();
});

test('gets summary of differences', function () {
    $tableDifference1 = new TableDifference(
        'users',
        ['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
        ['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
        [], // modified columns
        [], // missing indexes
        [], // extra indexes
        [], // modified indexes
        [], // missing foreign keys
        [], // extra foreign keys
        []  // modified foreign keys
    );

    $tableDifference2 = new TableDifference(
        'posts',
        [], // missing columns
        [], // extra columns
        [], // modified columns
        ['idx_posts_title' => ['type' => 'index', 'columns' => ['title']]], // missing indexes
        [], // extra indexes
        [], // modified indexes
        [], // missing foreign keys
        [], // extra foreign keys
        []  // modified foreign keys
    );

    $difference = new SchemaDifference(
        'source_schema',
        'target_schema',
        ['comments' => []], // missing tables
        ['categories' => []], // extra tables
        [
            'users' => $tableDifference1,
            'posts' => $tableDifference2,
        ]
    );

    $summary = $difference->getSummary();

    expect($summary)->toBeArray();
    expect($summary['missing_tables'])->toBe(1);
    expect($summary['extra_tables'])->toBe(1);
    expect($summary['modified_tables'])->toBe(2);
    expect($summary['total_differences'])->toBe(4);
});

test('gets description of differences', function () {
    $tableDifference = new TableDifference(
        'users',
        ['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
        ['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
        [
            'name' => [
                'source' => ['type' => 'varchar', 'nullable' => false],
                'target' => ['type' => 'varchar', 'nullable' => true],
            ],
        ], // modified columns
        [], // missing indexes
        [], // extra indexes
        [], // modified indexes
        [], // missing foreign keys
        [], // extra foreign keys
        []  // modified foreign keys
    );

    $difference = new SchemaDifference(
        'source_schema',
        'target_schema',
        ['comments' => []], // missing tables
        ['categories' => []], // extra tables
        ['users' => $tableDifference]
    );

    $description = $difference->getDescription();

    expect($description)->toBeString();
    expect($description)->toContain('Schema Differences');
    expect($description)->toContain('Missing Tables');
    expect($description)->toContain('comments');
    expect($description)->toContain('Extra Tables');
    expect($description)->toContain('categories');
    expect($description)->toContain('Table Differences');
    expect($description)->toContain('users');
});

test('generates migration code from differences', function () {
    $tableDifference = new TableDifference(
        'users',
        ['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
        ['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
        [
            'name' => [
                'source' => ['type' => 'varchar', 'nullable' => false],
                'target' => ['type' => 'varchar', 'nullable' => true],
            ],
        ], // modified columns
        [], // missing indexes
        [], // extra indexes
        [], // modified indexes
        [], // missing foreign keys
        [], // extra foreign keys
        []  // modified foreign keys
    );

    $difference = new SchemaDifference(
        'source_schema',
        'target_schema',
        ['comments' => [
            'columns' => [
                'id' => ['type' => 'integer', 'nullable' => false],
                'content' => ['type' => 'text', 'nullable' => false],
                'post_id' => ['type' => 'integer', 'nullable' => false],
            ],
        ]], // missing tables
        ['categories' => []], // extra tables
        ['users' => $tableDifference]
    );

    $migrationCode = $difference->generateMigrationCode('UpdateSchema');

    expect($migrationCode)->toBeString();
    expect($migrationCode)->toContain('class UpdateSchema extends AbstractMigration');
    expect($migrationCode)->toContain('public function up(ConnectionInterface $connection)');
    expect($migrationCode)->toContain('public function down(ConnectionInterface $connection)');
    expect($migrationCode)->toContain('$schema->create(\'comments\'');
    expect($migrationCode)->toContain('$schema->table(\'users\'');
    expect($migrationCode)->toContain('$schema->dropIfExists(\'categories\')');
});
