<?php
/**
 * Bundled minimal Monolog StreamHandler.
 *
 * Writes records to a file. Uses LOCK_EX on each write to prevent corruption
 * from concurrent PHP-FPM workers writing to the same log file.
 *
 * Original: https://github.com/Seldaek/monolog (MIT)
 */

declare(strict_types=1);

namespace Monolog\Handler;

use Monolog\Formatter\JsonFormatter;
use Monolog\Level;
use RuntimeException;

final class StreamHandler implements HandlerInterface
{
    private ?JsonFormatter $formatter = null;

    public function __construct(
        private readonly string $stream,
        private readonly Level $level = Level::Debug,
    ) {
        $dir = dirname($stream);
        if (!is_dir($dir) && !@mkdir($dir, 0750, true) && !is_dir($dir)) {
            throw new RuntimeException("Cannot create log directory: {$dir}");
        }
    }

    public function setFormatter(JsonFormatter $formatter): self
    {
        $this->formatter = $formatter;
        return $this;
    }

    public function handle(array $record): void
    {
        if ($record['level'] < $this->level->value) {
            return;
        }

        $line = $this->formatter !== null
            ? $this->formatter->format($record)
            : json_encode($record, JSON_UNESCAPED_SLASHES) . "\n";

        // Append with file locking. We open with 'a' so writes are atomic at the
        // OS level for small writes; LOCK_EX adds belt-and-braces protection
        // against simultaneous appends from multiple PHP-FPM workers.
        $handle = @fopen($this->stream, 'ab');
        if ($handle === false) {
            // Last-resort: write to PHP error log so we don't lose the message.
            error_log('[blackred logger] cannot open ' . $this->stream . ': ' . $line);
            return;
        }
        try {
            if (flock($handle, LOCK_EX)) {
                fwrite($handle, $line);
                fflush($handle);
                flock($handle, LOCK_UN);
            }
        } finally {
            fclose($handle);
        }
    }
}
