<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\Exception\MemoryThresholdExceededException;
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Migration\MigrationCollection;
use App\Framework\Database\Migration\MigrationRunner;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Migration\ValueObjects\MemoryThresholds;
use App\Framework\Database\Platform\MySQLPlatform;
use App\Framework\Database\ResultInterface;
use App\Framework\Database\ValueObjects\SqlQuery;
use App\Framework\DateTime\SystemClock;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Performance\OperationTracker;
use App\Framework\Performance\PerformanceReporter;

describe('Migration Performance Integration', function () {
    beforeEach(function () {
        $this->connection = new MigrationPerformanceMockConnection();
        $this->platform = new MySQLPlatform();
        $this->clock = new SystemClock();
        $this->memoryMonitor = new MemoryMonitor();
        $this->operationTracker = new OperationTracker($this->clock, $this->memoryMonitor);

        $this->migrationRunner = new MigrationRunner(
            $this->connection,
            $this->platform,
            $this->clock,
            null, // tableConfig
            null, // logger
            $this->operationTracker,
            $this->memoryMonitor
        );
    });

    test('migration runner integrates with performance monitoring', function () {
        $migration = new MigrationPerformanceTestMigration();
        $migrations = new MigrationCollection($migration);

        // Set no applied migrations initially
        $this->connection->setAppliedMigrations([]);

        $result = $this->migrationRunner->migrate($migrations);

        expect($result)->toContain('2024_01_01_000000');
        expect($migration->wasExecuted())->toBeTrue();

        // Verify performance tracking was used
        expect($this->operationTracker)->toBeInstanceOf(OperationTracker::class);
        expect($this->memoryMonitor)->toBeInstanceOf(MemoryMonitor::class);
    });

    test('memory monitor provides memory summary', function () {
        $memorySummary = $this->memoryMonitor->getSummary();

        expect($memorySummary)->toHaveProperties([
            'current',
            'peak',
            'limit',
            'usagePercentage',
            'isApproachingLimit',
        ]);

        expect($memorySummary->getCurrentHumanReadable())->toBeString();
        expect($memorySummary->getPeakHumanReadable())->toBeString();
        expect($memorySummary->getUsagePercentageFormatted())->toBeString();
    });

    test('operation tracker can track operations', function () {
        $operationId = 'test_migration_batch_' . uniqid();

        $snapshot = $this->operationTracker->startOperation(
            $operationId,
            \App\Framework\Performance\PerformanceCategory::DATABASE,
            ['operation_type' => 'migration_test']
        );

        expect($snapshot)->toBeInstanceOf(\App\Framework\Performance\ValueObjects\PerformanceSnapshot::class);

        // Complete the operation
        $finalSnapshot = $this->operationTracker->completeOperation($operationId);

        expect($finalSnapshot)->toBeInstanceOf(\App\Framework\Performance\ValueObjects\PerformanceSnapshot::class);
        expect($finalSnapshot->duration)->toBeInstanceOf(\App\Framework\Core\ValueObjects\Duration::class);
    });

    test('memory thresholds can be configured with custom values', function () {
        $customThresholds = new MemoryThresholds(
            warning: Percentage::from(60.0),
            critical: Percentage::from(75.0),
            abort: Percentage::from(90.0)
        );

        expect($customThresholds->warning->getValue())->toBe(60.0);
        expect($customThresholds->critical->getValue())->toBe(75.0);
        expect($customThresholds->abort->getValue())->toBe(90.0);
    });

    test('memory thresholds default configuration is valid', function () {
        $defaultThresholds = MemoryThresholds::default();

        expect($defaultThresholds->warning->getValue())->toBe(75.0);
        expect($defaultThresholds->critical->getValue())->toBe(85.0);
        expect($defaultThresholds->abort->getValue())->toBe(95.0);
    });

    test('memory thresholds conservative configuration is valid', function () {
        $conservativeThresholds = MemoryThresholds::conservative();

        expect($conservativeThresholds->warning->getValue())->toBe(60.0);
        expect($conservativeThresholds->critical->getValue())->toBe(70.0);
        expect($conservativeThresholds->abort->getValue())->toBe(80.0);
    });

    test('memory thresholds relaxed configuration is valid', function () {
        $relaxedThresholds = MemoryThresholds::relaxed();

        expect($relaxedThresholds->warning->getValue())->toBe(80.0);
        expect($relaxedThresholds->critical->getValue())->toBe(90.0);
        expect($relaxedThresholds->abort->getValue())->toBe(98.0);
    });

    test('memory threshold exception can be created for migration', function () {
        $currentUsage = Percentage::from(96.0);
        $threshold = Percentage::from(95.0);
        $currentMemory = \App\Framework\Core\ValueObjects\Byte::fromBytes(256 * 1024 * 1024);
        $memoryLimit = \App\Framework\Core\ValueObjects\Byte::fromBytes(256 * 1024 * 1024);

        $exception = MemoryThresholdExceededException::forMigration(
            '2024_01_01_000001',
            $currentUsage,
            $threshold,
            $currentMemory,
            $memoryLimit
        );

        expect($exception->getMessage())->toContain('Memory threshold exceeded during migration 2024_01_01_000001');
        expect($exception->getMessage())->toContain('96.0%');
        expect($exception->getMessage())->toContain('95.0%');
    });

    test('memory threshold exception can be created for batch abort', function () {
        $currentUsage = Percentage::from(97.0);
        $abortThreshold = Percentage::from(95.0);
        $currentMemory = \App\Framework\Core\ValueObjects\Byte::fromBytes(248 * 1024 * 1024);
        $memoryLimit = \App\Framework\Core\ValueObjects\Byte::fromBytes(256 * 1024 * 1024);

        $exception = MemoryThresholdExceededException::batchAborted(
            $currentUsage,
            $abortThreshold,
            $currentMemory,
            $memoryLimit,
            3,
            10
        );

        expect($exception->getMessage())->toContain('Migration batch aborted due to critical memory usage');
        expect($exception->getMessage())->toContain('97.0%');
        expect($exception->getMessage())->toContain('95.0%');
    });

    test('migration runner with memory thresholds can handle normal operation', function () {
        $memoryThresholds = MemoryThresholds::conservative();

        $migrationRunner = new MigrationRunner(
            $this->connection,
            $this->platform,
            $this->clock,
            null, // tableConfig
            null, // logger
            $this->operationTracker,
            $this->memoryMonitor,
            null, // performanceReporter
            $memoryThresholds
        );

        $migration = new MigrationPerformanceTestMigration();
        $migrations = new MigrationCollection($migration);

        // Set no applied migrations initially
        $this->connection->setAppliedMigrations([]);

        $result = $migrationRunner->migrate($migrations);

        expect($result)->toContain('2024_01_01_000000');
        expect($migration->wasExecuted())->toBeTrue();
    });

    test('rollback operation includes performance tracking', function () {
        $migration = new MigrationPerformanceTestMigration();
        $migrations = new MigrationCollection($migration);

        // Set migration as already applied
        $this->connection->setAppliedMigrations([
            ['version' => '2024_01_01_000000', 'description' => 'Performance Test Migration'],
        ]);

        $result = $this->migrationRunner->rollback($migrations, 1);

        expect($result)->toContain($migration);

        // Verify performance tracking was used during rollback
        expect($this->operationTracker)->toBeInstanceOf(OperationTracker::class);
        expect($this->memoryMonitor)->toBeInstanceOf(MemoryMonitor::class);
    });

    test('operation tracker can track multiple operations', function () {
        $operationId1 = 'test_migration_1';
        $operationId2 = 'test_migration_2';

        // Start first operation
        $snapshot1 = $this->operationTracker->startOperation(
            $operationId1,
            \App\Framework\Performance\PerformanceCategory::DATABASE,
            ['operation_type' => 'migration_execution']
        );

        // Start second operation
        $snapshot2 = $this->operationTracker->startOperation(
            $operationId2,
            \App\Framework\Performance\PerformanceCategory::DATABASE,
            ['operation_type' => 'migration_execution']
        );

        usleep(1000); // Small delay to ensure duration > 0

        // Complete both operations
        $finalSnapshot1 = $this->operationTracker->completeOperation($operationId1);
        $finalSnapshot2 = $this->operationTracker->completeOperation($operationId2);

        expect($finalSnapshot1)->toBeInstanceOf(\App\Framework\Performance\ValueObjects\PerformanceSnapshot::class);
        expect($finalSnapshot2)->toBeInstanceOf(\App\Framework\Performance\ValueObjects\PerformanceSnapshot::class);
        expect($finalSnapshot1->operationId)->toBe($operationId1);
        expect($finalSnapshot2->operationId)->toBe($operationId2);
    });

    test('migration runner validates memory threshold order', function () {
        expect(function () {
            new MemoryThresholds(
                warning: Percentage::from(90.0),  // Warning higher than critical
                critical: Percentage::from(80.0),
                abort: Percentage::from(95.0)
            );
        })->toThrow(\InvalidArgumentException::class, 'Warning threshold cannot be higher than critical threshold');

        expect(function () {
            new MemoryThresholds(
                warning: Percentage::from(70.0),
                critical: Percentage::from(90.0),  // Critical higher than abort
                abort: Percentage::from(80.0)
            );
        })->toThrow(\InvalidArgumentException::class, 'Critical threshold cannot be higher than abort threshold');
    });

    test('migration runner can handle pre-flight checks', function () {
        $migration = new MigrationPerformanceTestMigration();
        $migrations = new MigrationCollection($migration);

        // Set no applied migrations initially
        $this->connection->setAppliedMigrations([]);

        // Should pass pre-flight checks for mock connection
        $result = $this->migrationRunner->migrate($migrations);

        expect($result)->toContain('2024_01_01_000000');
        expect($migration->wasExecuted())->toBeTrue();
    });
});

