<?php
declare(strict_types=1);

namespace App\Application\Security;

use App\Application\Security\ValueObjects\{SecurityContext as SecurityContextVO, RequestContext};
use App\Framework\Core\Events\OnEvent;
use Psr\Log\LoggerInterface;
use Throwable;

final class SecurityEventLogger
{
    public function __construct(
        private LoggerInterface $logger,
        private ?OWASPSecurityEventFactory $eventFactory = null
    ) {
        $this->eventFactory ??= new OWASPSecurityEventFactory();
    }

    #[OnEvent]
    public function log(SecurityEvent $event): void
    {
        try {
            // OWASP-konforme Logging
            $this->logOWASPFormat($event);

            // Fallback: Ursprüngliches Format beibehalten
            $this->logLegacyFormat($event);

        } catch (Throwable $e) {
            // Fallback logging - niemals Security-Events verlieren
            $this->logger->critical('AUDIT_audit_failure:security_logger', [
                'datetime' => date('c'),
                'appid' => 'app.security',
                'event' => 'AUDIT_audit_failure:security_logger',
                'level' => 'FATAL',
                'description' => 'Security event logging failed: ' . $e->getMessage(),
                'original_event_type' => $event->type->value ?? 'UNKNOWN',
                'error_trace' => $e->getTraceAsString()
            ]);
        }
    }

    private function logOWASPFormat(SecurityEvent $event): void
    {
        $securityContext = SecurityContextVO::fromGlobals();
        $requestContext = RequestContext::fromGlobals();

        $owaspLogFormat = $this->eventFactory->createFromSecurityEvent(
            $event,
            $securityContext,
            $requestContext
        );

        // Als strukturiertes JSON loggen
        $this->logger->info($owaspLogFormat->getDescription(), [
            'owasp_format' => $owaspLogFormat->toArray()
        ]);
    }

    private function logLegacyFormat(SecurityEvent $event): void
    {
        $context = SecurityContext::fromGlobals();
        $payload = $this->extractPayload($event);

        $this->logger->warning($event->type->value, [
            ...$payload,
            'ip' => $context->ip,
            'user_agent' => $context->userAgent,
            'timestamp' => $context->timestamp->format('c'),
        ]);
    }

    private function extractPayload(SecurityEvent $event): array
    {
        $data = [];
        $ref = new \ReflectionObject($event);
        foreach ($ref->getProperties() as $prop) {
            $prop->setAccessible(true);
            $data[$prop->getName()] = $prop->getValue($event);
        }
        return $data;
    }
}
