<?php

namespace NoahVet\Reef\Log\Formater;

use NoahVet\Reef\Domain\Log\StringSanitizer;
use NoahVet\Reef\Domain\Log\SyslogMessageInterface;
use Psr\Clock\ClockInterface;

class Rfc5424Formater implements LogFormaterInterface
{
    public function __construct(
        protected readonly string $reefAppName,
        protected readonly ClockInterface $clock,
    ) {
    }

    #[\Override]
    public function format(SyslogMessageInterface $syslogMessage): string
    {
        $ret = \sprintf(
            '%s %s',
            $this->generateHeader(),
            $this->generateStructuredData($syslogMessage),
        );

        $textMessage = $syslogMessage->getMessage();
        if (null !== $textMessage) {
            $ret .= ' '.$textMessage;
        }

        return $ret;
    }

    protected function generateHeader(): string
    {
        // Local0
        $facility = 128;

        // Notice (for audit log)
        $severity = 5;

        $hostName = \gethostname();
        if (false === $hostName) {
            $hostName = '';
        }

        $pid = \getmypid();

        return \sprintf(
            '<%d>1 %s %s %s %s -',
            $facility + $severity,
            $this->clock->now()->format(\DateTimeInterface::ATOM),
            $_SERVER['REMOTE_ADDR'] ?? $hostName,
            $this->reefAppName,
            false === $pid ? '-' : (string) $pid,
        );
    }

    protected function generateStructuredData(SyslogMessageInterface $syslogMessage): string
    {
        $extraFields = $syslogMessage->getExtraData();
        if ($extraFields) {
            $extraData = \sprintf(
                '[%s %s]',
                'e',
                \implode(
                    ' ',
                    $this->generateParams(
                        $extraFields,
                    ),
                ),
            );
        } else {
            $extraData = '';
        }

        return \sprintf(
            '[%s %s]',
            'a',
            \implode(
                ' ',
                $this->generateParams(
                    [
                        'action' => $syslogMessage->getAction(),
                        'event_type' => $syslogMessage->getEventType(),
                        'principal_id' => $syslogMessage->getPrincipalId(),
                        'resource_type' => $syslogMessage->getResourceType(),
                        'resource_id' => $syslogMessage->getResourceId(),
                        'version' => $syslogMessage->getVersion(),
                    ],
                ),
            ),
        ).$extraData;
    }

    /**
     * @param array<string, string|null> $params
     *
     * @return list<string>
     */
    protected function generateParams(array $params): array
    {
        $ret = [];

        foreach ($params as $key => $value) {
            if (null === $value) {
                continue;
            }

            $ret[] = \sprintf('%s="%s"', $key, StringSanitizer::sanitize($value));
        }

        return $ret;
    }
}
