<?php
/**
 * Shared OAuth module settings helpers (secret masking, redirect validation).
 *
 * @package WP_PowerSuite\OAuth\Helpers
 */

namespace WP_PowerSuite\OAuth\Helpers;

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

final class OAuthSettingsHelper {

	public const SECRET_MASK = '••••••••••••';

	public const SOCIAL_MODULE_IDS = [
		'google-sign-in',
		'facebook-sign-in',
		'github-sign-in',
	];

	public static function isSocialModule( string $module_id ): bool {
		return in_array( $module_id, self::SOCIAL_MODULE_IDS, true );
	}

	public static function secretSettingKey( string $module_id ): string {
		return $module_id === 'facebook-sign-in' ? 'app_secret' : 'client_secret';
	}

	public static function secretFieldValue( string $stored ): string {
		return trim( $stored ) !== '' ? self::SECRET_MASK : '';
	}

	public static function hasSecretStored( string $stored ): bool {
		return trim( $stored ) !== '';
	}

	/**
	 * Preserve existing secret when the UI submits the mask or an empty value.
	 *
	 * @param array<string, mixed> $settings
	 * @param array<string, mixed> $existing
	 * @return array<string, mixed>
	 */
	public static function preserveSecretOnSave( array $settings, array $existing, string $module_id ): array {
		$key = self::secretSettingKey( $module_id );
		if ( ! array_key_exists( $key, $settings ) ) {
			return $settings;
		}
		$val = trim( (string) $settings[ $key ] );
		if ( $val === self::SECRET_MASK || $val === '' ) {
			$settings[ $key ] = ( is_array( $existing ) && isset( $existing[ $key ] ) )
				? (string) $existing[ $key ]
				: '';
		} else {
			$settings[ $key ] = sanitize_text_field( $val );
		}
		return $settings;
	}

	/**
	 * @param array<string, mixed> $settings
	 * @return array<string, mixed>
	 */
	public static function redactSecretsForResponse( array $settings, string $module_id ): array {
		$key = self::secretSettingKey( $module_id );
		if ( ! empty( $settings[ $key ] ) && is_string( $settings[ $key ] ) ) {
			$settings[ $key ] = self::SECRET_MASK;
		}
		return $settings;
	}

	/**
	 * @param array<string, mixed> $settings
	 * @param array<string, mixed> $existing
	 * @return array<string, mixed>
	 */
	public static function sanitizeRedirectAfterLogin( array $settings, array $existing ): array {
		if ( ! array_key_exists( 'redirect_after_login', $settings ) ) {
			return $settings;
		}
		$r = trim( (string) $settings['redirect_after_login'] );
		if ( $r === '' ) {
			$settings['redirect_after_login'] = '';
			return $settings;
		}
		$validated = wp_validate_redirect( $r, false );
		if ( is_string( $validated ) && $validated !== '' ) {
			$settings['redirect_after_login'] = $validated;
			return $settings;
		}
		if ( UrlHelper::isSameSiteUrl( $r ) ) {
			$settings['redirect_after_login'] = esc_url_raw( $r );
			return $settings;
		}
		$settings['redirect_after_login'] = is_array( $existing )
			? trim( (string) ( $existing['redirect_after_login'] ?? '' ) )
			: '';
		return $settings;
	}

	public static function siteUsesHttps(): bool {
		return is_ssl() || str_starts_with( (string) home_url( '/' ), 'https://' );
	}

	/**
	 * Checkbox keys submitted with the module settings form (including explicit "0" / off).
	 *
	 * @return list<string>
	 */
	public static function booleanSettingKeys( string $module_id ): array {
		$registration = [
			'auto_register',
			'bypass_registration_setting',
			'store_avatar_url',
			'woo_login',
			'button_label_inline',
		];

		return match ( $module_id ) {
			'google-sign-in'   => $registration,
			'github-sign-in'   => array_merge( $registration, [ 'allow_signup' ] ),
			'facebook-sign-in' => [ 'allow_registration', 'bypass_registration_setting', 'woo_login', 'button_label_inline' ],
			default            => [],
		};
	}

	/**
	 * Apply explicit booleans from raw POST before merging with stored settings.
	 *
	 * Unchecked checkboxes are sent as "0" but generic sanitization drops false values,
	 * which would otherwise leave prior "on" values in place after array_merge().
	 *
	 * @param array<string, mixed> $settings     Sanitized settings from the request.
	 * @param array<string, mixed> $raw_settings Raw POST settings (unmerged).
	 * @return array<string, mixed>
	 */
	public static function normalizeModuleSettings( string $module_id, array $settings, array $raw_settings ): array {
		if ( ! self::isSocialModule( $module_id ) ) {
			return $settings;
		}

		foreach ( self::booleanSettingKeys( $module_id ) as $key ) {
			if ( array_key_exists( $key, $raw_settings ) ) {
				$settings[ $key ] = self::parseBoolean( $raw_settings[ $key ] );
			}
		}

		return $settings;
	}

	/**
	 * Normalize global social_accounts policy from raw POST (checkbox "0" values).
	 *
	 * @param array<string, mixed> $settings     Sanitized module settings.
	 * @param array<string, mixed> $raw_settings Raw POST settings.
	 * @return array<string, mixed>
	 */
	public static function normalizeSocialAccountsSettings( array $settings, array $raw_settings ): array {
		$raw_sa = $raw_settings['social_accounts'] ?? null;
		if ( ! is_array( $raw_sa ) ) {
			return $settings;
		}

		$sa = is_array( $settings['social_accounts'] ?? null ) ? $settings['social_accounts'] : [];

		foreach ( [
			'linking_enabled',
			'require_email_match_to_connect',
			'show_on_my_account',
			'oauth_debug',
		] as $bool_key ) {
			if ( array_key_exists( $bool_key, $raw_sa ) ) {
				$sa[ $bool_key ] = self::parseBoolean( $raw_sa[ $bool_key ] );
			}
		}

		if ( array_key_exists( 'email_on_login', $raw_sa ) ) {
			$email_on = sanitize_key( (string) $raw_sa['email_on_login'] );
			$sa['email_on_login'] = in_array( $email_on, [ 'auto', 'manual' ], true ) ? $email_on : 'auto';
		}

		$settings['social_accounts'] = $sa;

		return $settings;
	}

	public static function parseBoolean( mixed $value ): bool {
		if ( is_bool( $value ) ) {
			return $value;
		}
		if ( $value === null || $value === '' || $value === false || $value === 0 || $value === '0' ) {
			return false;
		}
		if ( is_string( $value ) && strcasecmp( $value, 'false' ) === 0 ) {
			return false;
		}

		return $value === true || $value === 1 || $value === '1'
			|| ( is_string( $value ) && strcasecmp( $value, 'true' ) === 0 );
	}
}
