<?php
namespace Engine\Logger;

/**
 * Abstract class for logging errors
 * @author dan
 *
 * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * @see http://tools.ietf.org/html/rfc5424   The Syslog Message Severities
 */
abstract class GeneralAbstract
{

	/**
	 * Log Session
	 */
	protected $logSession = null;


	/**
	 * Constructor
	 */
	public function __construct()
	{
	}

	protected function generateLogSession()
	{
		if (php_sapi_name()=='cli') {
			$logSession = sprintf("%s-%04s-%08s", \Engine\NumberConvertor::baseEncode(TIMESTAMP), \Engine\NumberConvertor::baseEncode(getmypid()), \Engine\NumberConvertor::baseEncode(mt_rand()));
		}
		else {
			$logSession = sprintf("%s-%04s-%09s-%08s", \Engine\NumberConvertor::baseEncode(TIMESTAMP), \Engine\NumberConvertor::baseEncode(getmypid()), substr(session_id(), 0, 9), \Engine\NumberConvertor::baseEncode(mt_rand()));
		}

		return $logSession;
	}


	/**
	 * Set the log session to be reported
	 * @param string $logSession
	 * @return \Engine\Logger\GeneralAbstract
	 */
	public function setLogSession($logSession)
	{
		$this->logSession = $logSession;
		return $this;
	}

	/**
	 * Get the log session
	 */
	public function getLogSession($autogenerate=true)
	{
		if ($autogenerate && !$this->logSession) {
			$this->logSession = $this->generateLogSession();
		}
		return $this->logSession;
	}

	/**
	 * Critical conditions
	 * @param array $context
	 */
	public function critical($message, array $context = [])
	{
		return $this->log('critical', $message, $context);
	}

	/**
	 * Error conditions
	 * @param array $context
	 */
	public function error($message, array $context = [])
	{
		return $this->log('error', $message, $context);
	}

	/**
	 * Warning conditions
	 * @param array $context
	 */
	public function warning($message, array $context = [])
	{
		return $this->log('warning', $message, $context);
	}

	/**
	 * Informational messages
	 * @param array $context
	 */
	public function info($message, array $context = [])
	{
		return $this->log('info', $message, $context);
	}

	/**
	 * Constructing and logging the error message
	 * @param Array $context
	 */
	public function log($level, $message, array $context = [])
	{
		// check that the error level is valid
		if (!in_array($level, ['critical', 'error', 'warning', 'info'])) {
			throw new \Exception("Invalid error level: {$level}");
		}

		// stacktrace
		$stacktrace = '';
		if (isset($context['exception']) && ($context['exception'] instanceof \Exception)) {
			$exceptionMessage = '';
			// Entity Exception
			if (get_class($context['exception'])=='App\Entity\Exception') {
				foreach ($context['exception']->getMessagesCollector()->getMessages() as $msg) {
					$exceptionMessage .= sprintf("%s:%s ", $msg->getType(), $msg->getText());
				}
			}
			// normal Exception
			else {
				$exceptionMessage = $context['exception']->getMessage();
			}

			// get stacktrace from exception
			$stacktrace = $context['exception']->getTrace();

			$context['exception'] = sprintf("%s: '%s'. ", get_class($context['exception']), $exceptionMessage); // update exception in context params
		}

		// get stacktrace as reported by debug_backtrace if this was not an exception
		if (!$stacktrace) {
			$stacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
		}

		// save the message
		try {
			return $this->writeRawMessage([
				'level' => $level,
				'message' => $message,
				'context' => $context,
				'stacktrace' => $stacktrace,
				'idUser' => CURRENT_ID_USER,
				'addTime' => date('Y-m-d H:i:s'),
				'logSession' => $this->getLogSession(true),
			]);
		}
		catch (\RuntimeException $ex) {
			// ignore errors
		}
	}

	/**
	 * Logging database errors
	 * This function attaches the last sql query & error message to the context, then continues with error()
	 * @param array $context
	 * @return boolean
	 */
	public function dbError($message, array $context = [], $dbErrorNo=null, $dbErrorMsg=null, $dbLastQuery=null)
	{
		$db = \DB::getInstance();

		// last SQL query
		$context = array_merge([
			'ErrorMsg' => 	new \Engine\Logger\Context\StringRaw(sprintf("[%d] %s", ($dbErrorNo!==null?$dbErrorNo:$db->ErrorNo()), ($dbErrorMsg!==null?$dbErrorMsg:$db->ErrorMsg()))),
			'query'=> 		new \Engine\Logger\Context\Sql(($dbLastQuery!==null?$dbLastQuery:\DB::getLastQuery())),
		], $context);
		ksort($context);

		return $this->error($message, $context);
	}

	/**
	 * Save the error message
	 * @param String $level
	 * @param String $message
	 * @param Array $context
	 * @param String $extendedMessage
	 * @param Array $stacktrace
	 * @param Integer $idUser
	 * @param String $logSession
	 */
	abstract public function writeRawMessage($fields);
}
