<?php

/**
 * Enrichment Service
 *
 * Adds labels, redacts sensitive data, and enriches log entries
 * before they are buffered and transported.
 *
 * @package CloudXM\SDK\Services
 * @since 1.0.0
 */

namespace CloudXM\SDK\Services;

use CloudXM\SDK\Config\Configuration;
use CloudXM\SDK\Support\HasLogger;
use Psr\Log\LoggerInterface;

/**
 * Enrichment Service
 */
final class EnrichmentService
{
    use HasLogger;

    /**
     * Configuration instance
     */
    private Configuration $config;

    /**
     * Logger instance
     */
    private LoggerInterface $logger;

    /**
     * Salt for hashing sensitive data
     */
    private string $hashSalt;

    /**
     * Constructor
     */
    public function __construct(
        Configuration $config,
        LoggerInterface $logger
    ) {
        $this->config = $config;
        $this->logger = $logger;

        // Initialize salt for hashing
        $this->hashSalt = $this->generateSalt();
    }

    /**
     * Initialize the service
     */
    public function initialize(): void
    {
        // Register the main enrichment hook
        add_action('cxm_log', [$this, 'enrichLogEntry'], 10, 4);

        // Register config update hook to refresh config
        add_action('cxm_logger_config_updated', [$this, 'onConfigUpdated'], 10, 2);
    }

    /**
     * Handle config updates to refresh cached config
     */
    public function onConfigUpdated(string $key, $value): void
    {
        if (strpos($key, 'logging.') === 0) {
            // Force config reload when logging settings are updated
            // Note: reload method may not exist in all config implementations
            // This is handled gracefully in the config class itself
        }
    }

    /**
     * Enrich a log entry with labels and redaction
     */
    public function enrichLogEntry(string $level, string $message, array $context, array $labels): array
    {
        // Apply PII redaction first
        $context = $this->redactSensitiveData($context);

        // Add standard labels
        $enrichedLabels = $this->addStandardLabels($level, $message, $context, $labels);

        // Add request-specific context
        $context = $this->addRequestContext($context);

        // Add store-specific context
        $context = $this->addStoreContext($context);

        // Apply sampling if configured
        if (!$this->shouldSample($level, $message, $context)) {
            return []; // Return empty to indicate log should be dropped
        }

        return [
            'level' => $level,
            'message' => $message,
            'context' => $context,
            'labels' => $enrichedLabels,
        ];
    }

    /**
     * Add standard labels to log entry
     */
    private function addStandardLabels(string $level, string $message, array $context, array $labels): array
    {
        // Base labels
        $standardLabels = [
            'env' => $this->getEnvironment(),
            'service_name' => $this->config->get('service_name') ?: gethostname(),
            'site' => $this->getSiteDomain(),
            'app' => 'wordpress',
            'component' => $this->detectComponent($message, $context),
            'severity' => $this->mapLevelToSeverity($level),
            'version' => defined('WC_CXM_LOGGER_VERSION') ? constant('WC_CXM_LOGGER_VERSION') : '1.0.0',
        ];

        // Tenant is handled via X-Scope-OrgID header, not as a label

        // Merge with provided labels (provided labels take precedence for conflicts)
        return array_merge($standardLabels, $labels);
    }

