<?php

declare(strict_types=1);

namespace Tests\Framework\Database\Schema\Index;

use App\Framework\Database\Schema\Index\AdvancedIndexDefinition;
use App\Framework\Database\Schema\Index\AdvancedIndexType;

test('creates a standard index definition', function () {
    $index = AdvancedIndexDefinition::create(
        'idx_users_email',
        ['email'],
        AdvancedIndexType::INDEX
    );

    expect($index->name)->toBe('idx_users_email');
    expect($index->columns)->toBe(['email']);
    expect($index->type)->toBe(AdvancedIndexType::INDEX);
    expect($index->whereClause)->toBeNull();
    expect($index->options)->toBeEmpty();
    expect($index->isFunctional)->toBeFalse();
});

test('creates a partial index definition', function () {
    $index = AdvancedIndexDefinition::partial(
        'idx_users_email',
        ['email'],
        AdvancedIndexType::INDEX,
        'active = true'
    );

    expect($index->name)->toBe('idx_users_email');
    expect($index->columns)->toBe(['email']);
    expect($index->type)->toBe(AdvancedIndexType::INDEX);
    expect($index->whereClause)->toBe('active = true');
    expect($index->options)->toBeEmpty();
});

test('creates a functional index definition', function () {
    $index = AdvancedIndexDefinition::functional(
        'idx_users_lower_email',
        AdvancedIndexType::INDEX,
        ['LOWER(email)']
    );

    expect($index->name)->toBe('idx_users_lower_email');
    expect($index->columns)->toBeEmpty();
    expect($index->type)->toBe(AdvancedIndexType::INDEX);
    expect($index->isFunctional)->toBeTrue();
    expect($index->expressions)->toBe(['LOWER(email)']);
});

test('creates a GIN index definition', function () {
    $index = AdvancedIndexDefinition::gin(
        'idx_documents_content',
        ['content']
    );

    expect($index->name)->toBe('idx_documents_content');
    expect($index->columns)->toBe(['content']);
    expect($index->type)->toBe(AdvancedIndexType::GIN);
});

test('creates a GiST index definition', function () {
    $index = AdvancedIndexDefinition::gist(
        'idx_locations_position',
        ['position']
    );

    expect($index->name)->toBe('idx_locations_position');
    expect($index->columns)->toBe(['position']);
    expect($index->type)->toBe(AdvancedIndexType::GIST);
});

test('creates a BTREE index definition with options', function () {
    $index = AdvancedIndexDefinition::btree(
        'idx_users_name',
        ['first_name', 'last_name'],
        ['fillfactor' => 70]
    );

    expect($index->name)->toBe('idx_users_name');
    expect($index->columns)->toBe(['first_name', 'last_name']);
    expect($index->type)->toBe(AdvancedIndexType::BTREE);
    expect($index->options)->toBe(['fillfactor' => 70]);
});

test('generates PostgreSQL partial index SQL', function () {
    $index = AdvancedIndexDefinition::partial(
        'idx_users_email',
        ['email'],
        AdvancedIndexType::INDEX,
        'active = true'
    );

    $sql = $index->toSql('pgsql', 'users');

    expect($sql)->toBe('CREATE INDEX "idx_users_email" ON "users" ("email") WHERE active = true');
});

test('generates PostgreSQL functional index SQL', function () {
    $index = AdvancedIndexDefinition::functional(
        'idx_users_lower_email',
        AdvancedIndexType::INDEX,
        ['LOWER(email)']
    );

    $sql = $index->toSql('pgsql', 'users');

    expect($sql)->toBe('CREATE INDEX "idx_users_lower_email" ON "users" (LOWER(email))');
});

test('generates PostgreSQL GIN index SQL', function () {
    $index = AdvancedIndexDefinition::gin(
        'idx_documents_content',
        ['content']
    );

    $sql = $index->toSql('pgsql', 'documents');

    expect($sql)->toBe('CREATE INDEX "idx_documents_content" ON "documents" USING gin ("content")');
});

test('generates PostgreSQL GiST index SQL', function () {
    $index = AdvancedIndexDefinition::gist(
        'idx_locations_position',
        ['position']
    );

    $sql = $index->toSql('pgsql', 'locations');

    expect($sql)->toBe('CREATE INDEX "idx_locations_position" ON "locations" USING gist ("position")');
});

test('generates PostgreSQL index SQL with options', function () {
    $index = AdvancedIndexDefinition::btree(
        'idx_users_email',
        ['email'],
        ['fillfactor' => 70]
    );

    $sql = $index->toSql('pgsql', 'users');

    // Note: PostgreSQL doesn't use USING btree by default, so it's not in the SQL
    expect($sql)->toBe('CREATE INDEX "idx_users_email" ON "users" ("email")');
});

test('throws exception for partial index in MySQL', function () {
    $index = AdvancedIndexDefinition::partial(
        'idx_users_email',
        ['email'],
        AdvancedIndexType::INDEX,
        'active = true'
    );

    expect(fn () => $index->toSql('mysql', 'users'))
        ->toThrow(\InvalidArgumentException::class, 'MySQL does not support partial indexes');
});

test('generates MySQL functional index SQL', function () {
    $index = AdvancedIndexDefinition::functional(
        'idx_users_lower_email',
        AdvancedIndexType::INDEX,
        ['LOWER(email)']
    );

    $sql = $index->toSql('mysql', 'users');

    // MySQL 8.0+ supports functional indexes with ALTER TABLE
    expect($sql)->toBe('ALTER TABLE `users` ADD INDEX `idx_users_lower_email` (LOWER(email))');
});

test('throws exception for unsupported index type in MySQL', function () {
    $index = AdvancedIndexDefinition::gin(
        'idx_documents_content',
        ['content']
    );

    expect(fn () => $index->toSql('mysql', 'documents'))
        ->toThrow(\InvalidArgumentException::class, 'Index type gin is not supported by mysql');
});

test('throws exception for unsupported database driver', function () {
    $index = AdvancedIndexDefinition::btree(
        'idx_users_email',
        ['email']
    );

    expect(fn () => $index->toSql('unsupported_driver', 'users'))
        ->toThrow(\InvalidArgumentException::class, 'Unsupported driver: unsupported_driver');
});
