<?php
/**
 * Base Exception Class
 *
 * Base exception for all Sequensy Pro plugin exceptions.
 *
 * @package Sequensy
 * @since 2.0.0
 */

declare(strict_types=1);

namespace Sequensy\Exceptions;

use Exception;
use Throwable;
use WP_Error;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Class SequensyException
 *
 * Base exception class with context data support.
 */
class SequensyException extends Exception {

	/**
	 * Additional context data for debugging.
	 *
	 * @var array
	 */
	protected array $context = array();

	/**
	 * Whether this error is retryable.
	 *
	 * @var bool
	 */
	protected bool $retryable = false;

	/**
	 * Constructor.
	 *
	 * @param string         $message  Error message.
	 * @param int            $code     Error code.
	 * @param array          $context  Additional context data.
	 * @param Throwable|null $previous Previous exception.
	 */
	public function __construct(
		string $message = '',
		int $code = 0,
		array $context = array(),
		?Throwable $previous = null
	) {
		parent::__construct( $message, $code, $previous );
		$this->context = $context;
	}

	/**
	 * Get context data.
	 *
	 * @return array Context data.
	 */
	public function get_context(): array {
		return $this->context;
	}

	/**
	 * Check if this error is retryable.
	 *
	 * @return bool True if retryable.
	 */
	public function is_retryable(): bool {
		return $this->retryable;
	}

	/**
	 * Convert exception to WP_Error.
	 *
	 * @return WP_Error WP_Error representation.
	 */
	public function to_wp_error(): WP_Error {
		return new WP_Error(
			$this->get_error_code(),
			$this->getMessage(),
			array_merge(
				$this->context,
				array(
					'exception_code' => $this->getCode(),
					'retryable'      => $this->retryable,
				)
			)
		);
	}

	/**
	 * Get the WP_Error code for this exception.
	 *
	 * @return string Error code.
	 */
	protected function get_error_code(): string {
		return 'sequensy_error';
	}

	/**
	 * Create exception from WP_Error.
	 *
	 * @param WP_Error|mixed $error WP_Error object.
	 * @return static Exception instance.
	 */
	public static function from_wp_error( mixed $error ): static {
		if ( ! is_wp_error( $error ) ) {
			return new static( 'Unknown error' );
		}

		return new static(
			$error->get_error_message(),
			0,
			array(
				'wp_error_code' => $error->get_error_code(),
				'wp_error_data' => $error->get_error_data(),
			)
		);
	}

	/**
	 * Log this exception.
	 *
	 * @param string $level Log level (error, warning, info).
	 */
	public function log( string $level = 'error' ): void {
		if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
			return;
		}

		$log_message = sprintf(
			'Sequensy Pro %s [%s]: %s | Context: %s',
			ucfirst( $level ),
			get_class( $this ),
			$this->getMessage(),
			wp_json_encode( $this->context )
		);

		error_log( $log_message );
	}
}
