<?php

namespace Storeplugin\PDFInvoice\Framework\Utils;

defined('ABSPATH') || exit;

use Storeplugin\PDFInvoice\Config;

/**
 * Security Logger
 *
 * Logs security events for monitoring and audit purposes.
 */
class SecurityLogger
{
    /**
     * Maximum log entries to keep
     */
    private const MAX_LOG_ENTRIES = 100;

    /**
     * Event severity levels
     */
    const SEVERITY_INFO = 'info';
    const SEVERITY_WARNING = 'warning';
    const SEVERITY_ERROR = 'error';
    const SEVERITY_CRITICAL = 'critical';

    /**
     * Event types
     */
    const EVENT_NONCE_FAILED = 'nonce_failed';
    const EVENT_PERMISSION_DENIED = 'permission_denied';
    const EVENT_RATE_LIMIT_EXCEEDED = 'rate_limit_exceeded';
    const EVENT_INVALID_INPUT = 'invalid_input';
    const EVENT_SUSPICIOUS_ACTIVITY = 'suspicious_activity';
    const EVENT_LOGIN_FAILED = 'login_failed';
    const EVENT_SETTINGS_CHANGED = 'settings_changed';

    /**
     * Log a security event
     *
     * @param string $eventType Event type constant
     * @param string $message   Human-readable message
     * @param string $severity  Severity level
     * @param array  $context   Additional context data
     */
    public static function log(
        string $eventType,
        string $message,
        string $severity = self::SEVERITY_WARNING,
        array $context = []
    ): void {
        // Don't log in production if logging is disabled
        if (!self::isLoggingEnabled()) {
            return;
        }

        $entry = [
            'timestamp' => current_time('mysql'),
            'event_type' => $eventType,
            'severity' => $severity,
            'message' => $message,
            'user_id' => get_current_user_id(),
            'user_login' => self::getCurrentUserLogin(),
            'ip_address' => Utils::getClientIP(),
            'user_agent' => self::getUserAgent(),
            'request_uri' => self::getRequestURI(),
            'context' => $context,
        ];

        self::addLogEntry($entry);

        // Also surface critical events in debug environments.
        if ($severity === self::SEVERITY_CRITICAL) {
            Utils::debugLog(
                sprintf(
                    '%s: %s (User: %s, IP: %s)',
                    $eventType,
                    $message,
                    $entry['user_login'],
                    $entry['ip_address']
                ),
                Config::name()
            );
        }

        // Trigger action for custom handling
        \do_action('nxtpdf_security_event_logged', $entry);
    }

    /**
     * Log a failed nonce verification
     *
     * @param string $action The nonce action that failed
     * @param array  $context Additional context
     */
    public static function logNonceFailed(string $action, array $context = []): void
    {
        self::log(
            self::EVENT_NONCE_FAILED,
            sprintf('Nonce verification failed for action: %s', $action),
            self::SEVERITY_WARNING,
            array_merge(['action' => $action], $context)
        );
    }

    /**
     * Log a permission denied event
     *
     * @param string $capability Required capability
     * @param array  $context    Additional context
     */
    public static function logPermissionDenied(string $capability, array $context = []): void
    {
        self::log(
            self::EVENT_PERMISSION_DENIED,
            sprintf('Permission denied. Required capability: %s', $capability),
            self::SEVERITY_WARNING,
            array_merge(['capability' => $capability], $context)
        );
    }

    /**
     * Log a rate limit exceeded event
     *
     * @param string $action  The rate-limited action
     * @param array  $context Additional context
     */
    public static function logRateLimitExceeded(string $action, array $context = []): void
    {
        self::log(
            self::EVENT_RATE_LIMIT_EXCEEDED,
            sprintf('Rate limit exceeded for action: %s', $action),
            self::SEVERITY_ERROR,
            array_merge(['action' => $action], $context)
        );
    }

    /**
     * Log invalid input
     *
     * @param string $fieldId The field with invalid input
     * @param string $error   The validation error
     * @param array  $context Additional context
     */
    public static function logInvalidInput(string $fieldId, string $error, array $context = []): void
    {
        self::log(
            self::EVENT_INVALID_INPUT,
            sprintf('Invalid input for field %s: %s', $fieldId, $error),
            self::SEVERITY_INFO,
            array_merge(['field_id' => $fieldId, 'error' => $error], $context)
        );
    }

    /**
     * Get all security log entries
     *
     * @param int $limit Maximum number of entries to return
     *
     * @return array
     */
    public static function getLog(int $limit = 50): array
    {
        $log = \get_option(Config::option('security_log'), []);

        if (!is_array($log)) {
            return [];
        }

        // Return most recent entries first
        $log = array_reverse($log);

        return array_slice($log, 0, $limit);
    }

