<?php
/**
 * A class for getting and setting various plugin settings.
 *
 * @since 3.0.0
 */

declare(strict_types=1);
namespace FeData\FastEvents\Storage;

defined( 'ABSPATH' ) || exit;

use FeData\FastEvents\Logging\LoggerFactory;
use stdClass;

/**
 * A class for getting and setting various plugin settings.
 */
class PluginSettings {

	private const DEFAULT_MAILER  = '{"type":"mailer","provider":{"class":"FeData\\\\FastEvents\\\\Mail\\\\Provider\\\\LocalMailer"}}';
	private const DEFAULT_PAYMENT = '{"type":"payment","provider":{"class":"FeData\\\\FastEvents\\\\Payment\\\\Provider\\\\MollieProvider","live_key":"","test_key":""}}';

	/**
	 * Payment configuration
	 *
	 * @since 3.0.0
	 * @var stdClass
	 */
	private stdClass $payment_provider;

	/**
	 * Email configuration
	 *
	 * @since 3.0.0
	 * @var stdClass
	 */
	private stdClass $mail_provider;

	/**
	 * Settings of this plugin
	 *
	 * @since 3.0.0
	 * @var stdClass
	 */
	private stdClass $settings;

	/**
	 * Options constructor.
	 *
	 * @since 3.0.0
	 *
	 * @param Options $options Options class.
	 */
	public function __construct( private readonly Options $options ) {
		$this->settings = $this->options->get_plugin_settings();
	}

	/**
	 * Singletons should not be cloneable.
	 *
	 * @since 2.5.0
	 */
	private function __clone(): void {
	}

	/**
	 * Prevent from being unserialized.
	 *
	 * @since 2.5.0
	 */
	public function __wakeup(): void {
	}

	/**
	 * Reload the settings from the database.
	 *
	 * @since 3.0
	 */
	public function reload_settings(): void {
		$this->settings = $this->options->get_plugin_settings();
	}

