<?php
/**
 * WS Form Integration
 *
 * @link https://personalizewp.com/
 * @since 3.2.0 Merged with PWP
 * @since 2.2.0
 *
 * @package PersonalizeWP
 * @subpackage PersonalizeWP/Integrations
 */

namespace PersonalizeWP\Integrations\WSForm;

use WS_Form_Action;
use WS_Form_Common;
use PersonalizeWP\Model\Contact;
use PersonalizeWP\Model\Activity;

/**
 * PersonalizeWP - WSForm class
 */
class WSForm extends WS_Form_Action {

	// phpcs:disable Squiz.Commenting.VariableComment.Missing, Squiz.Commenting.VariableComment.WrongStyle
	public $id           = 'personalizewp';
	public $pro_required = true;
	public $label;
	public $label_action;
	public $events;
	public $multiple   = true;
	public $priority   = 150;
	public $can_repost = true;
	public $form_add   = false;

	// Licensing
	private $licensing;

	// Config
	public $list_id = false;
	public $field_mapping;
	public $custom_mapping;
	private $contact_fields = [];
	private $meta_fields    = [];
	// phpcs:enable

	/**
	 * Constructor
	 */
	public function __construct() {

		// Events
		$this->events = array( 'submit' );

		add_action( 'init', array( $this, 'init' ), 20 );

		// Register config filters
		add_filter( 'wsf_config_meta_keys', array( $this, 'config_meta_keys' ), 10, 2 );

		// Add UID hidden field to any WS Form
		add_filter( 'wsf_submit_hidden_fields', array( $this, 'submit_hidden_fields' ), 10, 2 );

		// Register action
		parent::register( $this );
	}

	/**
	 * Init
	 *
	 * @since 2.9.0
	 *
	 * @return void
	 */
	public function init() {

		// Set label
		$this->label = __( 'PersonalizeWP', 'personalizewp' );

		// Set label for actions pull down
		$this->label_action = __( 'Record in PersonalizeWP', 'personalizewp' );
	}

