<?php

/**
 * CloudXM Logger Facade (v2.0.0 - PHP 7.4 Compatible)
 *
 * Thin, client-agnostic static facade over Monolog-based pipeline.
 * - No buffering, no direct network transport here.
 * - Default routing: INTERNAL (file only).
 * - Use ['sink' => 'remote'] in $context to enable dual-sink (file + Loki).
 * - Derives a channel from callsite when none is provided.
 *
 * @package CloudXM\SDK
 * @since 2.0.0
 */

namespace CloudXM\SDK;

use CloudXM\SDK\Utilities\LoggerFactory;
use Psr\Log\LoggerInterface;

final class Logger
{
    /** Correlation ID for current request/session. */
    private static ?string $correlationId = null;

    /* ===== PSR-3-like public API ===== */
    public static function emergency(string $message, array $context = [], ?string $channel = null): void { self::log('emergency', $message, $context, $channel); }
    public static function alert(string $message, array $context = [], ?string $channel = null): void     { self::log('alert',     $message, $context, $channel); }
    public static function critical(string $message, array $context = [], ?string $channel = null): void { self::log('critical', $message, $context, $channel); }
    public static function error(string $message, array $context = [], ?string $channel = null): void     { self::log('error',     $message, $context, $channel); }
    public static function warning(string $message, array $context = [], ?string $channel = null): void   { self::log('warning',   $message, $context, $channel); }
    public static function notice(string $message, array $context = [], ?string $channel = null): void    { self::log('notice',    $message, $context, $channel); }
    public static function info(string $message, array $context = [], ?string $channel = null): void      { self::log('info',      $message, $context, $channel); }
    public static function debug(string $message, array $context = [], ?string $channel = null): void     { self::log('debug',     $message, $context, $channel); }

    /**
     * Generic log method — forwards to Monolog-backed logger.
     * Routing policy lives in the handlers wired by LoggerFactory.
     */
    public static function log(string $level, string $message, array $context = [], ?string $channel = null): void
    {
        $logger = self::resolveLogger($channel);

        // Ensure correlation id is attached if not provided already
        if (!isset($context['correlation_id'])) {
            $context['correlation_id'] = self::getCorrelationId();
        }

        // Add plugin context from channel if not already present
        if (!isset($context['plugin']) && $channel !== null) {
            $context['plugin'] = self::extractPluginFromChannel($channel);
        }

        $normalizedLevel = self::normalizeLevel($level);

        // Forward to Monolog logger for file/Loki output (emission happens in LoggerFactory processor)
        $logger->log($normalizedLevel, $message, $context);
    }

    /* ===== Internals ===== */

    /** Get correlation ID for current request. */
    public static function getCorrelationId(): string
    {
        if (self::$correlationId === null) {
            self::$correlationId = bin2hex(random_bytes(8));
        }
        return self::$correlationId;
    }

    /** Boot logger (register hooks, etc.) */
    public static function boot(): void
    {
        // Initialize correlation ID on boot
        self::getCorrelationId();

        // Additional boot logic can be added here if needed
    }

    /* ===== Helpers ===== */

    /** Resolve a PSR-3 logger for a channel, deriving from callsite when absent. */
    private static function resolveLogger(?string $channel = null): LoggerInterface
    {
        $channel = $channel ?? self::inferChannelFromBacktrace();
        return \CloudXM\SDK\Utilities\LoggerFactory::get($channel);
    }

    /**
     * Normalize short/uppercase levels to PSR-3 canonical lowercase names.
     * PHP 7.4+ Compatible - uses array lookup instead of match() expression
     */
    private static function normalizeLevel(string $level): string
    {
        $level = strtolower($level);

        // Mapping for non-canonical levels (PHP 7.4+ compatible)
        $mapping = [
            'warn' => 'warning',
            'err' => 'error',
            'crit' => 'critical',
        ];

        // Return mapped value or default
        return $mapping[$level] ?? $level;
    }

    /**
     * Infer a reasonable channel name from callsite.
     * Prefer nearest frame with a class; otherwise fall back to basename(file).
     */
    private static function inferChannelFromBacktrace(): string
    {
        $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 6);
        foreach ($bt as $frame) {
            if (!empty($frame['class'])) {
                return (string) $frame['class'];
            }
        }
        $file = $bt[1]['file'] ?? ($bt[0]['file'] ?? null);
        return $file ? basename($file) : 'App';
    }

    /**
     * Extract plugin name from channel/class name.
     * PHP 7.4+ Compatible - uses strncmp() instead of str_starts_with()
     */
    private static function extractPluginFromChannel(string $channel): string
    {
        // Handle CloudXM PSR-4 classes: CloudXM\Plugin\Services\ServiceName -> plugin
        if (strncmp($channel, 'CloudXM\\', strlen('CloudXM\\')) === 0) {
            $parts = explode('\\', $channel);
            return strtolower($parts[1] ?? 'cloudxm');
        }

        // Handle legacy WooCommerce style: WC_Marlim_Gateway -> marlim
        if (strncmp($channel, 'WC_', strlen('WC_')) === 0) {
            $pluginPart = explode('_', substr($channel, 3))[0] ?? '';
            return strtolower($pluginPart);
        }

        // Direct plugin names
        $pluginNames = ['marlim', 'envioz', 'nfse', 'logger'];
        foreach ($pluginNames as $plugin) {
            if (stripos($channel, $plugin) !== false) {
                return $plugin;
            }
        }

        return 'cloudxm';
    }
}
