<?php
declare(strict_types=1);

namespace App\Services;

use App\Core\Logging\FileLogger;

final class SecurityEventMonitor
{
    /** @param array<string,mixed> $context */
    public static function record(string $event, string $severity = 'warning', array $context = []): void
    {
        try {
            $base = defined('BASE_PATH') ? BASE_PATH : dirname(__DIR__, 2);
            $safe = self::sanitize($context);
            $safe += [
                'event' => $event,
                'severity' => $severity,
                'request_id' => self::requestId(),
                'user_id' => isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : null,
                'organisation_id' => isset($_SESSION['organisation_id']) ? (int)$_SESSION['organisation_id'] : null,
                'ip_hash' => self::hashValue((string)($_SERVER['REMOTE_ADDR'] ?? 'unknown')),
                'user_agent_hash' => self::hashValue((string)($_SERVER['HTTP_USER_AGENT'] ?? 'unknown')),
                'method' => (string)($_SERVER['REQUEST_METHOD'] ?? ''),
                'path' => self::safePath((string)($_SERVER['REQUEST_URI'] ?? '')),
            ];

            $logger = new FileLogger($base . '/storage/logs/security', 'security-events.log', 5 * 1024 * 1024, 10);
            $logger->security($event, $safe);
            SecurityAuditIntegrityService::append([
                'timestamp' => date(DATE_ATOM),
                'event' => $event,
                'severity' => $severity,
                'request_id' => $safe['request_id'],
                'user_id' => $safe['user_id'],
                'organisation_id' => $safe['organisation_id'],
                'path' => $safe['path'],
            ]);

            if (self::isAnomalous($base, $event, $safe)) {
                $alert = new FileLogger($base . '/storage/logs/security', 'security-alerts.log', 2 * 1024 * 1024, 10);
                $alert->security('security.anomaly_detected', [
                    'source_event' => $event,
                    'severity' => $severity,
                    'request_id' => $safe['request_id'],
                    'ip_hash' => $safe['ip_hash'],
                    'path' => $safe['path'],
                ]);
            }
        } catch (\Throwable $exception) {
            error_log('[AXIOM SECURITY EVENT] ' . $event . ' (' . $exception->getMessage() . ')');
        }
    }

    public static function requestId(): string
    {
        if (!empty($_SERVER['AXIOM_REQUEST_ID'])) return (string)$_SERVER['AXIOM_REQUEST_ID'];
        $_SERVER['AXIOM_REQUEST_ID'] = bin2hex(random_bytes(8));
        return (string)$_SERVER['AXIOM_REQUEST_ID'];
    }

    /** @param array<string,mixed> $context @return array<string,mixed> */
    private static function sanitize(array $context): array
    {
        $blocked = ['password','mot_de_passe','token','secret','authorization','cookie','csrf','session'];
        $result = [];
        foreach ($context as $key => $value) {
            $lower = strtolower((string)$key);
            if (array_filter($blocked, static fn(string $needle): bool => str_contains($lower, $needle))) {
                $result[$key] = '[REDACTED]';
                continue;
            }
            if (is_scalar($value) || $value === null) {
                $result[$key] = mb_substr((string)$value, 0, 500);
            } else {
                $result[$key] = '[COMPLEX_VALUE]';
            }
        }
        return $result;
    }

    /** @param array<string,mixed> $context */
    private static function isAnomalous(string $base, string $event, array $context): bool
    {
        $threshold = max(3, min(100, (int)($_ENV['SECURITY_ALERT_THRESHOLD'] ?? 8)));
        $window = max(60, min(3600, (int)($_ENV['SECURITY_ALERT_WINDOW_SECONDS'] ?? 300)));
        $directory = $base . '/storage/private/security';
        if (!is_dir($directory) && !mkdir($directory, 0770, true) && !is_dir($directory)) return false;
        $file = $directory . '/event-counters.json';
        $now = time();
        $fingerprint = hash('sha256', $event . '|' . ($context['ip_hash'] ?? '') . '|' . ($context['path'] ?? ''));
        $state = [];
        if (is_file($file)) {
            $decoded = json_decode((string)file_get_contents($file), true);
            if (is_array($decoded)) $state = $decoded;
        }
        foreach ($state as $key => $row) {
            if (!is_array($row) || (int)($row['last'] ?? 0) < $now - $window) unset($state[$key]);
        }
        $row = $state[$fingerprint] ?? ['count' => 0, 'first' => $now, 'last' => $now, 'alerted' => false];
        $row['count'] = (int)$row['count'] + 1;
        $row['last'] = $now;
        $anomaly = $row['count'] >= $threshold && !$row['alerted'];
        if ($anomaly) $row['alerted'] = true;
        $state[$fingerprint] = $row;
        file_put_contents($file, json_encode($state, JSON_UNESCAPED_SLASHES), LOCK_EX);
        @chmod($file, 0600);
        return $anomaly;
    }

    private static function hashValue(string $value): string
    {
        $salt = (string)($_ENV['SECURITY_LOG_HASH_SALT'] ?? 'axiom-local-security-log');
        return hash_hmac('sha256', $value, $salt);
    }

    private static function safePath(string $uri): string
    {
        $path = parse_url($uri, PHP_URL_PATH);
        return mb_substr(is_string($path) ? $path : '/', 0, 500);
    }
}