    /**
     * Get log entries by event type
     *
     * @param string $eventType Event type to filter by
     * @param int    $limit     Maximum entries to return
     *
     * @return array
     */
    public static function getLogByType(string $eventType, int $limit = 50): array
    {
        $log = self::getLog(self::MAX_LOG_ENTRIES);

        return array_slice(
            array_filter($log, function ($entry) use ($eventType) {
                return isset($entry['event_type']) && $entry['event_type'] === $eventType;
            }),
            0,
            $limit
        );
    }

    /**
     * Get log entries by severity
     *
     * @param string $severity Severity level to filter by
     * @param int    $limit    Maximum entries to return
     *
     * @return array
     */
    public static function getLogBySeverity(string $severity, int $limit = 50): array
    {
        $log = self::getLog(self::MAX_LOG_ENTRIES);

        return array_slice(
            array_filter($log, function ($entry) use ($severity) {
                return isset($entry['severity']) && $entry['severity'] === $severity;
            }),
            0,
            $limit
        );
    }

    /**
     * Clear the security log
     */
    public static function clearLog(): void
    {
        \delete_option(Config::option('security_log'));
    }

    /**
     * Add a log entry
     *
     * @param array $entry Log entry data
     */
    private static function addLogEntry(array $entry): void
    {
        $log = \get_option(Config::option('security_log'), []);

        if (!is_array($log)) {
            $log = [];
        }

        // Add new entry
        $log[] = $entry;

        // Keep only the most recent entries
        if (count($log) > self::MAX_LOG_ENTRIES) {
            $log = array_slice($log, -self::MAX_LOG_ENTRIES);
        }

        \update_option(Config::option('security_log'), $log, false);
    }

    /**
     * Check if security logging is enabled
     *
     * @return bool
     */
    private static function isLoggingEnabled(): bool
    {
        // Check if logging is disabled via constant
        if (Config::isSecurityLogDisabled()) {
            return false;
        }

        // Check if logging is enabled via option
        return (bool) \get_option(Config::option('enable_security_logging'), true);
    }

    /**
     * Get current user login
     *
     * @return string
     */
    private static function getCurrentUserLogin(): string
    {
        $user = \wp_get_current_user();
        return $user->exists() ? $user->user_login : 'guest';
    }

    /**
     * Get user agent
     *
     * @return string
     */
    private static function getUserAgent(): string
    {
        return isset($_SERVER['HTTP_USER_AGENT'])
            ? \sanitize_text_field(\wp_unslash($_SERVER['HTTP_USER_AGENT']))
            : 'unknown';
    }

    /**
     * Get request URI
     *
     * @return string
     */
    private static function getRequestURI(): string
    {
        return isset($_SERVER['REQUEST_URI'])
            ? \esc_url_raw(\wp_unslash($_SERVER['REQUEST_URI']))
            : 'unknown';
    }

    /**
     * Get security statistics
     *
     * @return array
     */
    public static function getStatistics(): array
    {
        $log = self::getLog(self::MAX_LOG_ENTRIES);

        $stats = [
            'total_events' => count($log),
            'by_severity' => [
                self::SEVERITY_INFO => 0,
                self::SEVERITY_WARNING => 0,
                self::SEVERITY_ERROR => 0,
                self::SEVERITY_CRITICAL => 0,
            ],
            'by_type' => [],
            'unique_ips' => [],
            'affected_users' => [],
        ];

        foreach ($log as $entry) {
            // Count by severity
            if (isset($entry['severity'])) {
                $severity = $entry['severity'];
                if (isset($stats['by_severity'][$severity])) {
                    $stats['by_severity'][$severity]++;
                }
            }

            // Count by type
            if (isset($entry['event_type'])) {
                $type = $entry['event_type'];
                if (!isset($stats['by_type'][$type])) {
                    $stats['by_type'][$type] = 0;
                }
                $stats['by_type'][$type]++;
            }

            // Track unique IPs
            if (isset($entry['ip_address']) && !in_array($entry['ip_address'], $stats['unique_ips'])) {
                $stats['unique_ips'][] = $entry['ip_address'];
            }

            // Track affected users
            if (isset($entry['user_login']) && $entry['user_login'] !== 'guest' && !in_array($entry['user_login'], $stats['affected_users'])) {
                $stats['affected_users'][] = $entry['user_login'];
            }
        }

        $stats['unique_ips_count'] = count($stats['unique_ips']);
        $stats['affected_users_count'] = count($stats['affected_users']);

        return $stats;
    }
}
