<?php

declare(strict_types=1);

namespace App\Framework\Logging\Aggregation;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;

/**
 * Aggregated Log Entry
 *
 * Repräsentiert mehrere identische/ähnliche Log-Einträge als einen.
 */
final class AggregatedLogEntry
{
    private int $count = 1;
    private Timestamp $lastOccurrence;

    /**
     * @param string[] $exampleMessages
     */
    public function __construct(
        private readonly MessageFingerprint $fingerprint,
        private readonly LogRecord $firstRecord,
        private array $exampleMessages = []
    ) {
        $this->lastOccurrence = Timestamp::now();
        $this->exampleMessages[] = $firstRecord->message;
    }

    public function add(LogRecord $record): void
    {
        $this->count++;
        $this->lastOccurrence = Timestamp::now();

        // Sammle bis zu 5 Beispiel-Messages
        if (count($this->exampleMessages) < 5) {
            $this->exampleMessages[] = $record->message;
        }
    }

    public function getCount(): int
    {
        return $this->count;
    }

    public function getFingerprint(): MessageFingerprint
    {
        return $this->fingerprint;
    }

    public function getFirstRecord(): LogRecord
    {
        return $this->firstRecord;
    }

    public function getLastOccurrence(): Timestamp
    {
        return $this->lastOccurrence;
    }

    /**
     * @return string[]
     */
    public function getExampleMessages(): array
    {
        return $this->exampleMessages;
    }

    public function getLevel(): LogLevel
    {
        return $this->firstRecord->level;
    }

    public function getChannel(): string
    {
        return $this->firstRecord->channel;
    }

    /**
     * Erstellt aggregierten LogRecord für Ausgabe
     */
    public function toLogRecord(): LogRecord
    {
        $message = $this->count === 1
            ? $this->firstRecord->message
            : sprintf(
                '%s (occurred %dx in last %s)',
                $this->fingerprint->getNormalizedMessage(),
                $this->count,
                $this->getTimeSpan()
            );

        $context = $this->firstRecord->context->mergeData([
            'aggregated' => true,
            'occurrence_count' => $this->count,
            'first_occurrence' => $this->firstRecord->timestamp->format('c'),
            'last_occurrence' => $this->lastOccurrence->format('c'),
            'time_span_seconds' => Timestamp::now()->diffInSeconds($this->firstRecord->timestamp),
            'fingerprint' => $this->fingerprint->getHash(),
        ]);

        // Füge Beispiel-Messages hinzu wenn > 1
        if ($this->count > 1) {
            $context = $context->addData('example_messages', array_slice($this->exampleMessages, 0, 3));
        }

        return new LogRecord(
            level: $this->firstRecord->level,
            message: $message,
            channel: $this->firstRecord->channel,
            context: $context,
            timestamp: $this->lastOccurrence->toDateTimeImmutable()
        );
    }

    private function getTimeSpan(): string
    {
        $seconds = Timestamp::now()->diffInSeconds(
            Timestamp::fromDateTimeInterface($this->firstRecord->timestamp)
        );

        if ($seconds < 60) {
            return sprintf('%ds', $seconds);
        }

        if ($seconds < 3600) {
            return sprintf('%dm', (int) ($seconds / 60));
        }

        return sprintf('%dh', (int) ($seconds / 3600));
    }
}
