<?php

declare(strict_types=1);

namespace App\Framework\Logging;

use App\Framework\Logging\ValueObjects\LogContext;
use DateTimeImmutable;

/**
 * Immutable Log Record following framework principles.
 *
 * Repräsentiert einen einzelnen Log-Eintrag mit allen relevanten Informationen.
 * Alle Modifikationen erzeugen neue Instanzen (Copy-on-Write Pattern).
 */
final readonly class LogRecord
{
    /**
     * Erstellt einen neuen Log-Eintrag
     *
     * @param array<string, mixed> $extra Zusätzliche Daten für Processors
     */
    public function __construct(
        public string $message,
        public LogContext $context,
        public LogLevel $level,
        public DateTimeImmutable $timestamp,
        public ?string $channel = null,
        public array $extra = []
    ) {
    }

    /**
     * Erstellt neuen LogRecord mit geänderter Nachricht
     */
    public function withMessage(string $message): self
    {
        return new self(
            message: $message,
            context: $this->context,
            level: $this->level,
            timestamp: $this->timestamp,
            channel: $this->channel,
            extra: $this->extra
        );
    }

    /**
     * Erstellt neuen LogRecord mit geändertem Kanal
     */
    public function withChannel(string $channel): self
    {
        return new self(
            message: $this->message,
            context: $this->context,
            level: $this->level,
            timestamp: $this->timestamp,
            channel: $channel,
            extra: $this->extra
        );
    }

    /**
     * Erstellt neuen LogRecord mit zusätzlichem Extra-Wert
     */
    public function withExtra(string $key, mixed $value): self
    {
        return new self(
            message: $this->message,
            context: $this->context,
            level: $this->level,
            timestamp: $this->timestamp,
            channel: $this->channel,
            extra: array_merge($this->extra, [$key => $value])
        );
    }

    /**
     * Erstellt neuen LogRecord mit zusätzlichen Extra-Werten
     */
    public function withExtras(array $extras): self
    {
        return new self(
            message: $this->message,
            context: $this->context,
            level: $this->level,
            timestamp: $this->timestamp,
            channel: $this->channel,
            extra: array_merge($this->extra, $extras)
        );
    }

    /**
     * Erstellt neuen LogRecord mit erweitertem Context
     */
    public function withContext(LogContext $context): self
    {
        return new self(
            message: $this->message,
            context: $context,
            level: $this->level,
            timestamp: $this->timestamp,
            channel: $this->channel,
            extra: $this->extra
        );
    }

    /**
     * Gibt die Log-Nachricht zurück
     *
     * @deprecated Use public property $message directly
     */
    public function getMessage(): string
    {
        return $this->message;
    }

    /**
     * Gibt das Log-Level zurück
     *
     * @deprecated Use public property $level directly
     */
    public function getLevel(): LogLevel
    {
        return $this->level;
    }

    /**
     * Gibt den Zeitstempel zurück
     *
     * @deprecated Use public property $timestamp directly
     */
    public function getTimestamp(): DateTimeImmutable
    {
        return $this->timestamp;
    }

    /**
     * Gibt den formattierten Zeitstempel zurück
     */
    public function getFormattedTimestamp(string $format = 'Y-m-d H:i:s'): string
    {
        return $this->timestamp->format($format);
    }

    /**
     * Gibt den Kanal zurück, falls gesetzt
     *
     * @deprecated Use public property $channel directly
     */
    public function getChannel(): ?string
    {
        return $this->channel;
    }

    /**
     * Gibt den Kontext zurück als flaches Array für Formatters
     */
    public function getContext(): array
    {
        $result = $this->context->structured;

        // Tags hinzufügen
        if ($this->context->hasTags()) {
            $result['_tags'] = $this->context->tags;
        }

        // Trace-Informationen hinzufügen
        if ($this->context->trace !== null) {
            $result['_trace_id'] = $this->context->trace->getTraceId();
            if ($activeSpan = $this->context->trace->getActiveSpan()) {
                $result['_span_id'] = $activeSpan->spanId;
            }
        }

        // User-Kontext hinzufügen
        if ($this->context->user !== null) {
            $result['_user_id'] = $this->context->user->userId ?? null;
        }

        // Request-Kontext hinzufügen
        if ($this->context->request !== null) {
            $result['_request_id'] = $this->context->request->requestId ?? null;
        }

        return array_merge($result, $this->context->metadata);
    }

    /**
     * Prüft, ob ein bestimmter Extra-Wert existiert
     */
    public function hasExtra(string $key): bool
    {
        return array_key_exists($key, $this->extra);
    }

    /**
     * Gibt einen Extra-Wert zurück
     */
    public function getExtra(string $key, mixed $default = null): mixed
    {
        return $this->extra[$key] ?? $default;
    }

    /**
     * Gibt alle Extra-Daten zurück
     *
     * @deprecated Use public property $extra directly
     */
    public function getExtras(): array
    {
        return $this->extra;
    }

    /**
     * Erstellt ein Array mit allen Log-Daten
     */
    public function toArray(): array
    {
        return [
            'message' => $this->message,
            'context' => $this->context->toArray(),
            'level' => $this->level->value,
            'level_name' => $this->level->getName(),
            'timestamp' => $this->getFormattedTimestamp(),
            'datetime' => $this->timestamp,
            'channel' => $this->channel,
            'extra' => $this->extra,
        ];
    }

    // ========================================
    // DEPRECATED: Mutable API (BC Layer)
    // ========================================

    /**
     * @deprecated Use withExtra() instead - returns new instance
     */
    public function addExtra(string $key, mixed $value): self
    {
        return $this->withExtra($key, $value);
    }

    /**
     * @deprecated Use withExtras() instead - returns new instance
     */
    public function addExtras(array $extras): self
    {
        return $this->withExtras($extras);
    }
}