    /**
     * Redact sensitive data from context
     */
    private function redactSensitiveData(array $context): array
    {
        $redacted = $context;

        // Get plugin-specific additional fields
        $additionalHashFields = $this->getAdditionalHashFields();
        $additionalRemoveFields = $this->getAdditionalRemoveFields();

        // Fields to hash (PII) - default + plugin-specific
        $hashFields = array_merge(['user_id', 'customer_id', 'ip', 'email', 'document_number'], $additionalHashFields);
        foreach ($hashFields as $field) {
            if (isset($redacted[$field])) {
                $redacted[$field . '_hash'] = $this->hashValue($redacted[$field]);
                unset($redacted[$field]);
            }
        }

        // Fields to completely remove - default + plugin-specific
        $removeFields = array_merge([
            'password', 'pwd', 'pass', 'token', 'auth', 'authorization', 'cookie',
            'card_number', 'card_cvv', 'card_expiration_date', 'cvv', 'pan'
        ], $additionalRemoveFields);
        foreach ($removeFields as $field) {
            if (isset($redacted[$field])) {
                unset($redacted[$field]);
            }
        }

        // Recursively process nested arrays
        foreach ($redacted as $key => $value) {
            if (is_array($value)) {
                $redacted[$key] = $this->redactSensitiveData($value);
            }
        }

        return $redacted;
    }

    /**
     * Add request-specific context
     */
    private function addRequestContext(array $context): array
    {
        // Only add if not already present
        if (!isset($context['request_id'])) {
            $context['request_id'] = $this->generateRequestId();
        }

        // Add correlation ID from Logger if available and not already present
        if (!isset($context['correlation_id'])) {
            // Correlation ID is managed externally, we don't need to access it here
            // as it should be passed in the context if needed
        }

        // Add trace ID if present in headers
        if (!isset($context['trace_id']) && isset($_SERVER['HTTP_TRACEPARENT'])) {
            $context['trace_id'] = $_SERVER['HTTP_TRACEPARENT'];
        }

        // Add user info if not present
        if (!isset($context['user_id_hash']) && function_exists('is_user_logged_in') && is_user_logged_in()) {
            $user = wp_get_current_user();
            $context['user_id_hash'] = $this->hashValue($user->ID);
            $context['user_role'] = $user->roles[0] ?? 'unknown';
        }

        return $context;
    }

    /**
     * Add store-specific context
     */
    private function addStoreContext(array $context): array
    {
        // Add WooCommerce version if available
        if (!isset($context['wc_version']) && defined('WC_VERSION')) {
            $context['wc_version'] = constant('WC_VERSION');
        }

        // Add multisite info
        if (!isset($context['multisite']) && function_exists('is_multisite') && is_multisite()) {
            $context['multisite'] = true;
            $context['site_id'] = get_current_blog_id();
        }

        return $context;
    }

    /**
     * Determine if log entry should be sampled
     */
    private function shouldSample(string $level, string $message, array $context): bool
    {
        // Always sample errors and critical logs
        if (in_array($level, ['error', 'critical', 'emergency', 'alert'])) {
            return true;
        }

        // Check minimum log level from config
        $minLevel = $this->config ? $this->config->get('logging.min_level', 'info') : 'info';

        if (!$this->isLevelAtOrAboveMinLevel($level, $minLevel)) {
            return false; // Drop logs below minimum level
        }

        // In debug mode, allow additional debug logs through (but min_level still applies)
        // This allows debug logs at or above min_level when in debug mode
        // For example, if min_level is 'info', debug logs are still filtered out
        // But if min_level is 'debug', debug logs are allowed

        // Apply sampling rates from config
        $samplingRates = $this->config ? $this->config->get('sampling', []) : [];

        if (isset($samplingRates[$level])) {
            $rate = (float) $samplingRates[$level];
            if ($rate < 1.0) {
                return (mt_rand() / mt_getrandmax()) <= $rate;
            }
        }

        // Default: allow all logs at or above minimum level
        return true;
    }

    /**
     * Detect component from message/context
     */
    private function detectComponent(string $message, array $context): string
    {
        // WooCommerce detection
        if (strpos($message, 'woo.') === 0 || isset($context['order_id'])) {
            return 'woocommerce';
        }

        // WordPress core detection
        if (strpos($message, 'wp.') === 0) {
            return 'wordpress';
        }

        // HTTP detection
        if (strpos($message, 'http.') === 0) {
            return 'http';
        }

        // Default to plugin
        return 'plugin';
    }

