<?php
declare(strict_types=1);

namespace CloudXM\SDK\Transport;

use Monolog\Handler\AbstractHandler;
use Monolog\Handler\HandlerInterface;
use Monolog\LogRecord;
use CloudXM\SDK\Contracts\EventDispatcherInterface;

/**
 * CloudXM Logger Sink Handler
 *
 * When sink is not provided, logs use unified routing (plugin-level rules apply).
 * When sink is provided, we follow its specific logic for routing.
 */
final class ConditionalSinkHandler extends AbstractHandler
{
    /** @var array<string, HandlerInterface> */
    private array $sinks = [];

    /** @var string */
    private string $defaultSinkName;

    /** @var EventDispatcherInterface|null */
    private ?EventDispatcherInterface $eventDispatcher = null;

    /**
     * @param HandlerInterface $defaultSink The handler used when no/unknown sink is requested (usually the file handler)
     * @param string           $defaultSinkName Logical name for the default sink (defaults to 'internal')
     * @param EventDispatcherInterface|null $eventDispatcher Event dispatcher for decoupled filtering
     */
    public function __construct(
        HandlerInterface $defaultSink,
        string $defaultSinkName = 'internal',
        ?EventDispatcherInterface $eventDispatcher = null
    ) {
        parent::__construct(); // uses default level/bubble
        $this->defaultSinkName = $defaultSinkName;
        $this->sinks[$defaultSinkName] = $defaultSink;
        $this->eventDispatcher = $eventDispatcher;
    }

    /**
     * Register/override a named sink.
     */
    public function addSink(string $name, HandlerInterface $handler): void
    {
        $this->sinks[$name] = $handler;
    }

    /**
     * Route a single record to the appropriate sink.
     *
     * SDK's job: Capture logs and emit events. That's it.
     * Consumers (like Logger plugin) decide what to do with the logs.
     */
    public function handle(LogRecord $record): bool
    {
        $sinkName = $this->extractSinkName($record) ?? $this->defaultSinkName;

        // Always write to file handler first (internal sink)
        $internalSink = $this->sinks[$this->defaultSinkName] ?? null;
        if ($internalSink && $internalSink->isHandling($record)) {
            $internalSink->handle($record);
        }

        // Dispatch LogCaptureEvent for enrichment
        if ($this->eventDispatcher) {
            $captureEvent = new \CloudXM\SDK\Events\LogCaptureEvent($record);
            $this->eventDispatcher->dispatch($captureEvent);
            $record = $captureEvent->getRecord();
        }

        // Emit log captured event - let consumers decide what to do
        // (Logger plugin will listen to this and send to Grafana if needed)
        if ($this->eventDispatcher) {
            $logEvent = new \CloudXM\SDK\Events\LogEmittedEvent($record);
            $this->eventDispatcher->dispatch($logEvent);
        }

        return false; // Don't stop bubbling
    }

    /**
     * Route a batch of records. Each record is evaluated independently.
     */
    public function handleBatch(array $records): void
    {
        foreach ($records as $record) {
            if ($record instanceof LogRecord) {
                $this->handle($record);
            }
        }
    }

    /**
     * Pull the 'sink' name from the record context if present and stringy.
     * If sink is provided, follow its logic; otherwise use unified routing.
     */
    private function extractSinkName(LogRecord $record): ?string
    {
        $sink = $record->context['sink'] ?? null;
        return is_string($sink) ? $sink : null;
    }

}