<?php

declare(strict_types=1);

use App\Framework\Logging\Formatter\LineFormatter;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ValueObjects\LogContext;

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

    describe('constructor', function () {
        it('accepts default format and timestamp format', function () {
            $formatter = new LineFormatter();

            expect($formatter instanceof LineFormatter)->toBeTrue();
        });

        it('accepts custom format', function () {
            $formatter = new LineFormatter(
                format: '{level}: {message}'
            );

            expect($formatter instanceof LineFormatter)->toBeTrue();
        });

        it('accepts custom timestamp format', function () {
            $formatter = new LineFormatter(
                format: '[{timestamp}] {message}',
                timestampFormat: 'H:i:s'
            );

            expect($formatter instanceof LineFormatter)->toBeTrue();
        });
    });

    describe('default format', function () {
        it('formats with default format string', function () {
            $formatter = new LineFormatter();

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

            $result = $formatter($record);

            expect($result)->toBeString();
            expect($result)->toContain('2024-01-15');
            expect($result)->toContain('INFO');
            expect($result)->toContain('Test message');
        });

        it('includes channel in output', function () {
            $formatter = new LineFormatter();

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

            $result = $formatter($record);

            expect($result)->toContain('security');
        });

        it('uses default channel "app" when none provided', function () {
            $formatter = new LineFormatter();

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

            $result = $formatter($record);

            expect($result)->toContain('app');
        });

        it('includes context as JSON', function () {
            $formatter = new LineFormatter();

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

            $result = $formatter($record);

            expect($result)->toContain('"user_id":123');
            expect($result)->toContain('"action":"login"');
        });

        it('handles empty context', function () {
            $formatter = new LineFormatter();

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

            $result = $formatter($record);

            // Empty context should result in empty context string
            expect($result)->toBeString();
            expect($result)->toContain('Test message');
        });
    });

    describe('custom format', function () {
        it('uses custom format string', function () {
            $formatter = new LineFormatter(
                format: '{level}: {message}'
            );

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

            $result = $formatter($record);

            expect($result)->toBe('WARNING: Custom format');
        });

        it('supports timestamp-only format', function () {
            $formatter = new LineFormatter(
                format: '[{timestamp}]',
                timestampFormat: 'Y-m-d H:i:s'
            );

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

            $result = $formatter($record);

            expect($result)->toBe('[2024-01-15 10:30:45]');
        });

        it('supports message-only format', function () {
            $formatter = new LineFormatter(
                format: '{message}'
            );

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

            $result = $formatter($record);

            expect($result)->toBe('Simple message');
        });

        it('supports complex custom format', function () {
            $formatter = new LineFormatter(
                format: '[{timestamp}] {channel}.{level}: {message} {context}'
            );

            $record = new LogRecord(
                message: 'Complex',
                context: LogContext::withData(['key' => 'value']),
                level: LogLevel::ERROR,
                timestamp: $this->timestamp,
                channel: 'api'
            );

            $result = $formatter($record);

            expect($result)->toContain('[2024-01-15');
            expect($result)->toContain('api.ERROR');
            expect($result)->toContain('Complex');
            expect($result)->toContain('"key":"value"');
        });
    });

    describe('timestamp formatting', function () {
        it('uses default timestamp format', function () {
            $formatter = new LineFormatter(
                format: '{timestamp}'
            );

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

            $result = $formatter($record);

            // Default format is Y-m-d H:i:s
            expect($result)->toBe('2024-01-15 10:30:45');
        });

        it('uses custom timestamp format', function () {
            $formatter = new LineFormatter(
                format: '{timestamp}',
                timestampFormat: 'd/m/Y H:i'
            );

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

            $result = $formatter($record);

            expect($result)->toBe('15/01/2024 10:30');
        });

        it('supports ISO 8601 timestamp format', function () {
            $formatter = new LineFormatter(
                format: '{timestamp}',
                timestampFormat: 'c'
            );

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

            $result = $formatter($record);

            expect($result)->toContain('2024-01-15T10:30:45');
        });

        it('supports microseconds in timestamp', function () {
            $formatter = new LineFormatter(
                format: '{timestamp}',
                timestampFormat: 'Y-m-d H:i:s.u'
            );

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

            $result = $formatter($record);

            expect($result)->toContain('2024-01-15 10:30:45.');
        });
    });

    describe('context handling', function () {
        it('formats context as JSON with unescaped slashes', function () {
            $formatter = new LineFormatter(
                format: '{context}'
            );

            $record = new LogRecord(
                message: 'Message',
                context: LogContext::withData([
                    'url' => 'https://example.com/path',
                    'file' => '/var/www/html/index.php'
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $result = $formatter($record);

            // JSON_UNESCAPED_SLASHES should be used
            expect($result)->toContain('https://example.com/path');
            expect($result)->toContain('/var/www/html/index.php');
            // Slashes should not be escaped
            expect(str_contains($result, '\\/'))->toBeFalse();
        });

        it('combines context and extras', function () {
            $formatter = new LineFormatter(
                format: '{context}'
            );

            $record = (new LogRecord(
                message: 'Message',
                context: LogContext::withData(['context_key' => 'context_value']),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            ))->addExtra('extra_key', 'extra_value');

            $result = $formatter($record);

            expect($result)->toContain('"context_key":"context_value"');
            expect($result)->toContain('"extra_key":"extra_value"');
        });

        it('handles nested context arrays', function () {
            $formatter = new LineFormatter(
                format: '{context}'
            );

            $record = new LogRecord(
                message: 'Message',
                context: LogContext::withData([
                    'user' => [
                        'id' => 123,
                        'name' => 'John Doe',
                        'roles' => ['admin', 'user']
                    ]
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $result = $formatter($record);

            expect($result)->toContain('"user"');
            expect($result)->toContain('"id":123');
            expect($result)->toContain('"name":"John Doe"');
            expect($result)->toContain('["admin","user"]');
        });

        it('handles special characters in context', function () {
            $formatter = new LineFormatter(
                format: '{context}'
            );

            $record = new LogRecord(
                message: 'Message',
                context: LogContext::withData([
                    'text' => 'Special chars: "quotes", \'apostrophes\', \\backslashes'
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $result = $formatter($record);

            expect($result)->toBeString();
            expect($result)->toContain('Special chars');
        });
    });

    describe('log levels', function () {
        it('formats DEBUG level', function () {
            $formatter = new LineFormatter(format: '{level}');

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

            expect($formatter($record))->toBe('DEBUG');
        });

        it('formats INFO level', function () {
            $formatter = new LineFormatter(format: '{level}');

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

            expect($formatter($record))->toBe('INFO');
        });

        it('formats WARNING level', function () {
            $formatter = new LineFormatter(format: '{level}');

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

            expect($formatter($record))->toBe('WARNING');
        });

        it('formats ERROR level', function () {
            $formatter = new LineFormatter(format: '{level}');

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

            expect($formatter($record))->toBe('ERROR');
        });

        it('formats CRITICAL level', function () {
            $formatter = new LineFormatter(format: '{level}');

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

            expect($formatter($record))->toBe('CRITICAL');
        });

        it('formats ALERT level', function () {
            $formatter = new LineFormatter(format: '{level}');

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

            expect($formatter($record))->toBe('ALERT');
        });

        it('formats EMERGENCY level', function () {
            $formatter = new LineFormatter(format: '{level}');

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

            expect($formatter($record))->toBe('EMERGENCY');
        });
    });

    describe('readonly behavior', function () {
        it('is a readonly class', function () {
            $reflection = new ReflectionClass(LineFormatter::class);

            expect($reflection->isReadOnly())->toBeTrue();
        });
    });

    describe('edge cases', function () {
        it('handles empty message', function () {
            $formatter = new LineFormatter(format: '{message}');

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

            expect($formatter($record))->toBe('');
        });

        it('handles very long message', function () {
            $formatter = new LineFormatter(format: '{message}');

            $longMessage = str_repeat('x', 10000);

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

            $result = $formatter($record);

            expect($result)->toHaveLength(10000);
        });

        it('handles unicode characters in message', function () {
            $formatter = new LineFormatter(format: '{message}');

            $record = new LogRecord(
                message: 'Unicode: 你好 мир 🌍',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $result = $formatter($record);

            expect($result)->toContain('你好');
            expect($result)->toContain('мир');
            expect($result)->toContain('🌍');
        });

        it('handles null channel gracefully', function () {
            $formatter = new LineFormatter(format: '{channel}');

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

            $result = $formatter($record);

            expect($result)->toBe('app'); // Default channel
        });

        it('handles format with no placeholders', function () {
            $formatter = new LineFormatter(format: 'static text');

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

            expect($formatter($record))->toBe('static text');
        });
    });
});
