<?php
/**
 * User Registration Processor for SureForms
 *
 * @package sureforms-pro.
 * @since 1.8.0
 */

namespace SRFM_Pro\Inc\Business\User_Registration;

use SRFM\Inc\Helper;
use SRFM_Pro\Inc\Traits\Get_Instance;
use WP_Error;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * User Registration Processor class for SureForms
 * Handles user registration from form submissions
 *
 * @since 1.8.0
 */
class Processor {
	use Get_Instance;

	/**
	 * Store password temporarily during form processing
	 *
	 * @var string
	 * @since 1.8.0
	 */
	private static string $password = '';

	/**
	 * Store registrations data temporarily during form processing
	 *
	 * @var string
	 * @since 1.8.0
	 */
	private $registrations = '';

	/**
	 * Constructor
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function __construct() {
		// Use srfm_before_submission action to catch errors early.
		add_action( 'srfm_before_submission', [ $this, 'process_user_registration' ] );
		add_filter( 'srfm_before_prepare_submission_data', [ $this, 'remove_password_field_from_form_data' ] );
		add_filter( 'srfm_before_fields_processing', [ $this, 'handle_registration_data' ] );
		add_filter( 'srfm_update_prepared_submission_data', [ $this, 'add_registration_data_to_submission' ] );
	}

	/**
	 * Remove password field from form data to prevent it from being stored in the database
	 *
	 * @param array<string,mixed> $submission_data The form data.
	 *
	 * @since 1.8.0
	 * @return array<string,mixed>
	 */
	public function remove_password_field_from_form_data( $submission_data ) {
		foreach ( $submission_data as $key => $value ) {
			if ( strpos( $key, 'srfm-password' ) === 0 ) {
				self::$password = is_string( $value ) ? $value : '';
				unset( $submission_data[ $key ] ); // Remove it from the data.
			}
		}
		return $submission_data;
	}

	/**
	 * Handle registration data from form submission
	 *
	 * @param array<string> $submission_data The form data.
	 *
	 * @since 1.8.0
	 * @return array<string,mixed>
	 */
	public function handle_registration_data( $submission_data ) {
		// Check if the form data contains 'srfm_registrations' and is not empty.
		if ( ! empty( $submission_data['srfm_registrations'] ) ) {
			// Assign the registrations to the class property for further processing.
			$this->registrations = $submission_data['srfm_registrations'];
			// Remove the registration data from the form data to avoid redundancy.
			unset( $submission_data['srfm_registrations'] );
		}
		return $submission_data;
	}

	/**
	 * Add registration data to submission for third-party integrations
	 *
	 * @param array<string,mixed> $modified_data The prepared submission data.
	 *
	 * @since 1.8.0
	 * @return array<string,mixed>
	 */
	public function add_registration_data_to_submission( $modified_data ) {
		// If the registration is not empty, add it to the submission data.
		// We are providing this for third-party integrations.
		// The registration will be structured as field 1, field 2, and so on.
		if ( ! empty( $this->registrations ) ) {
			// Registration will be JSON stringified, so decode it.
			$registration = json_decode( wp_unslash( $this->registrations ), true );
			if ( ! empty( $registration ) && is_array( $registration ) ) {
				$modified_data = array_merge( $modified_data, $registration );
			}
		}
		return $modified_data;
	}

