<?php

/**
 * Copyright (c) D3 Data Development (Inh. Thomas Dartsch)
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 *
 * https://www.d3data.de
 *
 * @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
 * @author    D3 Data Development - Daniel Seifert <info@shopmodule.com>
 * @link      https://www.oxidmodule.com
 */

declare(strict_types=1);

namespace D3\LoggerFactory;

use Exception;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Handler\BufferHandler;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use Monolog\Handler\FingersCrossedHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use OxidEsales\Eshop\Core\Registry;
use RuntimeException;

class LoggerFactory
{
    public const SPECIAL_HANDLERS_BUFFERING = 'buffering';
    public const SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY = 'logOnErrorOnly';

    public static function create(): LoggerFactory
    {
        return new LoggerFactory();
    }

    /**
     * @throws Exception
     */
    public function getFileLogger(
        string $loggerName,
        string $filePath,
        int $logLevel = Logger::INFO,
        ?int $maxFiles = null,
        array $specialHandlers = []
    ): Logger
    {
        $logger = new Logger($loggerName);
        $handler = $this->applySpecialHandlers(
            $this->getFileLoggerStreamHandler($filePath, $logLevel, $maxFiles),
            $specialHandlers
        );
        $logger->pushHandler($handler);

        return $logger;
    }

    /**
     * @throws Exception
     */
    public function getFileLoggerStreamHandler(
        string $filePath,
        int $logLevel = Logger::INFO,
        ?int $maxFiles = null
    ): AbstractProcessingHandler
    {
        return is_null($maxFiles) ?
            new StreamHandler($filePath, $logLevel) :
            new RotatingFileHandler($filePath, $maxFiles, $logLevel);
    }

    /**
     * @throws Exception
     */
    public function getCombinedOxidAndFileLogger(
        string $loggerName,
        string $filePath,
        int $logLevel = Logger::INFO,
        ?int $maxFiles = null,
        array $specialHandlers = []
    ): Logger
    {
        if (!class_exists(Registry::class)) {
            throw new RuntimeException(__METHOD__.' can executed in OXID eShop installations only');
        }

        $logger = new Logger($loggerName);
        $handler = $this->applySpecialHandlers(
            $this->getFileLoggerStreamHandler($filePath, $logLevel, $maxFiles),
            $specialHandlers
        );
        $logger->pushHandler($handler);

        $oxidLogFilePath = $this->getOxidLogPath('oxideshop.log');
        $oxidHandler = $this->applySpecialHandlers(
            new StreamHandler($oxidLogFilePath, Logger::ERROR),
            $specialHandlers
        );
        $logger->pushHandler($oxidHandler);

        return $logger;
    }

    protected function getOxidLogPath(string $fileName): string
    {
        if (!class_exists(Registry::class)) {
            throw new RuntimeException(__METHOD__.' can executed in OXID eShop installations only');
        }

        return OX_BASE_PATH . '/log' . DIRECTORY_SEPARATOR . $fileName;
    }

    public function applySpecialHandlers(
        AbstractProcessingHandler $handler,
        array $specialHandlers = []
    ): AbstractProcessingHandler
    {
        if (in_array(self::SPECIAL_HANDLERS_BUFFERING, $specialHandlers, true)) {
            $handler = $this->setLogItemsOnErrorOnly($handler);
        }
        if (in_array(self::SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY, $specialHandlers, true)) {
            $handler = $this->setLogItemsOnErrorOnly($handler);
        }

        return $handler;
    }

    public function setBuffering(AbstractProcessingHandler $handler): BufferHandler
    {
        return new BufferHandler($handler);
    }

    public function setLogItemsOnErrorOnly(
        AbstractProcessingHandler $handler,
        int $activationLevel = Logger::ERROR
    ): FingersCrossedHandler
    {
        return new FingersCrossedHandler(
            $handler,
            new ErrorLevelActivationStrategy($activationLevel)
        );
    }
}