<?php
/**
 * Fundraiser Settings Manager
 *
 * Manages integration with the Ambassador Hub settings system and provides
 * helpers for fundraiser-specific settings such as depth labels and permissions.
 *
 * @package AmbassadorHub\Fundraisers\Admin\Settings
 */

namespace AmbassadorHub\Fundraisers\Admin\Settings;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AmbassadorHub\Admin\Settings\Fields;
use AmbassadorHub\Admin\Settings\Registration;
use AmbassadorHub\Fundraisers\Admin\Settings\Tabs\FundraiserTab;

use function add_filter;
use function apply_filters;
use function do_action;
use function get_option;
use function update_option;
use function __;

/**
 * Registers fundraiser settings tabs and exposes helpers for depth labels
 * and permission checks used throughout the Fundraisers module.
 */
final class SettingsManager {

	// -------------------------------------------------------------------------
	// Bootstrap
	// -------------------------------------------------------------------------

	/**
	 * Initialize the Settings Manager.
	 *
	 * @return void
	 */
	public static function init(): void {

		add_filter( 'ambh_settings_tabs', array( self::class, 'register_tabs' ), 10, 1 );

		add_action( 'ambh_register_settings', array( self::class, 'register_settings' ) );

		add_action( 'ambh_settings_advanced_page_fields', array( self::class, 'render_create_fundraiser_page_setting' ) );
		add_action( 'ambh_fundraisers_settings_fields', array( self::class, 'render_fundraiser_fields' ) );
		add_action( 'ambh_fundraisers_settings_fields', array( self::class, 'render_top_level_creation_setting' ) );

		// Enqueue currency input assets on the settings page (hooked late to ensure registration ran first).
		add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_currency_input_assets_on_settings' ) );

		/**
		 * Fires when fundraiser settings manager is initialized.
		 *
		 * @since 0.1.0
		 */
		do_action( 'ambh_fundraisers_settings_init' );
	}

	// -------------------------------------------------------------------------
	// General Tab Integration
	// -------------------------------------------------------------------------


	/**
	 * Register the Account page setting.
	 *
	 * Allows admins to select which page contains the Account/My Account functionality.
	 * This page should have the Account block placed on it.
	 *
	 * @return void
	 */
	public static function render_create_fundraiser_page_setting(): void {
		Fields::page_selector(
			'ambh_create_fundraiser_page_id',
			esc_html__( 'Create Fundraiser Page', 'ambassadorhub' ),
			esc_html__( 'Select the page where the Create Fundraiser block will be placed. Ambassadors will access the fundraiser creation form on this page.', 'ambassadorhub' )
		);
	}

	/**
	 * Get the configured Create Fundraiser page ID.
	 *
	 * @return int|false Page ID or false if not configured.
	 */
	public static function get_create_fundraiser_page_id() {
		$page_id = get_option( 'ambh_create_fundraiser_page_id', 0 );
		return $page_id ? (int) $page_id : false;
	}

	/**
	 * Check if Create Fundraiser page is configured.
	 *
	 * @return bool True if configured, false otherwise.
	 */
	public static function is_create_fundraiser_page_configured(): bool {
		return (bool) self::get_create_fundraiser_page_id();
	}

	/**
	 * Get the URL of the Create Fundraiser page.
	 *
	 * @return string Page URL or home URL if not configured.
	 */
	public static function get_create_fundraiser_page_url(): string {
		$page_id = self::get_create_fundraiser_page_id();
		return $page_id ? get_permalink( $page_id ) : home_url();
	}

