<?php
/**
 * Gravity Forms Integration
 *
 * @link https://personalizewp.com/
 * @since 3.2.0 Merged with PWP
 * @since 1.2.0
 *
 * @package PersonalizeWP
 * @subpackage PersonalizeWP/Integrations
 */

namespace PersonalizeWP\Integrations\GravityForms;

use GF_Fields;
use GFAddOn;
use PersonalizeWP\Model\Contact;
use PersonalizeWP\Model\Activity;

/**
 * PersonalizeWP - GravityForms class
 */
class GravityForms extends GFAddOn {

	// phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore, Generic.Commenting.DocComment.MissingShort
	/**
	 * @var string Version number of the Add-On
	 */
	protected $_version = '0.7.4';

	/**
	 * The minimum Gravity Forms version required for the add-on to load.
	 *
	 * @var string Gravity Forms minimum version requirement
	 */
	protected $_min_gravityforms_version = '1.9';

	/**
	 * This instance
	 *
	 * @var GravityForms
	 */
	private static $_instance = null;

	/**
	 * Track adding of footer JS
	 *
	 * @var bool
	 */
	protected $script_added = false;
	// phpcs:enable

	/**
	 * Return this. Required by Gravity Forms.
	 *
	 * @since 1.2.0
	 *
	 * @return GravityForms
	 */
	public static function get_instance() {
		if ( null === self::$_instance ) {

			self::$_instance = new self();
		}

		return self::$_instance;
	}

	/**
	 * Setup the GF Addon and all associated variables
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function init() {
		// Ensure proper internationalisation of titles.
		$this->_title       = __( 'PersonalizeWP - Gravity Forms Add-On', 'personalizewp' );
		$this->_short_title = __( 'PersonalizeWP', 'personalizewp' );
		$this->_slug        = 'pwp-pro-gravityforms';
		$this->_path        = 'personalizewp-pro/personalizewp-pro.php';
		$this->_full_path   = __FILE__;

		parent::init();

		// Add a hidden input to all GF forms
		add_filter( 'gform_pre_render', array( $this, 'gform_add_hidden_pwp_field' ), 10, 6 );
		// Process all GF forms to track
		add_filter( 'gform_entry_post_save', array( $this, 'process_submission' ), 10, 2 );
	}

	/**
	 * Add PersonalizeWP fields to the GF form
	 *
	 * @since 1.2.0
	 *
	 * @param array $form Form settings
	 */
	public function form_settings_fields( $form ) {
		$settings = array(
			array(
				'title'  => esc_html__( 'PersonalizeWP Visitor Profile Field Mappings', 'personalizewp' ),
				'fields' => array(
					array(
						'name'      => 'pwp_mappings',
						'type'      => 'field_map',
						'label'     => esc_html__( 'Map Form fields to Visitor Profile fields', 'personalizewp' ),
						'field_map' => $this->get_pwp_fields(),
						'tooltip'   => '<h6>' . esc_html__( 'Map Fields', 'personalizewp' ) . '</h6>' . esc_html__( 'Select which Gravity Form fields pair with their respective PersonalizeWP Visitor Profile fields.', 'personalizewp' ),
					),
				),
			),
		);
		if ( personalizewp()->should_anonymise_profiles() ) {
			$settings[0]['description'] = esc_html__( 'You have checked the setting to anonymise profile data. This means that any data mapped from a form to PersonalizeWP will not be displayed. The action of completing a form will be tracked, but the profile will remain anonymous.', 'personalizewp' );
		}

		return $settings;
	}

