<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2018 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace LifeStyle\Tools\ElkLogFormatterBundle\Monolog\Formatter;

use Monolog\Formatter\NormalizerFormatter;

/**
 * Class ElkLogFormatter
 * @package LifeStyle\Tools\ElkLogFormatterBundle\Monolog\Formatter
 */
final class ElkLogFormatter extends NormalizerFormatter
{
    /**
     * @var string
     */
    protected $format = "[%datetime%] [%version%] [%host%] [%extra.token%] [%channel%] [%level_name%] [%message%] [%extra.serverData%]\n";

    /**
     * @var string
     */
    protected $systemName;

    /**
     * @var string
     */
    protected $version = 'LsELK 1.0.0';

    /**
     * @var bool
     */
    protected $includeStacktraces = false;

    /**
     * ElkLogFormatter constructor.
     */
    public function __construct()
    {
        $this->systemName = gethostname();
        parent::__construct('Y-m-d\TH:i:s.uP');
    }

    /**
     * @param array $record
     * @return string
     */
    public function format(array $record)
    {
        //add additional parameters
        $record['host'] = $this->systemName;
        $record['version'] = $this->version;

        $vars = parent::format($record);
        $output = $this->format;

        foreach ($vars['extra'] as $var => $val) {
            if (false !== strpos($output, '%extra.' . $var . '%')) {
                $output = str_replace('%extra.' . $var . '%', $this->stringify($val), $output);
                unset($vars['extra'][$var]);
            }
        }

        foreach ($vars['context'] as $var => $val) {
            if (false !== strpos($output, '%context.' . $var . '%')) {
                $output = str_replace('%context.' . $var . '%', $this->stringify($val), $output);
                unset($vars['context'][$var]);
            }
        }

        foreach ($vars as $var => $val) {
            if (false !== strpos($output, '%' . $var . '%')) {
                $output = str_replace('%' . $var . '%', $this->stringify($val), $output);
            }
        }

        return $output;
    }

    /**
     * @param array $records
     * @return string
     */
    public function formatBatch(array $records)
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }

    /**
     * @param $value
     * @return mixed
     */
    public function stringify($value)
    {
        return $this->replaceNewlines($this->convertToString($value));
    }

    /**
     * @param $e
     * @return string
     */
    protected function normalizeException($e)
    {
        // TODO 2.0 only check for Throwable
        if (!$e instanceof \Exception && !$e instanceof \Throwable) {
            throw new \InvalidArgumentException('Exception/Throwable expected, got ' . gettype($e) . ' / ' . get_class($e));
        }

        $previousText = '';
        if ($previous = $e->getPrevious()) {
            do {
                $previousText .= ', ' . get_class($previous) . '(code: ' . $previous->getCode() . '): ' . $previous->getMessage() . ' at ' . $previous->getFile() . ':' . $previous->getLine();
            } while ($previous = $previous->getPrevious());
        }

        $str = '[object] (' . get_class($e) . '(code: ' . $e->getCode() . '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . $previousText . ')';
        if ($this->includeStacktraces) {
            $str .= "\n[stacktrace]\n" . $e->getTraceAsString();
        }

        return $str;
    }

    /**
     * @param $data
     * @return string
     */
    protected function convertToString($data)
    {
        if (null === $data || is_bool($data)) {
            return var_export($data, true);
        }

        if (is_scalar($data)) {
            return (string)$data;
        }

        if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
            return $this->toJson($data, true);
        }

        return str_replace('\\/', '/', @json_encode($data));
    }

    /**
     * @param string $str
     * @return string
     */
    protected function replaceNewlines($str)
    {
        return str_replace(array("\r\n", "\r", "\n"), ' ', $str);
    }
}
