<?php

declare(strict_types=1);

use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\ValueObjects\LogEntry;
use App\Framework\Logging\ValueObjects\LogName;
use App\Framework\Logging\ValueObjects\LogReadResult;
use App\Framework\Logging\ValueObjects\LogSearchResult;

describe('LogSearchResult', function () {
    beforeEach(function () {
        $this->logPath1 = FilePath::create('/tmp/app.log');
        $this->logPath2 = FilePath::create('/tmp/debug.log');

        $this->result1 = LogReadResult::fromEntries(
            logName: LogName::fromString('app_error'),
            logPath: $this->logPath1,
            entries: [
                LogEntry::fromParsedLine(
                    timestamp: '2024-01-15 10:30:45',
                    level: 'ERROR',
                    messageWithContext: 'Database connection failed',
                    raw: '[2024-01-15 10:30:45] local.ERROR: Database connection failed',
                    sourcePath: $this->logPath1
                ),
                LogEntry::fromParsedLine(
                    timestamp: '2024-01-15 10:31:00',
                    level: 'WARNING',
                    messageWithContext: 'Database slow query',
                    raw: '[2024-01-15 10:31:00] local.WARNING: Database slow query',
                    sourcePath: $this->logPath1
                ),
            ],
            limit: 100
        );

        $this->result2 = LogReadResult::fromEntries(
            logName: LogName::fromString('debug_log'),
            logPath: $this->logPath2,
            entries: [
                LogEntry::fromParsedLine(
                    timestamp: '2024-01-15 10:32:00',
                    level: 'INFO',
                    messageWithContext: 'Database query executed',
                    raw: '[2024-01-15 10:32:00] local.INFO: Database query executed',
                    sourcePath: $this->logPath2
                ),
            ],
            limit: 100
        );
    });

    it('creates from results', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        expect($searchResult->searchTerm)->toBe('database');
        expect($searchResult->results)->toHaveCount(2);
        expect($searchResult->totalMatches)->toBe(3);
        expect($searchResult->filesSearched)->toBe(2);
    });

    it('creates empty result', function () {
        $searchResult = LogSearchResult::empty('test');

        expect($searchResult->searchTerm)->toBe('test');
        expect($searchResult->results)->toBe([]);
        expect($searchResult->totalMatches)->toBe(0);
        expect($searchResult->filesSearched)->toBe(0);
        expect($searchResult->isEmpty())->toBeTrue();
    });

    it('checks for matches', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        expect($searchResult->hasMatches())->toBeTrue();
        expect($searchResult->isEmpty())->toBeFalse();
    });

    it('gets all entries', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $allEntries = $searchResult->getAllEntries();

        expect($allEntries)->toHaveCount(3);
        expect($allEntries[0])->toBeInstanceOf(LogEntry::class);
    });

    it('filters by log name', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $filtered = $searchResult->filterByLogName(LogName::fromString('app_error'));

        expect($filtered->results)->toHaveCount(1);
        expect($filtered->totalMatches)->toBe(2);
    });

    it('filters by minimum level', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $filtered = $searchResult->filterByMinimumLevel(LogLevel::WARNING);

        expect($filtered->totalMatches)->toBe(2); // ERROR and WARNING only
    });

    it('sorts by most recent', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $sorted = $searchResult->sortByMostRecent();

        expect($sorted->results)->toHaveCount(2);
        // Most recent should be first
        expect($sorted->results[0]->last()->message)->toContain('executed');
    });

    it('takes limited results', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $limited = $searchResult->take(1);

        expect($limited->results)->toHaveCount(1);
        expect($limited->totalMatches)->toBe(2);
        expect($limited->filesSearched)->toBe(1);
    });

    it('groups by level', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $grouped = $searchResult->groupByLevel();

        expect($grouped)->toBeArray();
        expect($grouped)->toHaveKey('ERROR');
        expect($grouped)->toHaveKey('WARNING');
        expect($grouped)->toHaveKey('INFO');
        expect($grouped['ERROR'])->toHaveCount(1);
        expect($grouped['WARNING'])->toHaveCount(1);
        expect($grouped['INFO'])->toHaveCount(1);
    });

    it('gets statistics', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $stats = $searchResult->getStatistics();

        expect($stats)->toBeArray();
        expect($stats)->toHaveKey('search_term');
        expect($stats)->toHaveKey('total_matches');
        expect($stats)->toHaveKey('files_searched');
        expect($stats)->toHaveKey('level_distribution');
        expect($stats)->toHaveKey('files_with_matches');

        expect($stats['search_term'])->toBe('database');
        expect($stats['total_matches'])->toBe(3);
        expect($stats['files_searched'])->toBe(2);
        expect($stats['level_distribution'])->toBeArray();
    });

    it('converts to array', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $array = $searchResult->toArray();

        expect($array)->toBeArray();
        expect($array)->toHaveKey('search_term');
        expect($array)->toHaveKey('results');
        expect($array)->toHaveKey('total_matches');
        expect($array)->toHaveKey('files_searched');
        expect($array)->toHaveKey('statistics');

        expect($array['results'])->toBeArray();
        expect($array['results'])->toHaveCount(2);
    });

    it('gets summary', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $summary = $searchResult->getSummary();

        expect($summary)->toBeString();
        expect($summary)->toContain('database');
        expect($summary)->toContain('3');
        expect($summary)->toContain('2');
    });

    it('gets empty summary', function () {
        $searchResult = LogSearchResult::empty('test');

        $summary = $searchResult->getSummary();

        expect($summary)->toBeString();
        expect($summary)->toContain('No matches');
        expect($summary)->toContain('test');
    });

    it('creates iterator', function () {
        $searchResult = LogSearchResult::fromResults(
            'database',
            [$this->result1, $this->result2]
        );

        $iterator = $searchResult->getIterator();

        expect($iterator)->toBeInstanceOf(\ArrayIterator::class);

        $count = 0;
        foreach ($iterator as $result) {
            expect($result)->toBeInstanceOf(LogReadResult::class);
            $count++;
        }

        expect($count)->toBe(2);
    });

    it('validates results array contains only LogReadResult', function () {
        new LogSearchResult(
            searchTerm: 'test',
            results: ['invalid'],
            totalMatches: 0,
            filesSearched: 0
        );
    })->throws(\InvalidArgumentException::class, 'All results must be LogReadResult instances');
});
