<?php
/**
 * Admin settings registration and sanitization.
 *
 * @package TrustLens\Admin
 * @since   1.1.5
 */

defined( 'ABSPATH' ) || exit;

/**
 * Handles admin settings registration and sanitize callbacks.
 *
 * @since 1.1.5
 */
class TrustLens_Admin_Settings {

	/**
	 * Register hooks.
	 *
	 * @since 1.1.5
	 * @return void
	 */
	public function register_hooks(): void {
		add_action( 'admin_init', array( $this, 'register_settings' ) );
		// (Re)schedule the network sync cron whenever the toggle changes.
		add_action( 'update_option_trustlens_module_store_network_enabled', array( $this, 'reconcile_network_cron' ), 10, 0 );
	}

	/**
	 * Register settings.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function register_settings(): void {
		// IMPORTANT: one option group per Settings form. WordPress core
		// (wp-admin/options.php) rewrites EVERY option in the submitted group — null
		// for any field absent from that form — so two forms must never share a group
		// or saving one tab wipes the others. Groups are registered unconditionally so
		// a form's group always exists whenever the form renders (Pro forms gate on
		// is_paying / can_use_pro, which need not match a feature guard here).

		// General tab.
		register_setting( 'trustlens_general', 'trustlens_min_orders', array( 'sanitize_callback' => array( $this, 'sanitize_min_orders' ) ) );
		register_setting( 'trustlens_general', 'trustlens_enable_blocking', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_general', 'trustlens_enable_dashboard', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_general', 'trustlens_enable_order_warning', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_general', 'trustlens_block_message', array( 'sanitize_callback' => 'sanitize_textarea_field' ) );
		register_setting( 'trustlens_general', 'trustlens_returns_high_threshold', array( 'sanitize_callback' => array( $this, 'sanitize_returns_high_threshold' ) ) );
		register_setting( 'trustlens_general', 'trustlens_returns_critical_threshold', array( 'sanitize_callback' => array( $this, 'sanitize_returns_critical_threshold' ) ) );

		// Payment Controls tab (Pro).
		register_setting( 'trustlens_payment_controls', 'trustlens_enable_payment_method_controls', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_segments', array( 'sanitize_callback' => array( $this, 'sanitize_payment_method_control_segments' ) ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_gateways', array( 'sanitize_callback' => array( $this, 'sanitize_payment_method_control_gateways' ) ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_min_total', array( 'sanitize_callback' => array( $this, 'sanitize_payment_method_control_min_total' ) ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_message', array( 'sanitize_callback' => 'sanitize_textarea_field' ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_velocity_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_velocity_count', array( 'sanitize_callback' => array( $this, 'sanitize_payment_method_control_velocity_count' ) ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_velocity_hours', array( 'sanitize_callback' => array( $this, 'sanitize_payment_method_control_velocity_hours' ) ) );
		register_setting( 'trustlens_payment_controls', 'trustlens_payment_method_control_linked_risk_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );

		// Modules tab — detection-module toggles + their inline config + the chargebacks enable toggle.
		register_setting( 'trustlens_modules', 'trustlens_module_returns_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_module_orders_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_module_coupons_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_module_categories_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_module_linked_accounts_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_linked_accounts_penalty', array( 'sanitize_callback' => array( $this, 'sanitize_linked_accounts_penalty' ) ) );
		register_setting( 'trustlens_modules', 'trustlens_module_shipping_anomalies_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_shipping_anomalies_velocity_days', array( 'sanitize_callback' => array( $this, 'sanitize_shipping_anomalies_velocity_days' ) ) );
		register_setting( 'trustlens_modules', 'trustlens_shipping_anomalies_trend_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_coupons_block_linked_abuse', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_modules', 'trustlens_coupons_max_first_order', array( 'sanitize_callback' => array( $this, 'sanitize_coupons_max_first_order' ) ) );
		register_setting( 'trustlens_modules', 'trustlens_module_chargebacks_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );

		// Chargeback Monitor settings (Pro).
		register_setting( 'trustlens_chargebacks', 'trustlens_chargebacks_auto_block', array( 'sanitize_callback' => array( $this, 'sanitize_chargebacks_auto_block' ) ) );
		register_setting( 'trustlens_chargebacks', 'trustlens_chargeback_alert_threshold_pct', array( 'sanitize_callback' => array( $this, 'sanitize_chargeback_alert_threshold_pct' ) ) );
		register_setting( 'trustlens_chargebacks', 'trustlens_report_verification', array( 'sanitize_callback' => 'rest_sanitize_boolean', 'default' => true ) );

		$notification_booleans = array(
			'trustlens_enable_notifications',
			'trustlens_notify_blocked_checkout',
			'trustlens_notify_welcome_summary',
			'trustlens_notify_weekly_summary',
		);

		if ( wstl_can_use_pro() ) {
			$notification_booleans = array_merge(
				$notification_booleans,
				array(
					'trustlens_notify_high_risk_order',
					'trustlens_notify_segment_critical',
					'trustlens_enable_daily_digest',
					'trustlens_notify_high_value_order',
					'trustlens_notify_repeat_refunder',
					'trustlens_notify_velocity',
					'trustlens_notify_score_recovery',
					'trustlens_notify_new_customer_risk',
					'trustlens_notify_monthly_report',
					'trustlens_notify_chargeback_filed',
				)
			);
		}

		foreach ( $notification_booleans as $option ) {
			register_setting( 'trustlens_notifications', $option, array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		}

		register_setting( 'trustlens_notifications', 'trustlens_notification_email', array( 'sanitize_callback' => 'sanitize_email' ) );

		if ( wstl_can_use_pro() ) {
			foreach (
				array(
					'trustlens_notify_high_value_threshold',
					'trustlens_notify_high_value_score',
					'trustlens_notify_repeat_refunder_count',
					'trustlens_notify_repeat_refunder_days',
					'trustlens_notify_velocity_count',
					'trustlens_notify_velocity_hours',
					'trustlens_notify_new_customer_risk_score',
				) as $option
			) {
				register_setting( 'trustlens_notifications', $option, array( 'sanitize_callback' => 'absint' ) );
			}
		}

		// Automation page (Pro).
		register_setting( 'trustlens_automation', 'trustlens_enable_automation', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );

		// Webhooks tab (Pro).
		register_setting( 'trustlens_webhooks', 'trustlens_enable_webhooks', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_webhooks', 'trustlens_webhook_events', array( 'sanitize_callback' => array( $this, 'sanitize_webhook_events' ) ) );

		// Reports tab (Pro).
		register_setting( 'trustlens_reports', 'trustlens_enable_scheduled_reports', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_reports', 'trustlens_weekly_report_recipients', array( 'sanitize_callback' => 'wstl_sanitize_email_list' ) );
		register_setting( 'trustlens_reports', 'trustlens_monthly_report_recipients', array( 'sanitize_callback' => 'wstl_sanitize_email_list' ) );

		// Data tab.
		register_setting( 'trustlens_data', 'trustlens_remove_data_on_uninstall', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );

		// Store Trust Network tab (Pro, multi-site). Its own option group — sharing
		// trustlens_modules would null the modules tab's fields on save.
		register_setting( 'trustlens_network', 'trustlens_module_store_network_enabled', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_network', 'trustlens_network_group_key', array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'trustlens_network', 'trustlens_network_peers', array( 'sanitize_callback' => array( $this, 'sanitize_network_peers' ) ) );
		register_setting( 'trustlens_network', 'trustlens_network_share_flags', array( 'sanitize_callback' => 'rest_sanitize_boolean' ) );
		register_setting( 'trustlens_network', 'trustlens_network_caution_penalty', array( 'sanitize_callback' => 'absint' ) );
		register_setting( 'trustlens_network', 'trustlens_network_high_penalty', array( 'sanitize_callback' => 'absint' ) );
	}

	/**
	 * Sanitize the peer-URL textarea down to clean https base URLs.
	 *
	 * @since 1.3.5
	 * @param string $raw Raw textarea value.
	 * @return string Newline-joined sanitized URLs.
	 */
	public function sanitize_network_peers( $raw ): string {
		if ( ! class_exists( 'TrustLens_Network_Sync' ) ) {
			return '';
		}
		return implode( "\n", array_map( 'esc_url_raw', TrustLens_Network_Sync::peer_urls( (string) $raw ) ) );
	}