	/**
	 * Determines what needs to be processed for the provided entry.
	 *
	 * @since 1.2.0
	 *
	 * @param array $entry The Entry Object currently being processed.
	 * @param array $form  The Form Object currently being processed.
	 *
	 * @return array The Entry Object.
	 */
	public function process_submission( $entry, $form ) {

		// Get the uid, if set.
		$uid = ! empty( $_POST['input_9983'] ) ? sanitize_text_field( rgpost( 'input_9983', true ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: already checked during GravityForms processing

		if ( empty( $uid ) || 'pwp_hidden_value' === $uid ) {
			// Nothing to track
			return $entry;
		}

		$user_data = [];
		// All custom fields are put into plugin named area using slug.
		// Only update the Contact if fields have been mapped.
		// Focus first on fields that mark the user as 'known'.
		if ( ! empty( $form[ $this->_slug ] ) ) {
			$base_fields = array(
				'pwp_first_name'    => '',
				'pwp_last_name'     => '',
				'pwp_email_address' => '',
			);

			foreach ( $base_fields as $k => $v ) {
				$full_field = "pwp_mappings_{$k}";
				if ( isset( $form[ $this->_slug ][ $full_field ] ) ) {
					$field_map = $form[ $this->_slug ][ $full_field ];
					// Update field value
					$base_fields[ $k ] = sanitize_text_field( $entry[ $field_map ] );
				}
			}

			// set contact attributes
			$user_data = array(
				'first_name' => $base_fields['pwp_first_name'],
				'last_name'  => $base_fields['pwp_last_name'],
				'email'      => $base_fields['pwp_email_address'],
			);
		}
		// Store extra meta for easier rule validation.
		$user_data['meta_input'] = array(
			'_has-completed-form' => true,
		);

		// Get the Contact record, creating it if we have user data to set, or we are tracking anon users.
		$contact = Contact::find_by_uid( $uid, $user_data );
		if ( ! $contact ) {
			return $entry;
		}

		$track_anon = personalizewp()->should_track_anonymous_users();
		// Only continue if we are tracking anon users or the user is known.
		if ( ! $track_anon && ! $contact->is_known ) {
			return $entry;
		}

		// Complete the rest of the mapped custom fields.
		if ( ! empty( $form[ $this->_slug ] ) ) {
			// Fixed meta fields.
			$meta_fields = array(
				'marketing_consent',
				'privacy_policy',
				'wp_user_id',
				'wp_user_role',
			);
			// Add all possible extended fields.
			$meta_fields = array_merge( $meta_fields, array_keys( $contact->extended_fields ) );
			// Map the meta fields to the GF fields, populating with submitted data.
			foreach ( $meta_fields as $meta_key ) {
				// Follow GF naming convention with fields.
				$full_field = "pwp_mappings_pwp_{$meta_key}";
				if ( ! empty( $form[ $this->_slug ][ $full_field ] ) ) {
					$mapped_field = $form[ $this->_slug ][ $full_field ];
					// Update contact meta from field value.
					// This will store an empty value if the field is mapped, but no data was submitted.
					$contact->update_metadata( $meta_key, sanitize_text_field( $entry[ $mapped_field ] ) );
				}
			}
		}

		// Make URL relative if possible.
		$url = str_replace( home_url(), '', $entry['source_url'] );
		// Track a form entry, needs no mapping.
		$result = Activity::record(
			$contact->uid,
			array(
				'activity_type' => 'form',
				'url'           => $url,
				'object_id'     => $form['id'],
				'object_type'   => 'gravityforms', // Matching the unchanging plugin slug
			)
		);

		// Irregardless of result, return the entry for GF.
		return $entry;
	}

	/**
	 * Get the PersonalizeWP field mappings
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private function get_pwp_fields() {
		$fields = array(
			array(
				'name'          => 'pwp_first_name',
				'label'         => esc_html__( 'First Name', 'personalizewp' ),
				'required'      => false,
				'field_type'    => array( 'name', 'text', 'hidden' ),
				'default_value' => $this->get_first_field_by_type( 'name', 3 ),
			),
			array(
				'name'          => 'pwp_last_name',
				'label'         => esc_html__( 'Last Name', 'personalizewp' ),
				'required'      => false,
				'field_type'    => array( 'name', 'text', 'hidden' ),
				'default_value' => $this->get_first_field_by_type( 'name', 6 ),
			),
			array(
				'name'          => 'pwp_email_address',
				'label'         => esc_html__( 'Email Address', 'personalizewp' ),
				'required'      => false,
				'field_type'    => array( 'email', 'text', 'hidden' ),
				'default_value' => $this->get_first_field_by_type( 'email' ),
			),

			array(
				'name'          => 'pwp_marketing_consent',
				'label'         => esc_html__( 'Marketing Consent', 'personalizewp' ),
				'default_value' => '',
			),
			array(
				'name'          => 'pwp_privacy_policy',
				'label'         => esc_html__( 'Privacy Policy', 'personalizewp' ),
				'default_value' => '',
			),
			array(
				'name'          => 'pwp_wp_user_id',
				'label'         => esc_html__( 'WordPress User ID', 'personalizewp' ),
				'default_value' => '',
			),
			array(
				'name'          => 'pwp_wp_user_role',
				'label'         => esc_html__( 'WordPress User Role', 'personalizewp' ),
				'default_value' => '',
			),
		);

		// Use empty contact to get extended fields list.
		$contact = new Contact( [] );
		foreach ( $contact->extended_fields as $name => $label ) {
			$fields[] = array(
				'name'          => "pwp_{$name}",
				'label'         => esc_html( $label ),
				'default_value' => '',
			);
		}

		return $fields;
	}

	/**
	 * Add the automatic hidden field to populate with the JS visitor ID
	 *
	 * @param array $form Gravity Form Object
	 *
	 * @since 1.2.0
	 *
	 * @return array $form Gravity Form Object
	 */
	public function gform_add_hidden_pwp_field( $form ) {

		if ( ! $this->script_added ) {
			$this->script_added = true;
			// Add JS to populate the field with the local storage ID
			add_action( 'wp_footer', array( $this, 'action_wp_footer' ) );
		}

		$props = array(
			'id'           => 9983, // Needs to be high to not conflict. Alt: GFFormsModel::get_next_field_id?
			'label'        => 'PWP ID',
			'inputName'    => 'pwp_id',
			'type'         => 'hidden',
			'defaultValue' => 'pwp_hidden_value',
		);
		$field = GF_Fields::create( $props );
		array_push( $form['fields'], $field );

		return $form;
	}

	/**
	 * Output inline JS that populates our GF hidden input field with the Visitor ID, and triggers our tracking that maybe the user will now be known.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function action_wp_footer() {
		?>
<script>
(function(){
const trackPWPGFUser=()=>{
	let pUsr=localStorage.getItem('pwp_tracked_user')||'';
	if(!pUsr){return;}
	pUsr=JSON.parse(pUsr);
	const gF=document.querySelectorAll('input[value="pwp_hidden_value"]');
	if(gF && 0<gF.length){ gF.forEach(function(f){f.value=pUsr.id}); }

	const maybeKnown = window?.PersonalizeWP?.tracking?.maybeUserKnown || null;
	if(typeof maybeKnown === "function"){maybeKnown();}
	if(jQuery){
		// Have to use jQuery, because the event doesn't "appear" within normal browser events
		jQuery(document).on('gform_confirmation_loaded',()=>{
			if(typeof maybeKnown === "function"){ maybeKnown(); }
		});
	}
	if(window?.gform?.utils?.addFilter){
		window.gform.utils.addFilter('gform/ajax/post_ajax_submission',(data)=>{
			if(typeof maybeKnown === "function"){ maybeKnown(); }
			return data;
		});
	}
}
document.addEventListener('DOMContentLoaded',trackPWPGFUser);
document.addEventListener('PWP:userCreation',trackPWPGFUser);
})();
</script>
		<?php
	}
}
