<?php

declare(strict_types=1);

require_once __DIR__ . '/../../src/Framework/Database/Schema/SchemaCompiler.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/Blueprint.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/ColumnDefinition.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/IndexDefinition.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/IndexType.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/ForeignKeyDefinition.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/ForeignKeyAction.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/PostgreSQLSchemaCompiler.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/Commands/CreateTableCommand.php';
require_once __DIR__ . '/../../src/Framework/Database/Schema/Commands/DropTableCommand.php';

use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\PostgreSQLSchemaCompiler;
use App\Framework\Database\Schema\Commands\CreateTableCommand;

echo "Testing PostgreSQL Range Types\n";
echo "===============================\n\n";

try {
    $pdo = new PDO(
        'pgsql:host=db;dbname=michaelschiemer',
        'postgres',
        'StartSimple2024!'
    );
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    echo "✅ Database connection established\n\n";

    $compiler = new PostgreSQLSchemaCompiler();

    // Cleanup
    echo "Cleanup...\n";
    $pdo->exec("DROP TABLE IF EXISTS test_ranges CASCADE");
    echo "✅ Cleanup complete\n\n";

    // Test 1: Create table with all range types
    echo "Test 1: Create Table With Range Types\n";
    echo "======================================\n";

    $blueprint = new Blueprint('test_ranges');
    $blueprint->bigIncrements('id');
    $blueprint->string('description');

    // Numeric ranges
    $blueprint->int4range('age_range')->nullable();
    $blueprint->int8range('population_range')->nullable();
    $blueprint->numrange('price_range')->nullable();

    // Timestamp ranges
    $blueprint->tsrange('event_period')->nullable();
    $blueprint->tstzrange('booking_period')->nullable();

    // Date ranges
    $blueprint->daterange('valid_dates')->nullable();

    $createCommand = new CreateTableCommand('test_ranges', $blueprint);
    $sql = $compiler->compile($createCommand);

    echo "Creating table with range columns:\n";
    echo $sql . "\n\n";

    $pdo->exec($sql);
    echo "✅ Table 'test_ranges' created with all range types\n\n";

    // Test 2: Insert data with range values
    echo "Test 2: Insert Range Data\n";
    echo "==========================\n";

    $pdo->exec("
        INSERT INTO test_ranges (
            description,
            age_range,
            population_range,
            price_range,
            event_period,
            booking_period,
            valid_dates
        ) VALUES (
            'Hotel Booking',
            '[18,65)',                                    -- Age 18 to 64 (inclusive start, exclusive end)
            '[1000000,5000000)',                          -- Population range
            '[99.99,499.99]',                            -- Price range (both inclusive)
            '[2024-06-01 10:00:00, 2024-06-01 18:00:00)', -- Event period
            '[2024-06-01 10:00:00+02, 2024-06-01 18:00:00+02)', -- Booking with timezone
            '[2024-01-01, 2024-12-31]'                    -- Valid date range
        )
    ");
    echo "✅ Range data inserted\n\n";

    // Test 3: Query range data
    echo "Test 3: Query Range Data\n";
    echo "=========================\n";

    $stmt = $pdo->query("SELECT * FROM test_ranges WHERE id = 1");
    $result = $stmt->fetch(PDO::FETCH_ASSOC);

    echo "Retrieved data:\n";
    echo "   - Description: {$result['description']}\n";
    echo "   - Age Range: {$result['age_range']}\n";
    echo "   - Population Range: {$result['population_range']}\n";
    echo "   - Price Range: {$result['price_range']}\n";
    echo "   - Event Period: {$result['event_period']}\n";
    echo "   - Booking Period: {$result['booking_period']}\n";
    echo "   - Valid Dates: {$result['valid_dates']}\n";
    echo "✅ Range data successfully retrieved\n\n";

    // Test 4: Range operators
    echo "Test 4: Range Operators\n";
    echo "========================\n";

    // Insert test data for operator tests
    $pdo->exec("
        INSERT INTO test_ranges (description, age_range, price_range, valid_dates)
        VALUES
        ('Product A', '[25,50)', '[100.00,200.00]', '[2024-01-01,2024-06-30]'),
        ('Product B', '[30,60)', '[150.00,300.00]', '[2024-03-01,2024-09-30]'),
        ('Product C', '[40,70)', '[250.00,500.00]', '[2024-06-01,2024-12-31]')
    ");
    echo "✅ Test data inserted\n\n";

    // Test @> (contains) operator
    echo "Test @> (contains) operator:\n";
    $stmt = $pdo->query("SELECT description FROM test_ranges WHERE age_range @> 35");
    $results = $stmt->fetchAll(PDO::FETCH_COLUMN);
    echo "   Products with age range containing 35: " . implode(', ', $results) . "\n";
    echo (count($results) == 3) ? "✅ Contains operator works\n\n" : "❌ Should find 3 products\n\n";

    // Test <@ (is contained by) operator
    echo "Test <@ (is contained by) operator:\n";
    $stmt = $pdo->query("SELECT description FROM test_ranges WHERE '[100,150]'::numrange <@ price_range");
    $results = $stmt->fetchAll(PDO::FETCH_COLUMN);
    echo "   Products containing price range [100,150]: " . implode(', ', $results) . "\n";
    echo (count($results) >= 1) ? "✅ Is contained by operator works\n\n" : "❌ Should find products\n\n";

    // Test && (overlap) operator
    echo "Test && (overlap) operator:\n";
    $stmt = $pdo->query("SELECT description FROM test_ranges WHERE valid_dates && '[2024-05-01,2024-07-31]'::daterange");
    $results = $stmt->fetchAll(PDO::FETCH_COLUMN);
    echo "   Products with overlapping date range: " . implode(', ', $results) . "\n";
    echo (count($results) >= 2) ? "✅ Overlap operator works\n\n" : "❌ Should find overlapping products\n\n";

    // Test 5: Range functions
    echo "Test 5: Range Functions\n";
    echo "========================\n";

    // lower() and upper() functions
    $stmt = $pdo->query("
        SELECT
            description,
            lower(age_range) as min_age,
            upper(age_range) as max_age,
            lower(price_range) as min_price,
            upper(price_range) as max_price
        FROM test_ranges
        WHERE description = 'Product A'
    ");
    $result = $stmt->fetch(PDO::FETCH_ASSOC);

    echo "Product A bounds:\n";
    echo "   - Min Age: {$result['min_age']}\n";
    echo "   - Max Age: {$result['max_age']}\n";
    echo "   - Min Price: {$result['min_price']}\n";
    echo "   - Max Price: {$result['max_price']}\n";
    echo "✅ Range boundary functions work\n\n";

    // isempty() function
    echo "Test isempty() function:\n";
    $stmt = $pdo->query("SELECT COUNT(*) FROM test_ranges WHERE isempty(age_range)");
    $emptyCount = $stmt->fetchColumn();
    echo "   - Empty ranges: {$emptyCount}\n";
    echo ($emptyCount == 0) ? "✅ No empty ranges found (as expected)\n\n" : "✅ isempty() function works\n\n";

    // Test 6: Range intersection
    echo "Test 6: Range Intersection\n";
    echo "===========================\n";

    // Test range intersection
    $stmt = $pdo->query("
        SELECT
            '[25,60)'::int4range * '[30,50)'::int4range as intersection,
            '[100.00,200.00]'::numrange * '[150.00,300.00]'::numrange as price_intersection
    ");
    $result = $stmt->fetch(PDO::FETCH_ASSOC);
    echo "Range intersections:\n";
    echo "   - Age range [25,60) * [30,50) = {$result['intersection']}\n";
    echo "   - Price range [100,200] * [150,300] = {$result['price_intersection']}\n";
    echo "✅ Range intersection operator works\n\n";

    // Test 7: GiST index on range column
    echo "Test 7: GiST Index on Range Column\n";
    echo "===================================\n";

    $pdo->exec("CREATE INDEX idx_test_ranges_age ON test_ranges USING GIST (age_range)");
    echo "✅ GiST index created on age_range column\n";

    $pdo->exec("CREATE INDEX idx_test_ranges_price ON test_ranges USING GIST (price_range)");
    echo "✅ GiST index created on price_range column\n";

    $pdo->exec("CREATE INDEX idx_test_ranges_dates ON test_ranges USING GIST (valid_dates)");
    echo "✅ GiST index created on valid_dates column\n\n";

    // Test 8: Verify indexes are used
    echo "Test 8: Verify Index Usage\n";
    echo "===========================\n";

    $stmt = $pdo->query("
        SELECT indexname, indexdef
        FROM pg_indexes
        WHERE tablename = 'test_ranges'
        AND indexname LIKE 'idx_test_ranges_%'
        ORDER BY indexname
    ");
    $indexes = $stmt->fetchAll(PDO::FETCH_ASSOC);

    echo "Found " . count($indexes) . " GiST indexes:\n";
    foreach ($indexes as $index) {
        echo "   - {$index['indexname']}\n";
    }
    echo (count($indexes) == 3) ? "✅ All GiST indexes created\n\n" : "❌ Expected 3 indexes\n\n";

    // Cleanup
    echo "Cleanup...\n";
    $pdo->exec("DROP TABLE IF EXISTS test_ranges CASCADE");
    echo "✅ Test data cleaned up\n";

    echo "\n✅ All Range Types tests passed!\n";
    echo "\nSummary:\n";
    echo "========\n";
    echo "✅ INT4RANGE (integer ranges) works\n";
    echo "✅ INT8RANGE (bigint ranges) works\n";
    echo "✅ NUMRANGE (numeric ranges) works\n";
    echo "✅ TSRANGE (timestamp ranges) works\n";
    echo "✅ TSTZRANGE (timestamp with timezone ranges) works\n";
    echo "✅ DATERANGE (date ranges) works\n";
    echo "✅ Range operators (@>, <@, &&) work\n";
    echo "✅ Range functions (lower, upper, isempty) work\n";
    echo "✅ Range intersection operator (*) works\n";
    echo "✅ GiST indexes on range columns work\n";

} catch (\Exception $e) {
    echo "❌ Error: " . $e->getMessage() . "\n";
    echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
    exit(1);
}
