<?php

namespace YSquare\GiftCert\Core;

use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use OxidEsales\Eshop\Core\Registry;
use Psr\Log\LoggerInterface;
use Throwable;

/**
 * Channel-specific logger for ys_giftcert troubleshooting.
 *
 * Activated per request via the module setting `sYsGiftcertLogMode`:
 *   - `off`   — no output, all calls are no-ops
 *   - `error` — only warning/error are written
 *   - `debug` — full trace (debug/info/warning/error)
 *
 * Writes to `source/log/ys_giftcert.log` so it can be inspected independently
 * of the shop's standard log stream.
 */
class DebugLogger
{
    const MODE_OFF   = 'off';
    const MODE_ERROR = 'error';
    const MODE_DEBUG = 'debug';

    /** @var string|null cached mode lookup per request */
    private static $mode = null;

    /** @var LoggerInterface|null lazy-initialized monolog instance */
    private static $logger = null;

    public static function debug($context, $message, array $data = [])
    {
        if (self::getMode() !== self::MODE_DEBUG) {
            return;
        }
        self::write('debug', $context, $message, $data);
    }

    public static function info($context, $message, array $data = [])
    {
        if (self::getMode() !== self::MODE_DEBUG) {
            return;
        }
        self::write('info', $context, $message, $data);
    }

    public static function warning($context, $message, array $data = [])
    {
        if (self::getMode() === self::MODE_OFF) {
            return;
        }
        self::write('warning', $context, $message, $data);
    }

    public static function error($context, $message, array $data = [])
    {
        if (self::getMode() === self::MODE_OFF) {
            return;
        }
        self::write('error', $context, $message, $data);
    }

    public static function isEnabled()
    {
        return self::getMode() !== self::MODE_OFF;
    }

    public static function isDebug()
    {
        return self::getMode() === self::MODE_DEBUG;
    }

    /**
     * Returns a compact list of recent call frames for diagnosis.
     * Skips the DebugLogger frames themselves.
     *
     * @param int $depth how many frames to keep
     * @return string[]
     */
    public static function callers($depth = 10)
    {
        if (self::getMode() === self::MODE_OFF) {
            return [];
        }
        $frames = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $depth + 4);
        $out = [];
        foreach ($frames as $f) {
            $cls = isset($f['class']) ? $f['class'] : '';
            if ($cls === __CLASS__) {
                continue;
            }
            $func = isset($f['function']) ? $f['function'] : '';
            $type = isset($f['type']) ? $f['type'] : '';
            $file = isset($f['file']) ? basename($f['file']) : '?';
            $line = isset($f['line']) ? $f['line'] : '?';
            $out[] = $cls . $type . $func . ' (' . $file . ':' . $line . ')';
            if (count($out) >= $depth) {
                break;
            }
        }
        return $out;
    }

    private static function getMode()
    {
        if (self::$mode === null) {
            try {
                $mode = (string) Registry::getConfig()->getConfigParam('sYsGiftcertLogMode');
            } catch (Throwable $e) {
                $mode = '';
            }
            self::$mode = in_array($mode, [self::MODE_ERROR, self::MODE_DEBUG], true)
                ? $mode
                : self::MODE_OFF;
        }
        return self::$mode;
    }

    private static function write($level, $context, $message, array $data)
    {
        try {
            $logger = self::getLogger();
            if ($logger === null) {
                return;
            }
            $logger->{$level}('[' . $context . '] ' . $message, $data);
        } catch (Throwable $e) {
            // Logging must never break the request path it observes.
        }
    }

    private static function getLogger()
    {
        if (self::$logger !== null) {
            return self::$logger;
        }
        if (!class_exists(Logger::class)) {
            return null;
        }

        $logFile = rtrim((string) Registry::getConfig()->getConfigParam('sShopDir'), '/')
            . '/log/ys_giftcert.log';

        $logger  = new Logger('ys_giftcert');
        $handler = new StreamHandler($logFile, Logger::DEBUG);
        $handler->setFormatter(new LineFormatter(null, null, true, true));
        $logger->pushHandler($handler);

        self::$logger = $logger;
        return self::$logger;
    }
}