	/**
	 * Process user registration from form submission
	 *
	 * @param array $form_data The form data.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function process_user_registration( $form_data ) {
		$form_id         = isset( $form_data['form_id'] ) ? Helper::get_integer_value( $form_data['form_id'] ) : 0;
		$submission_data = isset( $form_data['data'] ) && is_array( $form_data['data'] ) ? $form_data['data'] : [];

		// Check if registration blocks are present in the form.
		$has_registration_blocks = false;
		if ( isset( $submission_data['srfm_registrations'] ) ) {
			$registration_blocks     = json_decode( $submission_data['srfm_registrations'], true );
			$has_registration_blocks = is_array( $registration_blocks ) && ! empty( $registration_blocks );
		}

		// Only process if registration blocks are present or if form has registration settings.
		$user_registration_settings = $this->get_user_registration_settings( $form_id );

		if ( empty( $user_registration_settings ) && ! $has_registration_blocks ) {
			return; // No user registration settings found and no registration blocks present.
		}

		// If no form-level settings but registration blocks are present, skip processing.
		if ( empty( $user_registration_settings ) ) {
			return;
		}

		// Process each active user registration configuration.
		foreach ( $user_registration_settings as $registration ) {
			if ( ! is_array( $registration ) || empty( $registration['status'] ) ) {
				continue; // Skip inactive configurations.
			}

			// Parse form field values from the registration configuration.
			$username = isset( $registration['username'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['username'] ), $submission_data ) : '';
			$email    = isset( $registration['email'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['email'] ), $submission_data ) : '';

			// Validate required fields and throw errors if validation fails.
			if ( empty( $username ) ) {
				$this->throw_error( __( 'Username is required.', 'sureforms-pro' ) );
			}

			if ( empty( $email ) || ! is_email( $email ) ) {
				$this->throw_error( __( 'Valid email is required.', 'sureforms-pro' ) );
			}

			// Check if we're creating a new user.
			if ( isset( $registration['registrationType'] ) && 'create' === $registration['registrationType'] ) {
				// Check if user already exists.
				if ( username_exists( $username ) ) {
					$this->throw_error( __( 'Username already exists.', 'sureforms-pro' ) );
				}

				if ( email_exists( $email ) ) {
					$this->throw_error( __( 'Email address already exists.', 'sureforms-pro' ) );
				}

				// Create user immediately as activation is not involved.
				$result = $this->register_user( $registration, $submission_data );

				if ( is_wp_error( $result ) ) {
					// Convert WP_Error to a form submission error.
					$this->throw_error( $result->get_error_message() );
				}

				// Check if auto login is enabled.
				$auto_login = isset( $registration['autoLogin'] ) && ! empty( $registration['autoLogin'] );

				// Auto login the user.
				if ( $auto_login && is_int( $result ) ) {
					$this->auto_login_user( $result );
				}

				// Add the user ID to the form data for use in email templates.
				if ( is_int( $result ) ) {
					$this->parse_user_id_smart_tag( Helper::get_string_value( $result ) );
				}
			} else {
				// Update existing user.
				$existing_user = get_user_by( 'email', $email );
				$result        = null;

				if ( ! $existing_user && ! empty( $username ) ) {
					// If no user found by email, try to find by username.
					$existing_user = get_user_by( 'login', $username );
				}

				if ( ! $existing_user ) {
					$this->throw_error( __( 'User not found for update.', 'sureforms-pro' ) );
				}

				// Update the existing user.
				if ( $existing_user instanceof \WP_User ) {
					$result = $this->update_user( $registration, $submission_data, $existing_user->ID );
				}

				if ( is_wp_error( $result ) ) {
					// Convert WP_Error to a form submission error.
					$this->throw_error( $result->get_error_message() );
				}

				// Add the user ID to the form data for use in email templates.
				if ( is_int( $result ) ) {
					$this->parse_user_id_smart_tag( Helper::get_string_value( $result ) );
				}
			}

			// Process only the first active configuration.
			break;
		}
	}

	/**
	 * Register a new user
	 *
	 * @param array<string,mixed> $registration The registration configuration.
	 * @param array<string,mixed> $form_data The form data.
	 *
	 * @since 1.8.0
	 * @return int|WP_Error User ID on success, WP_Error on failure.
	 */
	private function register_user( $registration, $form_data ) {
		// Parse user data from form fields.
		$firstname = isset( $registration['firstname'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['firstname'] ), $form_data ) : '';
		$lastname  = isset( $registration['lastname'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['lastname'] ), $form_data ) : '';
		$username  = isset( $registration['username'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['username'] ), $form_data ) : '';
		$email     = isset( $registration['email'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['email'] ), $form_data ) : '';
		$role      = isset( $registration['userRole'] ) ? Helper::get_string_value( $registration['userRole'] ) : 'subscriber';

		// Create the user.
		$user_data = [
			'user_login' => $username,
			'user_email' => $email,
			'user_pass'  => ! empty( self::$password ) ? self::$password : wp_generate_password(),
			'first_name' => $firstname,
			'last_name'  => $lastname,
			'role'       => $role,
		];

		$user_id = wp_insert_user( $user_data );

		if ( is_wp_error( $user_id ) ) {
			return $user_id;
		}

		// Add custom user meta.
		if ( isset( $registration['customUserMeta'] ) && is_array( $registration['customUserMeta'] ) ) {
			foreach ( $registration['customUserMeta'] as $meta ) {
				if ( is_array( $meta ) && isset( $meta['key'] ) && isset( $meta['value'] ) ) {
					$meta_key   = Helper::get_string_value( $meta['key'] );
					$meta_value = $this->parse_field_value( Helper::get_string_value( $meta['value'] ), $form_data );
					update_user_meta( $user_id, $meta_key, $meta_value );
				}
			}
		}

		// Send notification email.
		if ( isset( $registration['sendUserEmail'] ) && ! empty( $registration['sendUserEmail'] ) ) {
			$this->send_welcome_email( $user_id );
		}

		return $user_id;
	}