	/**
	 * Schedule/unschedule the network sync cron when the toggle changes.
	 *
	 * @since 1.3.5
	 * @return void
	 */
	public function reconcile_network_cron(): void {
		if ( ! class_exists( 'TrustLens_Network_Sync' ) ) {
			return;
		}
		if ( get_option( 'trustlens_module_store_network_enabled', false ) && function_exists( 'wstl_can_use_network' ) && wstl_can_use_network() ) {
			TrustLens_Network_Sync::schedule();
		} else {
			TrustLens_Network_Sync::unschedule();
		}
	}

	public function sanitize_webhook_events( $value ): array {
		if ( ! is_array( $value ) ) {
			return array();
		}

		$allowed_events = array(
			'score_updated',
			'customer_blocked',
			'customer_unblocked',
			'customer_allowlisted',
			'customer_allowlist_removed',
			'checkout_blocked',
			'high_risk_order',
			'automation_triggered',
		);

		$events = array_map( 'sanitize_text_field', $value );

		return array_values( array_intersect( $events, $allowed_events ) );
	}

	public function sanitize_min_orders( $value ): int {
		$v = absint( $value );
		return max( 1, min( 20, $v ) );
	}

	public function sanitize_returns_high_threshold( $value ): int {
		$v = absint( $value );
		return max( 10, min( 90, $v ) );
	}