	public static function register_settings(): void {

		// Registered under ambh_advanced (not ambh_fundraisers): it's rendered
		// via the ambh_settings_advanced_page_fields hook inside that tab's
		// own <form>/nonce, alongside the other page-ID settings in
		// Registration::register_advanced_settings() and Accounts'
		// SettingsManager — this one was missing entirely, so it was neither
		// sanitized nor persisted by the Settings API on save.
		register_setting(
			'ambh_advanced',
			'ambh_create_fundraiser_page_id',
			array(
				'sanitize_callback' => array( Registration::class, 'sanitize_page_id' ),
				'default'           => 0,
				'type'              => 'integer',
			)
		);

		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_currency',
			array(
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => 'EUR',
				'type'              => 'string',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_min_target',
			array(
				'sanitize_callback' => 'absint',
				'default'           => 100,
				'type'              => 'integer',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_min_donation',
			array(
				'sanitize_callback' => array( self::class, 'normalize_amount' ),
				'default'           => 1.0,
				'type'              => 'number',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_max_donation',
			array(
				'sanitize_callback' => array( self::class, 'normalize_amount' ),
				'default'           => 0,
				'type'              => 'number',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_enable_comments',
			array(
				'sanitize_callback' => array( self::class, 'sanitize_checkbox_string' ),
				'default'           => '0',
				'type'              => 'string',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_predefined_amounts',
			array(
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => '',
				'type'              => 'string',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_allow_anonymous',
			array(
				'sanitize_callback' => array( self::class, 'sanitize_checkbox_string' ),
				'default'           => '0',
				'type'              => 'string',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_collect_address',
			array(
				'sanitize_callback' => array( self::class, 'sanitize_checkbox_string' ),
				'default'           => '1',
				'type'              => 'string',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_closed_message',
			array(
				'sanitize_callback' => 'sanitize_textarea_field',
				'default'           => '',
				'type'              => 'string',
			)
		);
		register_setting(
			'ambh_fundraisers',
			'ambh_fundraisers_ambassadors_can_create_top_level',
			array(
				'sanitize_callback' => array( self::class, 'sanitize_checkbox_string' ),
				'default'           => '0',
				'type'              => 'string',
			)
		);
	}

	/**
	 * Render fundraiser general settings inside the plugin's General tab.
	 *
	 * Hooked to 'ambh_settings_general_fields' which fires inside the General
	 * tab's <form> (action="options.php", group "ambh_general"). Settings are
	 * saved automatically by the WP Settings API.
	 *
	 * @return void
	 */
	public static function render_fundraiser_fields(): void {
		?>
		<h3 style="margin:0;padding:1em 0 0;"><?php esc_html_e( 'Fundraiser Settings', 'ambassadorhub' ); ?></h3>
		<table class="form-table" role="presentation">
			<tbody>

				<?php

					Fields::textarea(
						'ambh_fundraisers_closed_message',
						esc_html__( 'Closed Fundraiser Message', 'ambassadorhub' ),
						esc_html__( 'Message displayed when a fundraiser is closed or has ended. Leave empty to use the default message.', 'ambassadorhub' ),
						3,
						__( 'This fundraiser is no longer accepting donations.', 'ambassadorhub' )
					);

				?>

			</tbody>
		</table>

		<h3 style="margin:0;padding:1em 0 0;"><?php esc_html_e( 'Donation Settings', 'ambassadorhub' ); ?></h3>
		<table class="form-table" role="presentation">
			<tbody>

				<?php

				Fields::select(
					'ambh_fundraisers_currency',
					esc_html__( 'Default Currency', 'ambassadorhub' ),
					array(
						'EUR' => esc_html__( 'Euro (€)', 'ambassadorhub' ),
						'USD' => esc_html__( 'US Dollar ($)', 'ambassadorhub' ),
						'GBP' => esc_html__( 'British Pound (£)', 'ambassadorhub' ),
					),
					esc_html__( 'Default currency for new fundraisers.', 'ambassadorhub' )
				);

				Fields::text(
					'ambh_fundraisers_min_target',
					esc_html__( 'Minimum Target Amount', 'ambassadorhub' ),
					esc_html__( 'Minimum target amount for fundraisers (0 for no minimum).', 'ambassadorhub' ),
					'number',
					array(
						'min'  => '0',
						'step' => '1',
					)
				);

				Fields::text(
					'ambh_fundraisers_min_donation',
					esc_html__( 'Minimum Donation Amount', 'ambassadorhub' ),
					esc_html__( 'Set the minimum donation amount that can be accepted (in default currency). Leave empty for no minimum.', 'ambassadorhub' ),
					'text',
					array(
						'class' => 'ambh-currency-input',
					)
				);

				Fields::text(
					'ambh_fundraisers_max_donation',
					esc_html__( 'Maximum Donation Amount', 'ambassadorhub' ),
					esc_html__( 'Set the maximum donation amount that can be accepted (in default currency). Set to 0 for no maximum.', 'ambassadorhub' ),
					'text',
					array(
						'class' => 'ambh-currency-input',
					)
				);

				Fields::text(
					'ambh_fundraisers_predefined_amounts',
					esc_html__( 'Predefined Donation Amounts', 'ambassadorhub' ),
					esc_html__( 'Comma-separated amounts (e.g. 10,25,50,100). Leave empty for only a free input field.', 'ambassadorhub' )
				);

				Fields::checkbox(
					'ambh_fundraisers_allow_anonymous',
					esc_html__( 'Allow Anonymous Donations', 'ambassadorhub' ),
					esc_html__( 'Allow donors to donate anonymously.', 'ambassadorhub' )
				);

				Fields::checkbox(
					'ambh_fundraisers_collect_address',
					esc_html__( 'Collect Donor Address', 'ambassadorhub' ),
					esc_html__( 'Ask donors for their street address, postal code, city and country on the donation form.', 'ambassadorhub' )
				);
				?>

			</tbody>
		</table>
		<?php
	}

	/**
	 * Render the "ambassadors can create top-level fundraisers" toggle.
	 *
	 * Hooked to 'ambh_fundraisers_settings_fields'.
	 *
	 * @return void
	 */
	public static function render_top_level_creation_setting(): void {
		?>
		<h3 style="margin:0;padding:1em 0 0;"><?php esc_html_e( 'Fundraiser Creation', 'ambassadorhub' ); ?></h3>
		<table class="form-table" role="presentation">
			<tbody>
				<?php
				Fields::checkbox(
					'ambh_fundraisers_ambassadors_can_create_top_level',
					esc_html__( 'Allow ambassadors to create root fundraisers', 'ambassadorhub' ),
					esc_html__( 'When enabled, ambassadors can create new top-level fundraisers. When disabled, they can only join existing ones.', 'ambassadorhub' )
				);
				?>
			</tbody>
		</table>
		<?php
	}

	/**
	 * Returns true when ambassadors are allowed to create their own root fundraisers.
	 *
	 * @return bool
	 */
	public static function ambassadors_can_create_top_level(): bool {
		return self::get_setting( 'ambassadors_can_create_top_level', '0' ) === '1';
	}

	/**
	 * Get the predefined donation amounts as a string (comma-separated).
	 *
	 * @return string
	 */
	public static function get_predefined_amounts(): string {
		return (string) self::get_setting( 'predefined_amounts' );
	}

	/**
	 * Get predefined donation amounts as an array of floats.
	 *
	 * @return float[]
	 */
	public static function get_predefined_amounts_array(): array {
		$raw = self::get_predefined_amounts();
		if ( ! $raw ) {
			return array();
		}
		$parts   = array_map( 'trim', explode( ',', $raw ) );
		$amounts = array();
		foreach ( $parts as $part ) {
			if ( is_numeric( $part ) ) {
				$amounts[] = (float) $part;
			}
		}
		return $amounts;
	}

	/**
	 * Check if anonymous donations are allowed.
	 *
	 * @return bool
	 */
	public static function allow_anonymous(): bool {
		return self::get_setting( 'allow_anonymous', '0' ) === '1';
	}

	/**
	 * Check if the donation form should collect the donor's address.
	 *
	 * Defaults to true so existing sites keep their current behaviour.
	 *
	 * @return bool
	 */
	public static function collect_address(): bool {
		return self::get_setting( 'collect_address', '1' ) === '1';
	}

	/**
	 * Get the message shown when a fundraiser is closed or has ended.
	 *
	 * Falls back to the default translatable string when no custom message is set.
	 *
	 * @return string
	 */
	public static function get_closed_message(): string {
		$msg = (string) self::get_setting( 'closed_message', '' );
		return '' !== $msg
			? $msg
			: __( 'This fundraiser is no longer accepting donations.', 'ambassadorhub' );
	}

	/**
	 * Get the configured default currency code (e.g. 'EUR', 'USD', 'GBP').
	 *
	 * @return string ISO 4217 currency code.
	 */
	public static function get_currency(): string {
		return (string) self::get_setting( 'currency', 'EUR' );
	}

	/**
	 * Get the currency symbol for the configured default currency.
	 *
	 * @return string Currency symbol (e.g. '€', '$', '£').
	 */
	public static function get_currency_symbol(): string {
		$symbols  = array(
			'EUR' => '€',
			'USD' => '$',
			'GBP' => '£',
		);
		$currency = self::get_currency();
		return $symbols[ $currency ] ?? $currency;
	}

	/**
	 * Get the number-format separators appropriate for the configured currency locale.
	 *
	 * EUR uses the continental European convention (decimal=',', thousands='.').
	 * USD and GBP use the Anglo-American convention (decimal='.', thousands=',').
	 *
	 * @return array{decimal: string, thousands: string}
	 */
	public static function get_currency_format(): array {
		$formats  = array(
			'EUR' => array(
				'decimal'   => ',',
				'thousands' => '.',
			),
			'USD' => array(
				'decimal'   => '.',
				'thousands' => ',',
			),
			'GBP' => array(
				'decimal'   => '.',
				'thousands' => ',',
			),
		);
		$currency = self::get_currency();
		return $formats[ $currency ] ?? array(
			'decimal'   => '.',
			'thousands' => ',',
		);
	}

	/**
	 * Format a number as a currency string using the configured currency symbol and locale separators.
	 *
	 * Example outputs:
	 *   EUR: €1.234,56
	 *   USD: $1,234.56
	 *   GBP: £1,234.56
	 *
	 * @param float $amount   The amount to format.
	 * @param int   $decimals Number of decimal places (default 2).
	 *
	 * @return string Formatted currency string.
	 */
	public static function format_currency( float $amount, int $decimals = 2 ): string {
		$format = self::get_currency_format();
		return self::get_currency_symbol() . number_format( $amount, $decimals, $format['decimal'], $format['thousands'] );
	}

	/**
	 * Normalise a user-entered currency string to a machine float.
	 *
	 * Handles both EUR (comma decimal / period thousands) and
	 * USD/GBP (period decimal / comma thousands) input formats so that
	 * values typed in the locale-specific format are stored correctly.
	 *
	 * Examples (EUR): '1.234,56' → 1234.56   '1,50' → 1.5   '100' → 100.0
	 * Examples (USD): '1,234.56' → 1234.56   '1.50' → 1.5
	 *
	 * @param string $value Raw user input.
	 * @return float Normalised float value.
	 */
	/**
	 * Sanitize a checkbox setting to a strict '1'/'0' string.
	 *
	 * Deliberately not `$value ? '1' : '0'`: plain PHP truthiness treats the
	 * string "false" (a value the Settings REST API, or any raw options.php
	 * POST, can plausibly send) as truthy, silently flipping the setting on.
	 * rest_sanitize_boolean() correctly treats "false"/"0"/0/''/null as false.
	 * Storage stays a '1'/'0' string (not a real bool) to match this option's
	 * existing 'type' => 'string' registration and the === '1' checks reading
	 * it elsewhere.
	 *
	 * @param mixed $value Raw submitted value.
	 * @return string '1' or '0'.
	 */
	public static function sanitize_checkbox_string( $value ): string {
		return rest_sanitize_boolean( $value ) ? '1' : '0';
	}

	public static function normalize_amount( string $value ): float {
		$format = self::get_currency_format();
		$value  = trim( $value );
		if ( '' === $value ) {
			return 0.0;
		}
		// Remove thousands separator then convert decimal separator to period.
		$value = str_replace( $format['thousands'], '', $value );
		$value = str_replace( $format['decimal'], '.', $value );
		return floatval( $value );
	}

	/**
	 * Format a stored float amount for display inside an HTML input field.
	 *
	 * Returns the value with the locale-specific decimal separator and no
	 * thousands separator (makes editing cleaner). Returns an empty string
	 * for zero/negative amounts so optional fields remain blank.
	 *
	 * Examples (EUR): 1234.5 → '1234,50'   0 → ''
	 * Examples (USD): 1234.5 → '1234.50'   0 → ''
	 *
	 * @param float $amount Stored float amount.
	 * @return string Display-formatted string for use as an input value.
	 */
	public static function format_amount_for_input( float $amount ): string {
		if ( $amount <= 0 ) {
			return '';
		}
		$format = self::get_currency_format();
		return number_format( $amount, 2, $format['decimal'], '' );
	}

	/**
	 * Return the currency config array used by the frontend JS utility.
	 *
	 * @return array{symbol: string, decimal: string, thousands: string}
	 */
	public static function get_currency_input_config(): array {
		$format = self::get_currency_format();
		return array(
			'symbol'    => self::get_currency_symbol(),
			'decimal'   => $format['decimal'],
			'thousands' => $format['thousands'],
		);
	}

	/**
	 * Enqueue the shared currency-input JS/CSS assets and localise them
	 * with the current currency configuration.
	 *
	 * Safe to call multiple times — wp_enqueue_* de-duplicates.
	 *
	 * @return void
	 */
	public static function enqueue_currency_input_assets(): void {
		if ( ! wp_script_is( 'ambh-currency-input', 'registered' ) ) {
			wp_register_script(
				'ambh-currency-input',
				AMBASSADOR_HUB_URL . 'assets/js/currency-input.js',
				array(),
				AMBASSADOR_HUB_VERSION,
				true
			);
		}
		if ( ! wp_style_is( 'ambh-currency-input', 'registered' ) ) {
			wp_register_style(
				'ambh-currency-input',
				AMBASSADOR_HUB_URL . 'assets/css/currency-input.css',
				array(),
				AMBASSADOR_HUB_VERSION
			);
		}
		wp_enqueue_script( 'ambh-currency-input' );
		wp_enqueue_style( 'ambh-currency-input' );
		wp_localize_script( 'ambh-currency-input', 'ambhCurrencyInput', self::get_currency_input_config() );
	}

	/**
	 * Enqueue currency assets on the Ambassador Hub settings admin pages.
	 *
	 * @return void
	 */
	public static function enqueue_currency_input_assets_on_settings(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
		if ( str_starts_with( $page, 'ambassadorhub' ) ) {
			self::enqueue_currency_input_assets();
		}
	}

	// -------------------------------------------------------------------------
	// Tab Registration
	// -------------------------------------------------------------------------

	/**
	 * Register fundraiser settings tabs.
	 *
	 * @param array $tabs Existing tabs.
	 *
	 * @return array Modified tabs array.
	 */
	public static function register_tabs( array $tabs ): array {
		$tabs['fundraisers'] = new FundraiserTab();

		/**
		 * Filter fundraiser settings tabs.
		 *
		 * @since 0.1.0
		 *
		 * @param array $tabs Settings tabs.
		 */
		return apply_filters( 'ambh_fundraisers_settings_tabs', $tabs );
	}

	// -------------------------------------------------------------------------
	// Generic Setting Helpers
	// -------------------------------------------------------------------------

	/**
	 * Get a fundraiser setting value.
	 *
	 * @param string $key           Setting key (without the ambh_fundraisers_ prefix).
	 * @param mixed  $default_value Default value.
	 *
	 * @return mixed Setting value.
	 */
	public static function get_setting( string $key, mixed $default_value = '' ): mixed {
		return get_option( 'ambh_fundraisers_' . $key, $default_value );
	}

	/**
	 * Update a fundraiser setting value.
	 *
	 * @param string $key   Setting key (without the ambh_fundraisers_ prefix).
	 * @param mixed  $value Setting value.
	 *
	 * @return bool True if saved, false otherwise.
	 */
	public static function update_setting( string $key, mixed $value ): bool {
		return update_option( 'ambh_fundraisers_' . $key, $value );
	}
}