// Test fixtures
class MigrationPerformanceTestMigration implements Migration
{
    private bool $executed = false;

    public function up(ConnectionInterface $connection): void
    {
        $this->executed = true;
        // Simulate migration execution
        $connection->execute(SqlQuery::create('CREATE TABLE performance_test (id INT)'));
    }

    public function down(ConnectionInterface $connection): void
    {
        $connection->execute(SqlQuery::create('DROP TABLE performance_test'));
    }

    public function getDescription(): string
    {
        return 'Performance Test Migration';
    }

    public function getVersion(): MigrationVersion
    {
        return MigrationVersion::fromTimestamp('2024_01_01_000000');
    }

    public function wasExecuted(): bool
    {
        return $this->executed;
    }
}

class MigrationPerformanceMockConnection implements ConnectionInterface
{
    private array $queries = [];

    private array $appliedMigrations = [];

    private bool $inTransaction = false;

    public function setAppliedMigrations(array $migrations): void
    {
        $this->appliedMigrations = $migrations;
    }

    public function execute(SqlQuery $query): int
    {
        $this->queries[] = ['type' => 'execute', 'sql' => $query->sql, 'params' => $query->parameters->toArray()];

        return 1;
    }

    public function query(SqlQuery $query): ResultInterface
    {
        $this->queries[] = ['type' => 'query', 'sql' => $query->sql, 'params' => $query->parameters->toArray()];

        return new MigrationPerformanceMockResult($this->appliedMigrations);
    }