	/**
	 * Process a form submission to PWP endpoint
	 *
	 * @param WS_Form_Form $form Form Object
	 * @param Object       $submit Submit object
	 * @param array        $config Form config to use
	 *
	 * @return bool
	 */
	public function post( $form, &$submit, $config ) {

		// Load configuration
		self::load_config( $config );

		// Process the raw Tracked User details first.
		$pwp_user_json = WS_Form_Common::get_query_var_nonce( 'wsf_pwp_tracked_user' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: already checked during WS Form processing
		$pwp_user_obj  = ! empty( $pwp_user_json ) ? json_decode( $pwp_user_json, true ) : null;
		if ( empty( $pwp_user_obj ) ) {
			return true;
		}
		// Get the UID, if set.
		$uid = ! empty( $pwp_user_obj['id'] ) ? sanitize_text_field( $pwp_user_obj['id'] ) : '';

		// If no UID present, nothing to track
		if ( empty( $uid ) ) {
			return true;
		}

		// Stage 1: Process field mapping first

		// Field mapping
		foreach ( $this->field_mapping as $field_map ) {

			// Get field ID
			$field_id = $field_map['ws_form_field'];
			if ( empty( $field_id ) ) {
				continue;
			}

			// Get submit value
			$submit_value = parent::get_submit_value( $submit, WS_FORM_FIELD_PREFIX . $field_id, false );
			if ( false === $submit_value ) {
				continue;
			}

			// Get API field
			$api_field = $field_map[ 'action_' . $this->id . '_list_fields' ];
			if ( empty( $api_field ) ) {
				continue;
			}

			// PersonalizeWP cannot accept arrays
			if ( is_array( $submit_value ) ) {
				$submit_value = implode( ',', $submit_value );
			}

			// Process
			self::process_user_data( $api_field, $submit_value );
		}

		// Custom mapping
		foreach ( $this->custom_mapping as $custom_map ) {

			// Get value
			$custom_value = $custom_map[ 'action_' . $this->id . '_custom_mapping_value' ];
			$custom_value = WS_Form_Common::parse_variables_process( $custom_value, $form, $submit, 'text/plain' );

			// Get attribute
			$api_field = $custom_map[ 'action_' . $this->id . '_list_fields' ];
			if ( '' === $api_field ) {
				continue;
			}

			// PersonalizeWP cannot accept arrays
			if ( is_array( $custom_value ) ) {
				$custom_value = implode( ',', $custom_value );
			}

			// Process
			self::process_user_data( $api_field, $custom_value );
		}

		// Create the Contact if we have data (i.e. is known) or if we're tracking anon users.
		$anon_users  = personalizewp()->should_anonymise_profiles();
		$track_anon  = personalizewp()->should_track_anonymous_users();
		$create_user = ! empty( $this->contact_fields ) || $track_anon;

		// Get the Contact record, creating it if we have user data to set, or we are tracking anon users.
		$contact = Contact::findByUid( $uid, $create_user );

		// Either we have a previously Anon user, or should have a User that will made known.
		if ( empty( $contact ) ) {
			return true;
		}

		if ( ! empty( $this->contact_fields ) ) {
			// Update contact attributes, making the User "known", unless specifically keeping users anon.
			if ( ! $anon_users ) {
				$contact->update( $this->contact_fields ); // Data already sanitised
			} else {
				// In which case manually mark the user as 'known', even if we don't know who specifically they are.
				$contact->update( array( 'is_known' => true ) );
			}
		}

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

		// Store extra meta for easier rule validation.
		$contact->update_metadata( '_has-completed-form', true );

		// Complete the rest of the mapped custom fields.
		foreach ( $this->meta_fields as $meta_key => $meta_value ) {
			// Update contact meta from field value.
			$contact->update_metadata( $meta_key, $meta_value ); // Already pre-sanitised during
		}

		// Read WordPress user
		$current_user = wp_get_current_user();

		// WP User ID
		if ( ! empty( $current_user->ID ) && ( 0 < $current_user->ID ) ) {
			$contact->update_metadata( 'wp_user_id', absint( $current_user->ID ) );
		}

		// WP User Role
		if ( ! empty( $current_user->roles ) && is_array( $current_user->roles ) ) {
			$contact->update_metadata( 'wp_user_role', $current_user->roles );
		}

		// Stage 2: Now track the activity entry itself.

		// Get URL from referer
		$url = sanitize_url( WS_Form_Common::get_http_env_raw( array( 'HTTP_REFERER' ) ) );

		// Track a form entry, needs no mapping.
		$result = Activity::record(
			$contact->uid,
			array(
				'activity_type' => 'form',
				'url'           => $url,
				'object_id'     => $form->id,
				'object_type'   => 'wsform', // Matching the unchanging plugin slug
			)
		);

		// Success
		self::success( __( 'Successfully recorded in PersonalizeWP', 'personalizewp' ) );

		return true;
	}

	/**
	 * Process Visitor Profile data
	 *
	 * @param string $api_field  Field key to update
	 * @param string $meta_value Field value to set
	 *
	 * @return void
	 */
	public function process_user_data( $api_field, $field_value ) {

		switch ( $api_field ) {
			case 'first_name':
			case 'last_name':
			case 'email':
				$this->contact_fields[ $api_field ] = sanitize_text_field( $field_value );

				break;

			case 'marketing_consent':
			case 'privacy_policy':
				$this->meta_fields[ $api_field ] = ! empty( $field_value );
				break;

			default:
				$this->meta_fields[ $api_field ] = sanitize_text_field( $field_value );
		}
	}

	/**
	 * Get the PersonalizeWP field mappings
	 */
	public function get_list_fields() {

		$fields = array(
			array(
				'value' => 'first_name',
				'text'  => __( 'First Name', 'personalizewp' ),
			),
			array(
				'value' => 'last_name',
				'text'  => __( 'Last Name', 'personalizewp' ),
			),
			array(
				'value' => 'email',
				'text'  => __( 'Email Address', 'personalizewp' ),
			),
			array(
				'value' => 'marketing_consent',
				'text'  => __( 'Marketing Consent', 'personalizewp' ),
			),
			array(
				'value' => 'privacy_policy',
				'text'  => __( 'Privacy Policy', 'personalizewp' ),
			),
		);

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

		// Sort fields
		usort(
			$fields,
			function ( $field_1, $field_2 ) {
				return $field_1['text'] <=> $field_2['text'];
			}
		);

		return $fields;
	}

	/**
	 * Get action settings
	 *
	 * @return array
	 */
	public function get_action_settings() {

		$settings = array(
			'meta_keys' => array(
				'action_' . $this->id . '_field_mapping',
				'action_' . $this->id . '_custom_mapping',
			),
		);

		// Wrap settings so they will work with sidebar_html function in admin.js
		$settings = parent::get_settings_wrapper( $settings );

		// Add labels
		$settings->label        = $this->label;
		$settings->label_action = $this->label_action;

		// Add multiple
		$settings->multiple = $this->multiple;

		// Add events
		$settings->events = $this->events;

		// Add can_repost
		$settings->can_repost = $this->can_repost;

		// Apply filter
		$settings = apply_filters( 'wsf_action_' . $this->id . '_settings', $settings );

		return $settings;
	}

	/**
	 * Meta keys for this action
	 *
	 * @param array $meta_keys Meta Keys for action
	 * @param int   $form_id Form ID
	 * @return array
	 */
	public function config_meta_keys( $meta_keys = array(), $form_id = 0 ) {

		// Build config_meta_keys
		$config_meta_keys = array(

			// Field mapping
			'action_' . $this->id . '_field_mapping'  => array(
				'label'            => __( 'Field Mapping', 'personalizewp' ),
				'type'             => 'repeater',
				'help'             => __( 'Map WS Form fields to PersonalizeWP fields.', 'personalizewp' ),
				'meta_keys'        => array(
					'ws_form_field',
					'action_' . $this->id . '_list_fields',
				),
				'meta_keys_unique' => array(
					'action_' . $this->id . '_list_fields',
				),
			),

			// List fields
			'action_' . $this->id . '_list_fields'    => array(
				'label'   => __( 'PersonalizeWP', 'personalizewp' ),
				'type'    => 'select',
				'options' => self::get_list_fields(),
			),

			// Custom field mapping
			'action_' . $this->id . '_custom_mapping' => array(
				'label'            => __( 'Custom Mapping', 'personalizewp' ),
				'type'             => 'repeater',
				'help'             => __( 'Map custom values to PersonalizeWP fields.', 'personalizewp' ),
				'meta_keys'        => array(
					'action_' . $this->id . '_custom_mapping_value',
					'action_' . $this->id . '_list_fields',
				),
				'meta_keys_unique' => array(
					'action_' . $this->id . '_list_fields',
				),
			),

			// Custom field mapping - Value
			'action_' . $this->id . '_custom_mapping_value' => array(
				'label' => __( 'Value', 'personalizewp' ),
				'type'  => 'text',
			),
		);

		// Merge
		$meta_keys = array_merge( $meta_keys, $config_meta_keys );

		return $meta_keys;
	}

	/**
	 * Add hidden field for uid to all forms
	 *
	 * @param array        $submit_hidden_fields Hidden fields
	 * @param WS_Form_Form $form Form Object
	 *
	 * @return array
	 */
	public function submit_hidden_fields( $submit_hidden_fields, $form ) {

		$submit_hidden_fields[] = array(
			'name'  => 'wsf_pwp_tracked_user',
			'value' => 'pwp_tracked_user',
			'type'  => 'local_storage',
		);

		return $submit_hidden_fields;
	}

	/**
	 * Load config for this action
	 *
	 * @param array $config Form config to use
	 *
	 * @return void
	 */
	public function load_config( $config = array() ) {

		// Field mapping
		$this->field_mapping = parent::get_config( $config, 'action_' . $this->id . '_field_mapping', array() );
		if ( ! is_array( $this->field_mapping ) ) {
			$this->field_mapping = array();
		}

		// Custom mapping
		$this->custom_mapping = parent::get_config( $config, 'action_' . $this->id . '_custom_mapping', array() );
		if ( ! is_array( $this->custom_mapping ) ) {
			$this->custom_mapping = array();
		}
	}
}