	/**
	 * Get the payment configuration
	 *
	 * @since 3.0
	 *
	 * @param bool $formatted If true, return a pretty printed formatted string.
	 * @return string|stdClass
	 */
	public function payment_config( bool $formatted = false ): string|stdClass {
		$payment = json_decode( $this->settings->payment_provider ?? self::DEFAULT_PAYMENT );
		if ( json_last_error() !== JSON_ERROR_NONE ) { // Invalid JSON.
			error_log( 'Invalid JSON in the payment configuration: ' . json_last_error_msg() ); // phpcs:ignore
			$payment = json_decode( self::DEFAULT_PAYMENT );
		}
		if ( $formatted ) {
			return wp_json_encode( $payment, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
		}
		$this->payment_provider = $payment;
		return $payment;
	}

	/**
	 * Get a property from the payment configuration
	 *
	 * @since 3.0
	 *
	 * @param string $property The property name to get.
	 * @param string $type     The type of the property (string|int|bool).
	 * @return bool|int|string
	 */
	public function payment_property( string $property, string $type = 'string' ): bool|int|string {
		$value = $this->payment_provider->provider->{$property} ?? null;

		// Choose a default based on the requested type.
		$defaults = array(
			'string' => '',
			'int'    => 0,
			'bool'   => false,
		);

		// If the caller gave an unsupported type, fall back to string.
		$type = $defaults[ $type ] ?? $defaults['string'];

		// Cast the existing value to the desired type.
		return match ( $type ) {
			''     => (string) $value,
			0      => (int) $value,
			false  => (bool) $value,
		};
	}

	/**
	 * Get the test mode flag
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function payment_wait(): bool {
		return $this->settings->payment_config->wait ?? true;
	}

	/**
	 * Get the currency
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function currency(): string {
		return strtoupper( $this->settings->payment_config->currency ?? 'EUR' );
	}

	/**
	 * Get the currency symbol
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function currency_symbol(): string {
		return $this->settings->payment_config->currency_symbol ?? '€';
	}

	/**
	 * Get the refund costs
	 *
	 * @since 3.0
	 *
	 * @return float
	 */
	public function refund_costs(): float {
		return $this->settings->payment_config->refund_costs ?? 2.25;
	}

	/**
	 * Get the refund per (1=Order and 2=Ticket)
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function refund_per(): int {
		return $this->settings->payment_config->refund_per ?? 1;
	}

	/**
	 * Get the payment expire time in minutes.
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function expire(): int {
		return $this->settings->payment_config->expire ?? 10;
	}

	/**
	 * The cancel URL.
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function cancel_url(): string {
		return $this->settings->payment_config->cancel_url ?? home_url();
	}

	/**
	 * Get the errorpage
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function errorpage(): string {
		return $this->settings->payment_config->errorpage ?? home_url() . '/order-error/';
	}

	/**
	 * Get the saas mode
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function saas_mode(): bool {
		return $this->settings->payment_config->saas_mode ?? false;
	}

	/**
	 * Get the saas test mode flag
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function saas_test(): bool {
		return $this->settings->payment_config->saas_test ?? true;
	}

	/**
	 * Get the SaaS client id
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function saas_client_id(): string {
		return $this->settings->payment_config->saas_client_id ?? '';
	}

	/**
	 * Get the SaaS client secret
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function saas_client_secret(): string {
		return $this->settings->payment_config->saas_client_secret ?? '';
	}

	/**
	 * Get the SaaS client fee
	 *
	 * @since 3.0
	 *
	 * @return float
	 */
	public function saas_client_fee(): float {
		return $this->settings->payment_config->saas_client_fee ?? 2.25;
	}

	/**
	 * Get the SaaS client fee per (1=Order and 2=Ticket)
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function saas_client_fee_per(): int {
		return $this->settings->payment_config->saas_client_fee_per ?? 1;
	}

	/**
	 * Get the email configuration
	 *
	 * @since 3.0
	 *
	 * @param bool $formatted If true, return a pretty printed formatted string.
	 * @return string|stdClass
	 */
	public function mailer_config( bool $formatted = false ): string|stdClass {
		$mailer = json_decode( $this->settings->mail_provider ?? self::DEFAULT_MAILER );
		if ( json_last_error() !== JSON_ERROR_NONE ) { // Invalid JSON.
			error_log( 'Invalid JSON in the email configuration: ' . json_last_error_msg() ); // phpcs:ignore
			$mailer = json_decode( self::DEFAULT_MAILER );
		}
		if ( $formatted ) {
			return wp_json_encode( $mailer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
		}
		$this->mail_provider = $mailer;
		return $mailer;
	}

	/**
	 * Get a property from the email configuration
	 *
	 * @since 3.0
	 *
	 * @param string $property The property name to get.
	 * @param string $type     The type of the property (string|int|bool).
	 * @return bool|int|string
	 */
	public function mailer_property( string $property, string $type = 'string' ): bool|int|string {
		$value = $this->mail_provider->provider->{$property} ?? null;

		// Choose a default based on the requested type.
		$defaults = array(
			'string' => '',
			'int'    => 0,
			'bool'   => false,
		);

		// If the caller gave an unsupported type, fall back to string.
		$type = $defaults[ $type ] ?? $defaults['string'];

		// Cast the existing value to the desired type.
		return match ( $type ) {
			''     => (string) $value,
			0      => (int) $value,
			false  => (bool) $value,
		};
	}

	/**
	 * Get the email sender name
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function sender_name(): string {
		return $this->settings->email_sender->name ?? (string) get_bloginfo( 'name' );
	}

	/**
	 * Get the email sender name
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function sender_email(): string {
		$urlparts = wp_parse_url( home_url() );
		$domain   = preg_replace( '/^www\./', '', $urlparts['host'] );
		return $this->settings->email_sender->email ?? 'info@' . $domain;
	}

	/**
	 * Get the email sender name
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function email_retries(): bool {
		return $this->settings->email_retries->enabled ?? false;
	}

	/**
	 * Get the email retry scheme
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function email_retry_scheme(): string {
		return $this->settings->email_retries->retry_scheme ?? '2,4,8,16,32,64,128';
	}

	/**
	 * Get the email webhooks flag
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function email_webhooks(): bool {
		return $this->settings->email_retries->webhooks ?? false;
	}

	/**
	 * Get the email webhooks flag
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function email_queue(): bool {
		return $this->settings->email_retries->queues ?? false;
	}


	/**
	 * Get the Captcha provider (0=Google, 1=Cloudflare)
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function captcha_provider(): int {
		return $this->settings->captcha->provider ?? 0;
	}

	/**
	 * Get the Captcha Site key
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function captcha_site_key(): string {
		return $this->settings->captcha->site_key ?? '';
	}

	/**
	 * Get the Captcha Secret key
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function captcha_secret_key(): string {
		return $this->settings->captcha->secret_key ?? '';
	}

	/**
	 * Get the admin api key
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function admin_api_key(): string {
		return $this->settings->fe_admin_api_key ?? '';
	}

	/**
	 * Get the Action Schedular purge days
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function as_purge_days(): int {
		return $this->settings->action_scheduler->purge_days ?? 30;
	}

	/**
	 * Get the Action Schedular time limit
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function as_time_limit(): int {
		return $this->settings->action_scheduler->time_limit ?? 30;
	}

	/**
	 * Get the Action Schedular batch size
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function as_batch_size(): int {
		return $this->settings->action_scheduler->batch_size ?? 25;
	}

	/**
	 * Get the Action Schedular concurrent batches
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function as_concurrent_batches(): int {
		return $this->settings->action_scheduler->concurrent_batches ?? 1;
	}

	/**
	 * Get the Action Schedular purge days
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function as_additional_runners(): int {
		return $this->settings->action_scheduler->additional_runners ?? 0;
	}

	/**
	 * Get the log level for the DB logger
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function log_level(): string {
		return strtolower( $this->settings->logging->level ?? 'notice' );
	}

	/**
	 * Get the #days to keep the logentries.
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function log_retention_days(): int {
		return $this->settings->logging->log_retention_days ?? 30;
	}

	/**
	 * Get the Monolog configuration
	 *
	 * @since 3.0
	 *
	 * @param bool $formatted If true, return a pretty printed formatted string.
	 * @return string|stdClass
	 */
	public function monolog_config( bool $formatted = false ): string|stdClass {
		$logging = json_decode( $this->settings->logging->monolog ?? '{"type":"monolog","channel":"fast-events","handlers":[]}' );
		if ( json_last_error() !== JSON_ERROR_NONE ) { // Invalid JSON.
			error_log( 'Invalid JSON in the logging configuration: ' . json_last_error_msg() ); // phpcs:ignore
			$logging = json_decode( '{"type":"monolog","channel":"fast-events","handlers":[]}' );
		}
		if ( $formatted ) {
			return wp_json_encode( $logging, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
		}
		return $logging;
	}

	/**
	 * Get the orders clean-up flag
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function misc_orders_cleanups(): bool {
		return $this->settings->misc->orders_cleanup ?? false;
	}

	/**
	 * Get the #seconds to cache statistic queries.
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function misc_cache_time(): int {
		return $this->settings->misc->stats_queries_cache_time ?? 60;
	}

	/**
	 * Get the custom order status
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function misc_custom_order_status(): string {
		return $this->settings->misc->custom_order_status ?? 'processing,shipping';
	}

	/**
	 * Get the ordering api flag
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function misc_ordering_api(): bool {
		return $this->settings->misc->ordering_api ?? false;
	}

	/**
	 * Get the #seconds to cache the order form.
	 *
	 * @since 3.0
	 *
	 * @return int
	 */
	public function misc_cache_time_order_form(): int {
		return $this->settings->misc->order_form_cache_time ?? 60;
	}
}
