<?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;

describe('LogReadResult', function () {
    beforeEach(function () {
        $this->logName = LogName::fromString('app_error');
        $this->logPath = FilePath::create('/tmp/test.log');

        $this->entries = [
            LogEntry::fromParsedLine(
                timestamp: '2024-01-15 10:30:45',
                level: 'ERROR',
                messageWithContext: 'Database error',
                raw: '[2024-01-15 10:30:45] local.ERROR: Database error',
                sourcePath: $this->logPath
            ),
            LogEntry::fromParsedLine(
                timestamp: '2024-01-15 10:31:00',
                level: 'WARNING',
                messageWithContext: 'Cache miss',
                raw: '[2024-01-15 10:31:00] local.WARNING: Cache miss',
                sourcePath: $this->logPath
            ),
            LogEntry::fromParsedLine(
                timestamp: '2024-01-15 10:31:30',
                level: 'INFO',
                messageWithContext: 'User logged in',
                raw: '[2024-01-15 10:31:30] local.INFO: User logged in',
                sourcePath: $this->logPath
            ),
        ];
    });

    it('creates from entries', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        expect($result->logName)->toBe($this->logName);
        expect($result->logPath)->toBe($this->logPath);
        expect($result->entries)->toHaveCount(3);
        expect($result->totalEntries)->toBe(3);
        expect($result->limit)->toBe(100);
    });

    it('checks if empty', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: [],
            limit: 100
        );

        expect($result->isEmpty())->toBeTrue();
        expect($result->hasEntries())->toBeFalse();
    });

    it('filters by log level', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $filtered = $result->filterByLevel(LogLevel::ERROR);

        expect($filtered->entries)->toHaveCount(1);
        expect($filtered->entries[0]->level)->toBe(LogLevel::ERROR);
    });

    it('filters by search term', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $filtered = $result->filterBySearch('database');

        expect($filtered->entries)->toHaveCount(1);
        expect($filtered->entries[0]->message)->toContain('Database');
    });

    it('filters by minimum log level', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

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

        expect($filtered->entries)->toHaveCount(2); // ERROR and WARNING
        expect($filtered->first()->level)->toBe(LogLevel::ERROR);
        expect($filtered->last()->level)->toBe(LogLevel::WARNING);
    });

    it('gets first entry', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $first = $result->first();

        expect($first)->not->toBeNull();
        expect($first->level)->toBe(LogLevel::ERROR);
    });

    it('gets last entry', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $last = $result->last();

        expect($last)->not->toBeNull();
        expect($last->level)->toBe(LogLevel::INFO);
    });

    it('returns null for first/last on empty result', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: [],
            limit: 100
        );

        expect($result->first())->toBeNull();
        expect($result->last())->toBeNull();
    });

    it('takes limited entries', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $limited = $result->take(2);

        expect($limited->entries)->toHaveCount(2);
        expect($limited->totalEntries)->toBe(2);
    });

    it('skips entries', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $skipped = $result->skip(1);

        expect($skipped->entries)->toHaveCount(2);
        expect($skipped->first()->level)->toBe(LogLevel::WARNING);
    });

    it('reverses entry order', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $reversed = $result->reverse();

        expect($reversed->first()->level)->toBe(LogLevel::INFO);
        expect($reversed->last()->level)->toBe(LogLevel::ERROR);
    });

    it('gets level statistics', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $stats = $result->getLevelStatistics();

        expect($stats)->toBeArray();
        expect($stats['ERROR'])->toBe(1);
        expect($stats['WARNING'])->toBe(1);
        expect($stats['INFO'])->toBe(1);
    });

    it('gets metadata', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100,
            search: 'test',
            levelFilter: LogLevel::ERROR
        );

        $metadata = $result->getMetadata();

        expect($metadata)->toBeArray();
        expect($metadata['log_name'])->toBe('app_error');
        expect($metadata['total_entries'])->toBe(3);
        expect($metadata['limit'])->toBe(100);
        expect($metadata['search'])->toBe('test');
        expect($metadata['level_filter'])->toBe('ERROR');
    });

    it('converts to array', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $array = $result->toArray();

        expect($array)->toHaveKey('log_name');
        expect($array)->toHaveKey('log_path');
        expect($array)->toHaveKey('entries');
        expect($array)->toHaveKey('total_entries');
        expect($array)->toHaveKey('metadata');
        expect($array['entries'])->toBeArray();
        expect($array['entries'])->toHaveCount(3);
    });

    it('creates iterator', function () {
        $result = LogReadResult::fromEntries(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $this->entries,
            limit: 100
        );

        $iterator = $result->getIterator();

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

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

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