<?php
declare(strict_types=1);

/**
 * Security event logging — separate from activity_log (which is business-event focused).
 *
 * Records to the security_events table. All write paths are wrapped in try/catch
 * so a logging failure never bubbles up and breaks the originating action.
 *
 * Event type taxonomy (extend as needed; keep keys snake_case):
 *
 *   Authentication:
 *     login_success, login_failure, login_locked,
 *     mfa_failure, mfa_lockout,
 *     password_changed, password_reset_requested, password_reset_completed,
 *     email_verified, registration
 *
 *   Account management (admin):
 *     admin_user_create, admin_user_update, admin_user_delete,
 *     admin_password_reset, admin_role_change
 *
 *   Plugins / updates:
 *     plugin_enable, plugin_disable, plugin_delete, plugin_upload,
 *     update_perform
 *
 *   Privilege escalation:
 *     impersonate_start, impersonate_stop
 *
 * Severity:
 *   info  – routine, expected event (login_success)
 *   warn  – noteworthy but not necessarily malicious (login_failure, mfa_failure)
 *   critical – probable attack indicator or destructive admin action
 *              (login_locked, plugin_delete, admin_user_delete)
 */
class SecurityLog
{
    public const SEV_INFO     = 'info';
    public const SEV_WARN     = 'warn';
    public const SEV_CRITICAL = 'critical';

    /**
     * Record a security event. Never throws — logging failure must not break
     * the calling action. $details is JSON-encoded; do not include passwords,
     * tokens, or other secrets.
     */
    public static function log(
        string $eventType,
        string $severity = self::SEV_INFO,
        array $details = [],
        ?int $userId = null
    ): void {
        try {
            $db = Database::getInstance();
            $db->execute(
                'INSERT INTO security_events (event_type, severity, user_id, ip, details) VALUES (?, ?, ?, ?, ?)',
                [
                    $eventType,
                    $severity,
                    $userId ?? (Auth::id() ?: null),
                    self::clientIp(),
                    $details ? json_encode($details, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
                ]
            );
        } catch (\Throwable $e) {
            // Last-resort: emit to PHP error log. Never re-throw.
            error_log('[Plenvo SecurityLog] write failed for ' . $eventType . ': ' . $e->getMessage());
        }
    }

    /** Best-effort client IP capture, honouring `trust_proxy_headers` if the admin enabled it. */
    private static function clientIp(): ?string
    {
        return Helper::clientIp();
    }

    /** Delete events older than the configured retention window (in days). */
    public static function pruneOlderThan(int $days): int
    {
        if ($days <= 0) return 0;
        try {
            $cutoff = date('Y-m-d H:i:s', time() - $days * 86400);
            $db = Database::getInstance();
            return $db->execute('DELETE FROM security_events WHERE created_at < ?', [$cutoff]);
        } catch (\Throwable) {
            return 0;
        }
    }
}