    /**
     * Map PSR-3 levels to Loki severity
     */
    private function mapLevelToSeverity(string $level): string
    {
        $mapping = [
            'emergency' => 'critical',
            'alert' => 'critical',
            'critical' => 'critical',
            'error' => 'error',
            'warning' => 'warn',
            'notice' => 'info',
            'info' => 'info',
            'debug' => 'debug',
        ];

        return $mapping[$level] ?? 'info';
    }

    /**
     * Get environment label
     */
    private function getEnvironment(): string
    {
        $env = $this->config ? $this->config->get('environment') : null;
        if ($env) {
            return $env;
        }

        // Fallback detection
        if (function_exists('get_site_url') && (strpos(get_site_url(), 'localhost') !== false || strpos(get_site_url(), '.local') !== false)) {
            return 'dev';
        }
        if (defined('WP_ENVIRONMENT_TYPE')) {
            return constant('WP_ENVIRONMENT_TYPE');
        }

        return 'prod';
    }

    /**
     * Get server name
     */
    private function getServerName(): string
    {
        $server = $this->config ? $this->config->get('server_name') : null;
        return $server ?: gethostname();
    }

    /**
     * Get site domain
     */
    private function getSiteDomain(): string
    {
        if (function_exists('get_site_url')) {
            $domain = parse_url(get_site_url(), PHP_URL_HOST);
            return $domain ?: 'unknown';
        }
        return 'unknown';
    }

    /**
     * Generate request ID
     */
    private function generateRequestId(): string
    {
        return uniqid('req-', true);
    }

    /**
     * Hash a value with salt
     */
    private function hashValue($value): string
    {
        return hash('sha256', $this->hashSalt . (string) $value);
    }

    /**
     * Check if log level is at or above minimum level
     */
    private function isLevelAtOrAboveMinLevel(string $level, string $minLevel): bool
    {
        $levels = [
            'debug' => 0,
            'info' => 1,
            'notice' => 2,
            'warning' => 3,
            'error' => 4,
            'critical' => 5,
            'alert' => 6,
            'emergency' => 7,
        ];

        $levelValue = $levels[$level] ?? 1; // Default to info
        $minLevelValue = $levels[$minLevel] ?? 1; // Default to info

        return $levelValue >= $minLevelValue;
    }

    /**
     * Check if we're in debug mode
     */
    private function isDebugMode(): bool
    {
        return defined('WP_DEBUG') && WP_DEBUG;
    }

    /**
     * Generate or retrieve salt for hashing
     */
    private function generateSalt(): string
    {
        $optionName = 'cxm_logger_hash_salt';

        $salt = get_option($optionName);
        if (!$salt) {
            $salt = wp_generate_password(64, true, true);
            update_option($optionName, $salt);
        }

        return $salt;
    }

    /**
     * Get additional fields to hash from plugins/config
     */
    private function getAdditionalHashFields(): array
    {
        $fields = [];

        // Check config first
        if ($this->config) {
            $additionalFields = $this->config->get('pii.additional_hash_fields', []);
            if (is_array($additionalFields)) {
                $fields = array_merge($fields, $additionalFields);
            }
        }

        // Allow plugins to filter additional hash fields
        $fields = apply_filters('cxm_logger_additional_hash_fields', $fields);

        return array_unique($fields);
    }

    /**
     * Get additional fields to remove from plugins/config
     */
    private function getAdditionalRemoveFields(): array
    {
        $fields = [];

        // Check config first
        if ($this->config) {
            $additionalFields = $this->config->get('pii.additional_remove_fields', []);
            if (is_array($additionalFields)) {
                $fields = array_merge($fields, $additionalFields);
            }
        }

        // Allow plugins to filter additional remove fields
        $fields = apply_filters('cxm_logger_additional_remove_fields', $fields);

        return array_unique($fields);
    }

    /**
     * Prevent cloning
     */
    private function __clone()
    {
    }
}