	/**
	 * Update an existing user
	 *
	 * @param array<string,mixed> $registration The registration configuration.
	 * @param array<string,mixed> $form_data The form data.
	 * @param int                 $user_id The user ID to update.
	 *
	 * @since 1.8.0
	 * @return int|WP_Error User ID on success, WP_Error on failure.
	 */
	private function update_user( $registration, $form_data, $user_id ) {
		// Parse user data from form fields.
		$firstname = isset( $registration['firstname'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['firstname'] ), $form_data ) : '';
		$lastname  = isset( $registration['lastname'] ) ? $this->parse_field_value( Helper::get_string_value( $registration['lastname'] ), $form_data ) : '';
		$role      = isset( $registration['userRole'] ) ? Helper::get_string_value( $registration['userRole'] ) : '';

		// Update the user.
		$user_data = [
			'ID'         => $user_id,
			'first_name' => $firstname,
			'last_name'  => $lastname,
		];

		// Only update role if specified.
		if ( ! empty( $role ) ) {
			$user_data['role'] = $role;
		}

		// Update password if provided.
		if ( ! empty( self::$password ) ) {
			$user_data['user_pass'] = self::$password;
		}

		$result = wp_update_user( $user_data );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		// Add custom user meta.
		if ( isset( $registration['customUserMeta'] ) && is_array( $registration['customUserMeta'] ) ) {
			foreach ( $registration['customUserMeta'] as $meta ) {
				if ( is_array( $meta ) && isset( $meta['key'] ) && isset( $meta['value'] ) ) {
					$meta_key   = Helper::get_string_value( $meta['key'] );
					$meta_value = $this->parse_field_value( Helper::get_string_value( $meta['value'] ), $form_data );
					update_user_meta( $user_id, $meta_key, $meta_value );
				}
			}
		}

		return $user_id;
	}

	/**
	 * Send welcome email to the newly registered user
	 *
	 * @param int $user_id The user ID.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	private function send_welcome_email( $user_id ) {
		$user = get_userdata( $user_id );

		if ( ! $user ) {
			return;
		}

		$subject = sprintf(
			/* translators: %s: site name */
			__( 'Welcome to %s', 'sureforms-pro' ),
			get_bloginfo( 'name' )
		);

		$message = sprintf(
			/* translators: %1$s: user's name, %2$s: site name, %3$s: login URL */
			__( "Hello %1\$s,\n\nThank you for registering on %2\$s. You can now log in using your username and password.\n\nLogin: %3\$s", 'sureforms-pro' ),
			$user->display_name,
			get_bloginfo( 'name' ),
			wp_login_url()
		);

		// Allow filtering of email content.
		$subject = apply_filters( 'srfm_user_welcome_email_subject', $subject, $user );
		$message = apply_filters( 'srfm_user_welcome_email_message', $message, $user );

		wp_mail( $user->user_email, $subject, $message );
	}

	/**
	 * Throw an error that will stop form submission.
	 *
	 * @param string $message The error message.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	private function throw_error( $message ) {
		wp_send_json_error(
			[
				'message'  => $message,
				'position' => 'header',
			]
		);
	}

	/**
	 * Get user registration settings for a form.
	 *
	 * @param int $form_id The form ID.
	 *
	 * @since 1.8.0
	 * @return array<int,array<string,mixed>> The user registration settings.
	 */
	private function get_user_registration_settings( $form_id ) {
		$settings = get_post_meta( $form_id, '_srfm_user_registration_settings', true );

		if ( empty( $settings ) || ! is_string( $settings ) ) {
			return [];
		}

		$decoded_settings = json_decode( $settings, true );

		if ( ! is_array( $decoded_settings ) ) {
			return [];
		}

		return $decoded_settings;
	}

	/**
	 * Parse a field value from the form data
	 *
	 * @param string              $field_key The field key or static value.
	 * @param array<string,mixed> $form_data The form data.
	 *
	 * @since 1.8.0
	 * @return string The parsed field value.
	 */
	private function parse_field_value( $field_key, $form_data ) {
		// If the field key doesn't use the {form:field-name} format, return as is.
		if ( ! preg_match( '/\{form:(.*?)\}/', $field_key, $matches ) ) {
			return $field_key;
		}

		$form_field = $matches[1];

		// Look for the field in form data.
		if ( isset( $form_data[ $form_field ] ) ) {
			$value = $form_data[ $form_field ];
			return is_string( $value ) ? $value : '';
		}

		return '';
	}

	/**
	 * Auto login a user after registration
	 *
	 * @param int $user_id The user ID.
	 *
	 * @since 1.8.0
	 * @return bool True on success, false on failure.
	 */
	private function auto_login_user( $user_id ) {
		if ( is_user_logged_in() ) {
			return false;
		}

		$user = get_user_by( 'id', $user_id );

		if ( ! $user instanceof \WP_User ) {
			return false;
		}

		// Defer login until shutdown to avoid nonce context mismatch.
		add_action(
			'shutdown',
			static function () use ( $user_id ) {
				// Set the current user.
				wp_set_current_user( $user_id );
				// Set the auth cookie for the user.
				wp_set_auth_cookie( $user_id );
			},
			0
		);

		return true;
	}

	/**
	 * Parse the user ID smart tag for use in email templates
	 *
	 * @param string $user_id The user ID to parse.
	 * @since 1.8.0
	 * @return void
	 */
	private function parse_user_id_smart_tag( $user_id ) {
		if ( empty( $user_id ) ) {
			return;
		}
		// Add a filter to parse the {user_id} smart tag.
		add_filter(
			'srfm_parse_smart_tags',
			static function( $parsed, $context ) use ( $user_id ) {
				if ( '{user_id}' === $context['tag'] ) {
					return $user_id;
				}
				return $parsed;
			},
			10,
			2
		);
	}
}
