<?php

namespace Sibs;

use PrestaShopLogger;

/**
 * This class represents an logger to allow
 * consistency between all loggers of different e-commerces.
 *
 * This abstraction is important for using the same interface
 * and to allow that the generic SIBS classes can use also a logger
 */
class SibsLogger
{
    /**
     * @var mixed
     */
    protected static $logger = null;

    // LOG FILES
    public const SIBS_LOG         = 'sibs';
    public const SIBS_INSTALL_LOG = 'sibs-install';
    public const SIBS_CRON_LOG    = 'sibs-cron';
    public const SIBS_WEBHOOK_LOG = 'sibs-webhook';
    public const SIBS_SBO_LOG     = 'sibs-sbo';

    // LOG DEPTHS
    public const LOG_UNCAPSULATE_DEPTH = 2;
    public const LOG_CAPSULATE_DEPTH   = 3;

    // LOG LEVELS
    public const LEVEL_DEBUG   = 'logDebug';         // 1
    public const LEVEL_INFO    = 'logInfo';           // 2
    public const LEVEL_WARNING = 'logWarning';     // 3
    public const LEVEL_ERROR   = 'logError';         // 4

    /**
     * Get loggger Instance.
     *
     * @return SibsFileLogger
     */
    private static function getInstance(): SibsFileLogger
    {
        if (is_null(self::$logger)) {
            self::$logger = new SibsFileLogger(SibsFileLogger::DEBUG);
        }

        return self::$logger;
    }

    /**
     * Set Filename Log.
     *
     * @param string $logname
     * @return string
     */
    public static function getFilenameLog(string $logname = self::SIBS_LOG): string
    {
        return _PS_ROOT_DIR_ . '/var/logs/' . $logname . '-' . date('Y-m-d') . '.log';
    }

    /**
     * Debug Log.
     *
     * @param mixed $message
     * @param string $context
     */
    public static function debug($message, string $context = self::SIBS_LOG): void
    {
        self::addRecord(self::LEVEL_DEBUG, $message, $context, self::LOG_CAPSULATE_DEPTH);
    }

    /**
     * Info Log.
     *
     * @param mixed $message
     * @param string $context
     */
    public static function info($message, string $context = self::SIBS_LOG): void
    {
        self::addRecord(self::LEVEL_INFO, $message, $context, self::LOG_CAPSULATE_DEPTH);
    }

    /**
     * Warning Log.
     *
     * @param mixed $message
     * @param string $context
     */
    public static function warning($message, string $context = self::SIBS_LOG): void
    {
        self::addRecord(self::LEVEL_WARNING, $message, $context, self::LOG_CAPSULATE_DEPTH);
    }

    /**
     * Error Log.
     *
     * @param mixed $message
     * @param string $context
     */
    public static function error($message, string $context = self::SIBS_LOG): void
    {
        self::addRecord(self::LEVEL_ERROR, $message, $context, self::LOG_CAPSULATE_DEPTH);
    }

    /**
     * Add a record to the log.
     *
     * @param string $loggerLevel Possible values are all SibsLoggerger::LEVEL_*
     * @param mixed $message Message to add to log
     * @param string $context context file to log
     * @param int $traceDepth Stack trace depth of the method that called the logger
     *
     * @note: $level should be transformed into severity level of respective e-commerce.
     * @note: $message should be formatted for a regular log message with more injected info
     *        For that, use self::formatMessageToLog($message)
     */
    public static function addRecord(
        string $loggerLevel,
        $message,
        string $context = self::SIBS_LOG,
        int $traceDepth = self::LOG_UNCAPSULATE_DEPTH
    ): void {
        $prettyMessage = self::isArrayOrJson($message) ? self::prettify($message) : $message;
        $messageToLog  = self::formatMessageToLog($prettyMessage, $traceDepth);

        $loggerInstance = self::getInstance();
        $loggerFilename = self::getFilenameLog($context);
        $loggerInstance->setFilename($loggerFilename);
        $loggerInstance->{$loggerLevel}($messageToLog);

        // TODO: After finish log page, remove this logger
        $severity        = self::mapLoggerLevelToSeverity($loggerLevel);
        $error_code      = null;
        $object_type     = 'Sibs';
        $object_id       = null;
        $allow_duplicate = true;
        $id_employee     = null;

        PrestaShopLogger::addLog(
            $message,
            $severity,
            $error_code,
            $object_type,
            $object_id,
            $allow_duplicate,
            $id_employee
        );
    }

    /**
     * @param string $message Message that will be formatted
     * @param int $traceDepth Stack trace depth of the method that called the logger
     *      The justification for the values of $traceDepth
     *      0 -> this method, $this->formatMessageToLog
     *      1 -> $this->addRecord
     *      2 -> the caller method
     */
    protected static function formatMessageToLog(string $message, int $traceDepth = self::LOG_UNCAPSULATE_DEPTH): string
    {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $traceDepth + 1);
        // Get the call function where log is
        $traceLevel = $trace[$traceDepth];
        // Get the line where log is called
        $traceLineLevel = $trace[($traceDepth > 0) ? ($traceDepth - 1) : $traceDepth];

        return (isset($traceLevel['class']) ? $traceLevel['class'] . '::' : '') .
            (isset($traceLevel['function']) ? $traceLevel['function'] : '') .
            (isset($traceLineLevel['line']) ? '(' . $traceLineLevel['line'] . '): ' : ' ') .
            $message;
    }

    /**
     * Map Logger Lever to Severity Level.
     *
     * @param string $loggerLevel
     *
     * @return int
     */
    public static function mapLoggerLevelToSeverity(string $loggerLevel): int
    {
        switch($loggerLevel) {
            case self::LEVEL_DEBUG:
            case self::LEVEL_INFO:
                return PrestaShopLogger::LOG_SEVERITY_LEVEL_INFORMATIVE;
            case self::LEVEL_WARNING:
                return PrestaShopLogger::LOG_SEVERITY_LEVEL_WARNING;
            case self::LEVEL_ERROR:
                return PrestaShopLogger::LOG_SEVERITY_LEVEL_ERROR;
            default:
                return PrestaShopLogger::LOG_SEVERITY_LEVEL_MAJOR;
        }
    }

    /**
     * Prettify Message to be logged.
     *
     * @param mixed $message
     *
     * @return string
     */
    public static function prettify($message): string
    {
        $objectMessage = SibsObjectify::json_mapper($message);

        return json_encode($objectMessage, JSON_PRETTY_PRINT);
    }

    /**
     * Verify if given message is a json string.
     *
     * @param mixed $message
     *
     * @return bool
     */
    protected static function isArrayOrJson($message): bool
    {
        if (is_array($message)) {
            return true;
        }

        if (! is_string($message)) {
            return false;
        }

        json_decode($message);

        return json_last_error() === JSON_ERROR_NONE;
    }
}
