<?php

declare(strict_types=1);

use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\Processors\InterpolationProcessor;
use App\Framework\Logging\ValueObjects\LogContext;

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

    describe('constructor', function () {
        it('can be instantiated', function () {
            $processor = new InterpolationProcessor();

            expect($processor instanceof InterpolationProcessor)->toBeTrue();
        });
    });

    describe('getPriority()', function () {
        it('returns priority 20', function () {
            expect($this->processor->getPriority())->toBe(20);
        });
    });

    describe('getName()', function () {
        it('returns name interpolation', function () {
            expect($this->processor->getName())->toBe('interpolation');
        });
    });

    describe('processRecord()', function () {
        it('returns original record when no placeholders', function () {
            $record = new LogRecord(
                message: 'Simple message without placeholders',
                context: LogContext::empty(),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('Simple message without placeholders');
        });

        it('interpolates scalar values', function () {
            $record = new LogRecord(
                message: 'User {username} logged in from {ip}',
                context: LogContext::withData([
                    'username' => 'john_doe',
                    'ip' => '192.168.1.1'
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('User john_doe logged in from 192.168.1.1');
        });

        it('interpolates integer values', function () {
            $record = new LogRecord(
                message: 'Processing user ID {user_id}',
                context: LogContext::withData(['user_id' => 42]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('Processing user ID 42');
        });

        it('interpolates float values', function () {
            $record = new LogRecord(
                message: 'Temperature is {temp} degrees',
                context: LogContext::withData(['temp' => 23.5]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('Temperature is 23.5 degrees');
        });

        it('interpolates boolean values', function () {
            $record = new LogRecord(
                message: 'Status is {status}',
                context: LogContext::withData(['status' => true]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('Status is 1');
        });

        it('interpolates null values', function () {
            $record = new LogRecord(
                message: 'Value is {value}',
                context: LogContext::withData(['value' => null]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('Value is ');
        });

        it('formats throwables with file and line', function () {
            $exception = new RuntimeException('Test error', 500);

            $record = new LogRecord(
                message: 'Error occurred: {error}',
                context: LogContext::withData(['error' => $exception]),
                level: LogLevel::ERROR,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect(str_contains($processed->getMessage(), 'Test error'))->toBeTrue();
            expect(str_contains($processed->getMessage(), 'InterpolationProcessorTest.php'))->toBeTrue();
        });

        it('formats objects with __toString', function () {
            $object = new class {
                public function __toString(): string
                {
                    return 'CustomObject';
                }
            };

            $record = new LogRecord(
                message: 'Object: {obj}',
                context: LogContext::withData(['obj' => $object]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('Object: CustomObject');
        });

        it('formats objects without __toString', function () {
            $object = new stdClass();

            $record = new LogRecord(
                message: 'Object: {obj}',
                context: LogContext::withData(['obj' => $object]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect(str_contains($processed->getMessage(), '[object stdClass]'))->toBeTrue();
        });

        it('formats arrays as JSON', function () {
            $record = new LogRecord(
                message: 'Data: {data}',
                context: LogContext::withData([
                    'data' => ['key1' => 'value1', 'key2' => 'value2']
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect(str_contains($processed->getMessage(), 'array'))->toBeTrue();
            expect(str_contains($processed->getMessage(), 'key1'))->toBeTrue();
            expect(str_contains($processed->getMessage(), 'value1'))->toBeTrue();
        });

        it('handles multiple placeholders', function () {
            $record = new LogRecord(
                message: 'User {user} performed {action} on {resource}',
                context: LogContext::withData([
                    'user' => 'john_doe',
                    'action' => 'update',
                    'resource' => 'profile'
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('User john_doe performed update on profile');
        });

        it('ignores missing placeholders', function () {
            $record = new LogRecord(
                message: 'User {user} did {action}',
                context: LogContext::withData(['user' => 'john_doe']),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            expect($processed->getMessage())->toBe('User john_doe did {action}');
        });

        it('preserves context data', function () {
            $record = new LogRecord(
                message: 'User {user} logged in',
                context: LogContext::withData([
                    'user' => 'john_doe',
                    'ip' => '192.168.1.1'
                ]),
                level: LogLevel::INFO,
                timestamp: $this->timestamp
            );

            $processed = $this->processor->processRecord($record);

            $context = $processed->getContext();
            expect($context['user'])->toBe('john_doe');
            expect($context['ip'])->toBe('192.168.1.1');
        });
    });

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

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