<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Monitoring;

use const FILTER_VALIDATE_EMAIL;

use function is_array;
use function is_string;

use Monolog\LogRecord;
use Monolog\Processor\ProcessorInterface;

/**
 * Monolog processor that scrubs PII from log entries before sending to Loki.
 *
 * Critical for RGPD compliance (NFR-S3): No personal data in centralized logs.
 *
 * @see Story 1.8 - T5.4: Filter PII from logs (scrubber processor)
 */
final class PiiScrubberLogProcessor implements ProcessorInterface
{
    public function __invoke(LogRecord $record): LogRecord
    {
        $context = $this->scrubArray($record->context);
        $extra = $this->scrubArray($record->extra);

        return $record->with(context: $context, extra: $extra);
    }

    /**
     * @param array<array-key, mixed> $data
     *
     * @return array<array-key, mixed>
     */
    private function scrubArray(array $data): array
    {
        $result = [];

        foreach ($data as $key => $value) {
            if (is_string($key) && $this->isPiiKey($key)) {
                $result[$key] = '[REDACTED]';
            } elseif (is_array($value)) {
                $result[$key] = $this->scrubArray($value);
            } elseif (is_string($value) && $this->looksLikePii($value)) {
                $result[$key] = '[REDACTED]';
            } else {
                $result[$key] = $value;
            }
        }

        return $result;
    }

    private function isPiiKey(string $key): bool
    {
        return PiiPatterns::isSensitiveKey($key);
    }

    private function looksLikePii(string $value): bool
    {
        // Filter email addresses
        if (filter_var($value, FILTER_VALIDATE_EMAIL) !== false) {
            return true;
        }

        // Filter JWT tokens
        if (preg_match('/^eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$/', $value)) {
            return true;
        }

        // Filter French phone numbers
        if (preg_match('/^(?:\+33|0)[1-9](?:[0-9]{2}){4}$/', preg_replace('/\s/', '', $value) ?? '')) {
            return true;
        }

        // Filter French NIR (numéro de sécurité sociale) - RGPD critical
        $cleanValue = preg_replace('/[\s.-]/', '', $value) ?? '';
        if (preg_match('/^[12]\d{2}(0[1-9]|1[0-2])\d{2}\d{3}\d{3}\d{2}$/', $cleanValue)) {
            return true;
        }

        return false;
    }
}