	public function sanitize_returns_critical_threshold( $value ): int {
		$v = absint( $value );
		$v = max( 20, min( 100, $v ) );
		$high = 40;
		if ( isset( $_POST['trustlens_returns_high_threshold'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Sanitized in options.php flow.
			$high = $this->sanitize_returns_high_threshold( absint( wp_unslash( $_POST['trustlens_returns_high_threshold'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		} else {
			$high = (int) get_option( 'trustlens_returns_high_threshold', 40 );
		}
		return max( $v, $high );
	}

	public function sanitize_linked_accounts_penalty( $value ): int {
		$v = absint( $value );
		return max( 0, min( 20, $v ) );
	}

	/**
	 * Sanitize shipping anomalies velocity days.
	 *
	 * @since 1.2.0
	 * @param mixed $value Input value.
	 * @return int Sanitized value.
	 */
	public function sanitize_shipping_anomalies_velocity_days( $value ): int {
		$value = (int) $value;
		return max( 7, min( 90, $value ) );
	}

	public function sanitize_coupons_max_first_order( $value ): int {
		$v = absint( $value );
		return max( 1, min( 20, $v ) );
	}

	public function sanitize_payment_method_control_segments( $value ): array {
		if ( ! is_array( $value ) ) {
			return array( 'risk', 'critical' );
		}

		$allowed = array( 'vip', 'trusted', 'normal', 'caution', 'risk', 'critical' );
		$segments = array_values( array_intersect( $allowed, array_map( 'sanitize_key', $value ) ) );

		return empty( $segments ) ? array( 'risk', 'critical' ) : $segments;
	}

	public function sanitize_payment_method_control_gateways( $value ): array {
		if ( ! is_array( $value ) ) {
			return array();
		}

		$gateways = array_values( array_filter( array_map( 'sanitize_key', $value ) ) );

		if ( ! class_exists( 'WC_Payment_Gateways' ) ) {
			return $gateways;
		}

		$available = array();
		foreach ( WC_Payment_Gateways::instance()->payment_gateways() as $gateway ) {
			if ( isset( $gateway->id ) ) {
				$available[] = sanitize_key( (string) $gateway->id );
			}
		}

		return array_values( array_intersect( $available, $gateways ) );
	}

	public function sanitize_payment_method_control_min_total( $value ): float {
		$amount = is_numeric( $value ) ? (float) $value : 0.0;
		$amount = max( 0.0, min( 9999999.0, $amount ) );
		return round( $amount, 2 );
	}

	public function sanitize_payment_method_control_velocity_count( $value ): int {
		$v = absint( $value );
		return max( 2, min( 50, $v ) );
	}

	public function sanitize_payment_method_control_velocity_hours( $value ): int {
		$v = absint( $value );
		return max( 1, min( 168, $v ) );
	}

	public function sanitize_chargebacks_auto_block( $value ): int {
		$v = absint( $value );
		return max( 0, min( 20, $v ) );
	}

	public function sanitize_chargeback_alert_threshold_pct( $value ): int {
		$v = absint( $value );
		return max( 50, min( 100, $v ) );
	}
}