    public function queryOne(SqlQuery $query): ?array
    {
        return null;
    }

    public function queryColumn(SqlQuery $query): array
    {
        $this->queries[] = ['type' => 'queryColumn', 'sql' => $query->sql, 'params' => $query->parameters->toArray()];

        return array_column($this->appliedMigrations, 'version');
    }

    public function queryScalar(SqlQuery $query): mixed
    {
        $this->queries[] = ['type' => 'queryScalar', 'sql' => $query->sql, 'params' => $query->parameters->toArray()];

        // Return '1' for connectivity test (SELECT 1)
        if (str_contains($query->sql, 'SELECT 1')) {
            return '1';
        }

        return null;
    }

    public function beginTransaction(): void
    {
        $this->inTransaction = true;
    }

    public function commit(): void
    {
        $this->inTransaction = false;
    }

    public function rollback(): void
    {
        $this->inTransaction = false;
    }

    public function inTransaction(): bool
    {
        return $this->inTransaction;
    }

    public function lastInsertId(): string
    {
        return '1';
    }

    public function getPdo(): \PDO
    {
        return new class () extends \PDO {
            public function __construct()
            {
                // Skip parent constructor to avoid actual DB connection
            }

            public function getAttribute(int $attribute): mixed
            {
                return match($attribute) {
                    \PDO::ATTR_DRIVER_NAME => 'mysql',
                    default => null
                };
            }
        };
    }

    public function getQueries(): array
    {
        return $this->queries;
    }
}

class MigrationPerformanceMockResult implements ResultInterface
{
    private array $data;

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function fetch(): ?array
    {
        return $this->data[0] ?? null;
    }

    public function fetchAll(): array
    {
        return $this->data;
    }

    public function fetchOne(): ?array
    {
        return $this->data[0] ?? null;
    }

    public function fetchColumn(int $column = 0): array
    {
        return array_column($this->data, $column);
    }

    public function fetchScalar(): mixed
    {
        $row = $this->fetchOne();

        return $row ? array_values($row)[0] : null;
    }

    public function rowCount(): int
    {
        return count($this->data);
    }

    public function getIterator(): \Traversable
    {
        return new \ArrayIterator($this->data);
    }

    public function count(): int
    {
        return count($this->data);
    }
}
