<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Logging\Handlers\RotatingFileHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ValueObjects\LogContext;

describe('RotatingFileHandler', function () {
    beforeEach(function () {
        $this->testDir = sys_get_temp_dir() . '/rotating_handler_test_' . uniqid();
        mkdir($this->testDir, 0777, true);
        $this->testLogFile = $this->testDir . '/test.log';
    });

    afterEach(function () {
        // Clean up test files
        $files = glob($this->testDir . '/*');
        if ($files) {
            foreach ($files as $file) {
                if (is_file($file)) {
                    unlink($file);
                }
            }
        }
        if (is_dir($this->testDir)) {
            rmdir($this->testDir);
        }
    });

    describe('withSizeRotation()', function () {
        it('creates handler with size-based rotation', function () {
            $handler = RotatingFileHandler::withSizeRotation(
                $this->testLogFile,
                maxFileSize: Byte::fromKilobytes(1),
                maxFiles: 3
            );

            expect($handler)->toBeInstanceOf(RotatingFileHandler::class);

            // Write small log
            $record = new LogRecord(
                message: 'Test message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );

            $handler->handle($record);

            expect(file_exists($this->testLogFile))->toBeTrue();
        });

        it('rotates log when size limit exceeded', function () {
            $handler = RotatingFileHandler::withSizeRotation(
                $this->testLogFile,
                maxFileSize: Byte::fromBytes(100), // Very small size
                maxFiles: 3
            );

            // Write multiple large messages to exceed size
            for ($i = 0; $i < 10; $i++) {
                $record = new LogRecord(
                    message: str_repeat('X', 50), // 50 bytes per message
                    context: LogContext::empty(),
                    level: LogLevel::INFO,
                    timestamp: new DateTimeImmutable()
                );

                $handler->handle($record);
            }

            // Check that rotation happened (should have .1 file)
            expect(file_exists($this->testLogFile . '.1'))->toBeTrue();
        });
    });

    describe('daily()', function () {
        it('creates handler with daily rotation strategy', function () {
            $handler = RotatingFileHandler::daily($this->testLogFile);

            expect($handler)->toBeInstanceOf(RotatingFileHandler::class);

            $strategy = $handler->getRotationStrategy();
            expect($strategy['time_based'])->toBe('daily');
            expect($strategy['size_based'])->toBeTrue();
        });

        it('rotates log when file is from previous day', function () {
            // Create old log file
            file_put_contents($this->testLogFile, "Old log content\n");

            // Set file modification time to yesterday
            touch($this->testLogFile, strtotime('yesterday'));

            $handler = RotatingFileHandler::daily($this->testLogFile);

            // Write new log
            $record = new LogRecord(
                message: 'New log entry',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );

            $handler->handle($record);

            // File should have been rotated
            expect(file_exists($this->testLogFile . '.1'))->toBeTrue();

            // New file should contain only new entry
            $content = file_get_contents($this->testLogFile);
            expect($content)->toContain('New log entry');
            expect($content)->not->toContain('Old log content');
        });

        it('does not rotate log when file is from today', function () {
            // Create log file
            file_put_contents($this->testLogFile, "Today's log\n");

            $handler = RotatingFileHandler::daily($this->testLogFile);

            // Write new log
            $record = new LogRecord(
                message: 'Another entry',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );

            $handler->handle($record);

            // No rotation should have occurred
            expect(file_exists($this->testLogFile . '.1'))->toBeFalse();

            // File should contain both entries
            $content = file_get_contents($this->testLogFile);
            expect($content)->toContain("Today's log");
            expect($content)->toContain('Another entry');
        });
    });

    describe('weekly()', function () {
        it('creates handler with weekly rotation strategy', function () {
            $handler = RotatingFileHandler::weekly($this->testLogFile);

            $strategy = $handler->getRotationStrategy();
            expect($strategy['time_based'])->toBe('weekly');
        });

        it('rotates log when file is from previous week', function () {
            // Create old log file
            file_put_contents($this->testLogFile, "Old week log\n");

            // Set file modification time to last week
            touch($this->testLogFile, strtotime('last week'));

            $handler = RotatingFileHandler::weekly($this->testLogFile);

            // Write new log
            $record = new LogRecord(
                message: 'New week entry',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );

            $handler->handle($record);

            // File should have been rotated
            expect(file_exists($this->testLogFile . '.1'))->toBeTrue();
        });
    });

    describe('monthly()', function () {
        it('creates handler with monthly rotation strategy', function () {
            $handler = RotatingFileHandler::monthly($this->testLogFile);

            $strategy = $handler->getRotationStrategy();
            expect($strategy['time_based'])->toBe('monthly');
        });

        it('rotates log when file is from previous month', function () {
            // Create old log file
            file_put_contents($this->testLogFile, "Old month log\n");

            // Set file modification time to last month
            touch($this->testLogFile, strtotime('last month'));

            $handler = RotatingFileHandler::monthly($this->testLogFile);

            // Write new log
            $record = new LogRecord(
                message: 'New month entry',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );

            $handler->handle($record);

            // File should have been rotated
            expect(file_exists($this->testLogFile . '.1'))->toBeTrue();
        });
    });

    describe('production()', function () {
        it('creates handler with production-optimized settings', function () {
            $handler = RotatingFileHandler::production($this->testLogFile);

            expect($handler)->toBeInstanceOf(RotatingFileHandler::class);

            $strategy = $handler->getRotationStrategy();
            expect($strategy['time_based'])->toBe('daily');
            expect($strategy['size_based'])->toBeTrue();
        });

        it('uses INFO as default level for production', function () {
            $handler = RotatingFileHandler::production($this->testLogFile);

            // DEBUG should not be handled
            $debugRecord = new LogRecord(
                message: 'Debug message',
                context: LogContext::empty(),
                level: LogLevel::DEBUG,
                timestamp: new DateTimeImmutable()
            );

            expect($handler->isHandling($debugRecord))->toBeFalse();

            // INFO should be handled
            $infoRecord = new LogRecord(
                message: 'Info message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );

            expect($handler->isHandling($infoRecord))->toBeTrue();
        });
    });

    describe('withMaxSize()', function () {
        it('allows configuring max size after creation', function () {
            $handler = RotatingFileHandler::daily($this->testLogFile);

            $handler->withMaxSize(Byte::fromBytes(50), maxFiles: 5);

            expect($handler)->toBeInstanceOf(RotatingFileHandler::class);
        });
    });

    describe('getRotationStrategy()', function () {
        it('returns rotation strategy info', function () {
            $handler = RotatingFileHandler::daily($this->testLogFile);

            $strategy = $handler->getRotationStrategy();

            expect($strategy)->toHaveKeys(['time_based', 'size_based', 'last_check']);
            expect($strategy['time_based'])->toBe('daily');
            expect($strategy['size_based'])->toBeTrue();
        });

        it('returns none for size-only rotation', function () {
            $handler = RotatingFileHandler::withSizeRotation($this->testLogFile);

            $strategy = $handler->getRotationStrategy();

            expect($strategy['time_based'])->toBe('none');
        });
    });

    describe('caching behavior', function () {
        it('caches rotation checks for performance', function () {
            $handler = RotatingFileHandler::daily($this->testLogFile);

            // First write
            $record1 = new LogRecord(
                message: 'First message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );
            $handler->handle($record1);

            $strategy1 = $handler->getRotationStrategy();
            $lastCheck1 = $strategy1['last_check'];

            // Immediate second write (within cache window)
            $record2 = new LogRecord(
                message: 'Second message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: new DateTimeImmutable()
            );
            $handler->handle($record2);

            $strategy2 = $handler->getRotationStrategy();
            $lastCheck2 = $strategy2['last_check'];

            // Last check should be same (cached)
            expect($lastCheck1)->toBe($lastCheck2);
        });
    });
});
