<?php

/*
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright 2005-2016 Zarafa Deutschland GmbH
 * SPDX-FileCopyrightText: Copyright 2020-2024 grommunio GmbH
 */

/**
 * Defines a base exception class for all custom exceptions, so every exceptions that
 * is thrown/caught by this application should extend this base class and make use of it.
 *
 * Some basic function of Exception class
 * getMessage()- message of exception
 * getCode() - code of exception
 * getFile() - source filename
 * getLine() - source line
 * getTrace() - n array of the backtrace()
 * getTraceAsString() - formatted string of trace
 */
class BaseException extends Exception {
	/**
	 * Base name of the file, so we don't have to use static path of the file.
	 *
	 * @var null|string
	 */
	private $baseFile;

	/**
	 * Flag to check if exception is already handled or not.
	 *
	 * @var bool
	 */
	public $isHandled = false;

	/**
	 * Flag for allow to exception details message or not.
	 *
	 * @var bool
	 */
	public $allowToShowDetailsMessage = false;

	/**
	 * The exception title to show as a message box title at client side.
	 *
	 * @var null|string
	 */
	public $title;

	/**
	 * The notification type by which exception needs to be shown at client side.
	 *
	 * @var string
	 */
	public $notificationType = "";

	/**
	 * The exception message to show at client side.
	 *
	 * @var null|string
	 */
	public $displayMessage;

	/**
	 * Constructs the exception.
	 */
	public function __construct(string $errorMessage, int $code = 0, ?Throwable $previous = null, ?string $displayMessage = null) {
		parent::__construct($errorMessage, (int) $code, $previous);
		$this->displayMessage = $displayMessage;
	}

	/**
	 * Returns file name and line number combined where exception occurred.
	 */
	public function getFileLine(): string {
		return $this->getBaseFile() . ':' . $this->getLine();
	}

	/**
	 * Returns message that should be sent to client to display.
	 */
	public function getDisplayMessage(): string {
		return $this->displayMessage ?? $this->getMessage();
	}

	/**
	 * Sets display message of an exception that will be sent to the client side
	 * to show it to user.
	 */
	public function setDisplayMessage(string $message): void {
		$this->displayMessage = $message . " (" . mapi_strerror($this->getCode()) . ")";
	}

	/**
	 * Sets the  title of an exception that will be sent to the client side
	 * to show it to user.
	 */
	public function setTitle(string $title): void {
		$this->title = $title;
	}

	/**
	 * Returns title that should be sent to client to display as a message box title.
	 */
	public function getTitle(): ?string {
		return $this->title;
	}

	/**
	 * Sets a flag in exception class to indicate that exception is already handled
	 * so if it is caught again in the top level of function stack
	 * then we have to silently ignore it.
	 */
	public function setHandled(): void {
		$this->isHandled = true;
	}

	/**
	 * Returns whether this exception has been handled.
	 */
	public function isHandled(): bool {
		return $this->isHandled;
	}

	/**
	 * Returns base path of the file where exception occurred.
	 */
	public function getBaseFile(): string {
		return $this->baseFile ??= basename(parent::getFile());
	}

	/**
	 * Returns the name of the class of exception.
	 */
	public function getName(): string {
		return static::class;
	}

	/**
	 * Sets a type of notification by which exception needs to be shown at client side.
	 */
	public function setNotificationType(string $notificationType): void {
		$this->notificationType = $notificationType;
	}

	/**
	 * Returns a type of notification to show an exception.
	 */
	public function getNotificationType(): string {
		return $this->notificationType;
	}

	/**
	 * Sets the flag to allow showing detailed exception messages.
	 */
	public function setShowDetailsMessage(): void {
		$this->allowToShowDetailsMessage = true;
	}

	/**
	 * Returns details of the error message if allowToShowDetailsMessage is set.
	 * Includes MAPI error code, exception message, JSON request, and backtrace.
	 *
	 * @return string returns details error message
	 */
	public function getDetailsMessage(): string {
		if (!$this->allowToShowDetailsMessage) {
			return '';
		}

		return 'MAPIException Code [' . get_mapi_error_name($this->getCode()) . ']' .
			PHP_EOL . PHP_EOL . 'Message: ' . $this->getMessage() .
			PHP_EOL . PHP_EOL . 'Error: ' . mapi_strerror($this->getCode()) .
			PHP_EOL . PHP_EOL . 'BaseException in ' . $this->getFile() . ':' . $this->getLine() .
			PHP_EOL . PHP_EOL . 'Stack trace:' .
			PHP_EOL . PHP_EOL . $this->getTraceAsString();
	}
}
