<?php

declare(strict_types=1);

namespace App\Framework\Logging;

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

/**
 * In-memory logger for testing
 *
 * Stores all log records in memory for inspection and assertions in tests.
 * Implements the Logger interface without any actual I/O operations.
 */
final class InMemoryLogger implements Logger
{
    /** @var LogRecord[] */
    private array $records = [];

    public function debug(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }

    public function info(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    public function notice(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    public function warning(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    public function error(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    public function critical(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    public function alert(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    public function emergency(string $message, ?LogContext $context = null): void
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    public function log(LogLevel $level, string $message, ?LogContext $context = null): void
    {
        $context = $context ?? LogContext::empty();

        $record = new LogRecord(
            message: $message,
            context: $context,
            level: $level,
            timestamp: new DateTimeImmutable()
        );

        $this->records[] = $record;
    }

    /**
     * Get all logged records
     *
     * @return LogRecord[]
     */
    public function getRecords(): array
    {
        return $this->records;
    }

    /**
     * Get records by log level
     *
     * @return LogRecord[]
     */
    public function getRecordsByLevel(LogLevel $level): array
    {
        return array_filter(
            $this->records,
            fn(LogRecord $record) => $record->level === $level
        );
    }

    /**
     * Check if a message was logged
     */
    public function hasMessage(string $message): bool
    {
        foreach ($this->records as $record) {
            if (str_contains($record->message, $message)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if a message was logged at specific level
     */
    public function hasMessageAtLevel(string $message, LogLevel $level): bool
    {
        foreach ($this->getRecordsByLevel($level) as $record) {
            if (str_contains($record->message, $message)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get number of logged records
     */
    public function count(): int
    {
        return count($this->records);
    }

    /**
     * Get number of records by level
     */
    public function countByLevel(LogLevel $level): int
    {
        return count($this->getRecordsByLevel($level));
    }

    /**
     * Clear all logged records
     */
    public function clear(): void
    {
        $this->records = [];
    }
}
