<?php

declare(strict_types=1);

namespace App\Framework\Logging\Formatter;

use App\Framework\Logging\LogRecord;

/**
 * Structured Formatter für Log-Aggregation und Analysis
 */
final readonly class StructuredFormatter implements LogFormatter
{
    public function __construct(
        private string $format = 'logfmt', // 'logfmt' or 'kv'
        private bool $includeTimestamp = true
    ) {
    }

    public function __invoke(LogRecord $record): string|array
    {
        $data = $this->extractStructuredData($record);

        return match($this->format) {
            'logfmt' => $this->formatAsLogfmt($data),
            'kv' => $this->formatAsKeyValue($data),
            'array' => $data, // Return raw array for further processing
            default => $this->formatAsLogfmt($data)
        };
    }

    private function extractStructuredData(LogRecord $record): array
    {
        $data = [];

        if ($this->includeTimestamp) {
            $data['ts'] = $record->getTimestamp()->format('c');
        }

        $data['level'] = $record->getLevel()->getName();
        $data['msg'] = $record->getMessage();

        if ($record->getChannel()) {
            $data['channel'] = $record->getChannel();
        }

        // Flatten context
        $context = $record->getContext();
        foreach ($context as $key => $value) {
            $data[$this->sanitizeKey($key)] = $this->sanitizeValue($value);
        }

        // Add extras
        $extras = $record->getExtras();
        foreach ($extras as $key => $value) {
            $data[$this->sanitizeKey($key)] = $this->sanitizeValue($value);
        }

        return $data;
    }

    private function formatAsLogfmt(array $data): string
    {
        $pairs = [];

        foreach ($data as $key => $value) {
            if (is_string($value) && (str_contains($value, ' ') || str_contains($value, '='))) {
                $value = '"' . addslashes($value) . '"';
            }

            $pairs[] = "{$key}={$value}";
        }

        return implode(' ', $pairs);
    }

    private function formatAsKeyValue(array $data): string
    {
        $pairs = [];

        foreach ($data as $key => $value) {
            $pairs[] = "{$key}: {$value}";
        }

        return implode(', ', $pairs);
    }

    private function sanitizeKey(string $key): string
    {
        // Remove internal prefixes and make key aggregation-friendly
        $key = ltrim($key, '_');

        return preg_replace('/[^a-zA-Z0-9_]/', '_', $key);
    }

    private function sanitizeValue(mixed $value): string
    {
        return match(true) {
            is_string($value) => $value,
            is_numeric($value) => (string) $value,
            is_bool($value) => $value ? 'true' : 'false',
            is_null($value) => 'null',
            is_array($value) => json_encode($value, JSON_UNESCAPED_SLASHES),
            is_object($value) => get_class($value),
            default => 'unknown'
        };
    }
}
