<?php

declare(strict_types=1);

use App\Framework\Logging\Handlers\SyslogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ValueObjects\LogContext;

describe('SyslogHandler', function () {
    beforeEach(function () {
        $this->timestamp = new DateTimeImmutable('2024-01-15 10:30:45', new DateTimeZone('Europe/Berlin'));
    });

    describe('constructor', function () {
        it('accepts custom ident and facility', function () {
            $handler = new SyslogHandler(
                ident: 'test-app',
                facility: LOG_LOCAL0,
                minLevel: LogLevel::WARNING
            );

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

        it('uses default values when not specified', function () {
            $handler = new SyslogHandler();

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

    describe('isHandling()', function () {
        it('returns true when record level is above minLevel', function () {
            $handler = new SyslogHandler(minLevel: LogLevel::WARNING);

            $record = new LogRecord(
                message: 'test message',
                context: LogContext::empty(),
                level: LogLevel::ERROR,
                timestamp: $this->timestamp
            );

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

        it('returns true when record level equals minLevel', function () {
            $handler = new SyslogHandler(minLevel: LogLevel::WARNING);

            $record = new LogRecord(
                message: 'test message',
                context: LogContext::empty(),
                level: LogLevel::WARNING,
                timestamp: $this->timestamp
            );

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

        it('returns false when record level is below minLevel', function () {
            $handler = new SyslogHandler(minLevel: LogLevel::ERROR);

            $record = new LogRecord(
                message: 'test message',
                context: LogContext::empty(),
                level: LogLevel::WARNING,
                timestamp: $this->timestamp
            );

            expect($handler->isHandling($record))->toBeFalse();
        });
    });

    describe('handle()', function () {
        it('handles log records without errors', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Test log message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            // Should not throw exception
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('handles records with request_id extra', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = (new LogRecord(
                message: 'Test message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            ))->addExtra('request_id', 'req-123');

            // Should not throw exception
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('handles records with channel', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Test message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp,
                channel: 'security'
            );

            // Should not throw exception
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('handles records with context data', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Test message',
                context: LogContext::withData(['user_id' => 123, 'action' => 'login']),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            // Should not throw exception
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('handles all log levels', function () {
            $handler = new SyslogHandler(ident: 'pest-test', minLevel: LogLevel::DEBUG);

            $levels = [
                LogLevel::DEBUG,
                LogLevel::INFO,
                LogLevel::NOTICE,
                LogLevel::WARNING,
                LogLevel::ERROR,
                LogLevel::CRITICAL,
                LogLevel::ALERT,
                LogLevel::EMERGENCY,
            ];

            foreach ($levels as $level) {
                $record = new LogRecord(
                    message: "{$level->getName()} message",
                    context: LogContext::empty(),
                    level: $level,
                    timestamp: $this->timestamp
                );

                // Should not throw exception
                $handler->handle($record);
            }

            expect(true)->toBeTrue();
        });

        it('handles records with empty context', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Message without context',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            // Should not throw exception
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('handles complex context data with special characters', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Test message',
                context: LogContext::withData([
                    'email' => 'test@example.com',
                    'path' => '/api/users/123',
                    'special' => "line1\nline2\ttabbed",
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            // Should not throw exception
            $handler->handle($record);
            expect(true)->toBeTrue();
        });
    });

    describe('syslog priority mapping', function () {
        it('correctly maps emergency level', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Emergency',
                context: LogContext::empty(),
                level: LogLevel::EMERGENCY,
                timestamp: $this->timestamp
            );

            // Should use LOG_EMERG priority internally
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('correctly maps alert level', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Alert',
                context: LogContext::empty(),
                level: LogLevel::ALERT,
                timestamp: $this->timestamp
            );

            // Should use LOG_ALERT priority internally
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('correctly maps critical level', function () {
            $handler = new SyslogHandler(ident: 'pest-test');

            $record = new LogRecord(
                message: 'Critical',
                context: LogContext::empty(),
                level: LogLevel::CRITICAL,
                timestamp: $this->timestamp
            );

            // Should use LOG_CRIT priority internally
            $handler->handle($record);
            expect(true)->toBeTrue();
        });
    });

    describe('syslog connection management', function () {
        it('opens syslog connection on first handle', function () {
            $handler = new SyslogHandler(ident: 'pest-test-connection');

            $record = new LogRecord(
                message: 'First message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            // Should open connection internally
            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('reuses open syslog connection for multiple records', function () {
            $handler = new SyslogHandler(ident: 'pest-test-reuse');

            $record1 = new LogRecord(
                message: 'First message',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $record2 = new LogRecord(
                message: 'Second message',
                context: LogContext::empty(),
                level: LogLevel::WARNING,
                timestamp: $this->timestamp
            );

            // Should reuse connection
            $handler->handle($record1);
            $handler->handle($record2);
            expect(true)->toBeTrue();
        });
    });

    describe('different facilities', function () {
        it('accepts LOG_USER facility', function () {
            $handler = new SyslogHandler(
                ident: 'pest-test',
                facility: LOG_USER
            );

            $record = new LogRecord(
                message: 'Test',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('accepts LOG_LOCAL0 facility', function () {
            $handler = new SyslogHandler(
                ident: 'pest-test',
                facility: LOG_LOCAL0
            );

            $record = new LogRecord(
                message: 'Test',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $handler->handle($record);
            expect(true)->toBeTrue();
        });

        it('accepts LOG_DAEMON facility', function () {
            $handler = new SyslogHandler(
                ident: 'pest-test',
                facility: LOG_DAEMON
            );

            $record = new LogRecord(
                message: 'Test',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $handler->handle($record);
            expect(true)->toBeTrue();
        });
    });
});
