<?php

declare(strict_types=1);

use App\Framework\Database\QueryOptimization\Analysis\NPlusOneDetector;
use App\Framework\Database\QueryOptimization\ValueObjects\QueryLog;

describe('NPlusOneDetector', function () {
    it('detects N+1 pattern with multiple executions', function () {
        $detector = new NPlusOneDetector(minExecutionCount: 5, minSeverityScore: 4.0);

        $queryLogs = [];

        // 10 queries with same pattern
        for ($i = 1; $i <= 10; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 8.0,
                stackTrace: 'UserController::index',
                callerClass: 'UserController',
                callerMethod: 'index',
                callerLine: 42
            );
        }

        $detections = $detector->analyze($queryLogs);

        expect($detections)->toHaveCount(1);
        expect($detections[0]->pattern->getTableName())->toBe('posts');
        expect($detections[0]->pattern->getExecutionCount())->toBe(10);
    });

    it('ignores patterns with too few executions', function () {
        $detector = new NPlusOneDetector(minExecutionCount: 10);

        $queryLogs = [];

        // Only 5 queries
        for ($i = 1; $i <= 5; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 5.0,
                stackTrace: ''
            );
        }

        $detections = $detector->analyze($queryLogs);

        expect($detections)->toBeEmpty();
    });

    it('ignores patterns with low severity', function () {
        $detector = new NPlusOneDetector(minExecutionCount: 5, minSeverityScore: 8.0);

        $queryLogs = [];

        // 6 queries with low severity
        for ($i = 1; $i <= 6; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 1.0,
                stackTrace: '',
                callerClass: 'Different' . $i,
                callerMethod: 'method' . $i
            );
        }

        $detections = $detector->analyze($queryLogs);

        expect($detections)->toBeEmpty();
    });

    it('generates recommendations for detected N+1', function () {
        $detector = new NPlusOneDetector();

        $queryLogs = [];

        for ($i = 1; $i <= 10; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 5.0,
                stackTrace: 'UserController::index',
                callerClass: 'UserController',
                callerMethod: 'index',
                callerLine: 42
            );
        }

        $detections = $detector->analyze($queryLogs);

        expect($detections[0]->recommendation)->toContain('eager loading');
        expect($detections[0]->recommendation)->toContain('batch loading');
        expect($detections[0]->recommendation)->toContain('JOIN');
    });

    it('detects multiple N+1 patterns', function () {
        $detector = new NPlusOneDetector();

        $queryLogs = [];

        // Pattern 1: Posts
        for ($i = 1; $i <= 10; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 5.0,
                stackTrace: '',
                callerClass: 'UserController',
                callerMethod: 'index'
            );
        }

        // Pattern 2: Comments
        for ($i = 1; $i <= 8; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM comments WHERE post_id = ?',
                bindings: [$i],
                executionTimeMs: 3.0,
                stackTrace: '',
                callerClass: 'PostController',
                callerMethod: 'show'
            );
        }

        $detections = $detector->analyze($queryLogs);

        expect($detections)->toHaveCount(2);

        $tables = array_map(fn($d) => $d->pattern->getTableName(), $detections);
        expect($tables)->toContain('posts');
        expect($tables)->toContain('comments');
    });

    it('calculates statistics correctly', function () {
        $detector = new NPlusOneDetector();

        $queryLogs = [];

        // 10 N+1 queries
        for ($i = 1; $i <= 10; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 10.0,
                stackTrace: '',
                callerClass: 'UserController',
                callerMethod: 'index'
            );
        }

        // 5 normal queries
        for ($i = 1; $i <= 5; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'INSERT INTO logs (message) VALUES (?)',
                bindings: ["log{$i}"],
                executionTimeMs: 2.0,
                stackTrace: ''
            );
        }

        $stats = $detector->getStatistics($queryLogs);

        expect($stats['total_queries'])->toBe(15);
        expect($stats['n_plus_one_patterns'])->toBe(1);
        expect($stats['n_plus_one_queries'])->toBe(10);
        expect($stats['n_plus_one_time_ms'])->toBe(100.0);
        expect($stats['n_plus_one_percentage'])->toBeGreaterThan(50.0);
    });

    it('quick check detects N+1 problems', function () {
        $detector = new NPlusOneDetector();

        $queryLogs = [];

        for ($i = 1; $i <= 10; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 5.0,
                stackTrace: '',
                callerClass: 'UserController',
                callerMethod: 'index'
            );
        }

        expect($detector->hasNPlusOne($queryLogs))->toBeTrue();
    });

    it('quick check returns false for no N+1', function () {
        $detector = new NPlusOneDetector();

        $queryLogs = [
            new QueryLog('SELECT * FROM users', [], 5.0, ''),
            new QueryLog('SELECT * FROM posts WHERE id = ?', [1], 3.0, ''),
        ];

        expect($detector->hasNPlusOne($queryLogs))->toBeFalse();
    });

    it('generates metadata with query details', function () {
        $detector = new NPlusOneDetector();

        $queryLogs = [];

        for ($i = 1; $i <= 10; $i++) {
            $queryLogs[] = new QueryLog(
                sql: 'SELECT * FROM posts WHERE user_id = ?',
                bindings: [$i],
                executionTimeMs: 5.0,
                stackTrace: '',
                callerClass: 'UserController',
                callerMethod: 'index'
            );
        }

        $detections = $detector->analyze($queryLogs);

        expect($detections[0]->metadata)->toHaveKey('execution_count');
        expect($detections[0]->metadata)->toHaveKey('total_time_ms');
        expect($detections[0]->metadata)->toHaveKey('average_time_ms');
        expect($detections[0]->metadata)->toHaveKey('table_name');
        expect($detections[0]->metadata)->toHaveKey('first_query_sql');
    });
});
