<?php

/**
 * BWFCRM_Actions_Handler
 */
#[AllowDynamicProperties]
class BWFCRM_Link_Trigger_Handler {
	/**
	 * Class instance
	 */
	private static $ins = null;

	protected $contact_obj;
	protected $link_id = 0;

	/**
	 * Class constructor
	 */
	public function __construct() {
		$this->contact_obj = new stdClass();

		/** Action to handle link trigger click */
		add_action( 'wp_loaded', array( $this, 'handle_link_trigger_click' ), 101 );

		/** Check for link trigger URL */
		add_filter( 'bwfan_is_link_trigger_url', array( $this, 'is_link_trigger_url' ), 10, 2 );

		/** Handle schedule action  */
		add_action( 'bwfcrm_run_link_trigger_async', array( $this, 'link_trigger_async_cb' ), 10, 2 );

		/** Returns contacts link trigger data */
		add_action( 'bwfan_get_contact_field_by_slug', array( $this, 'link_trigger_contact_data' ), 10, 3 );
	}

	/**
	 * Returns class instance
	 *
	 * @return BWFCRM_Link_Trigger_Handler|null
	 */
	public static function get_instance() {
		if ( null === self::$ins ) {
			self::$ins = new self();
		}

		return self::$ins;
	}

	/**
	 * Handling link trigger click
	 */
	public function handle_link_trigger_click() {
		/** If link trigger is not set, return */
		if ( ! isset( $_GET['bwfan-link-trigger'] ) || empty( $_GET['bwfan-link-trigger'] ) ) {
			return;
		}

		/** Link data */
		$link_hash  = filter_input( INPUT_GET, 'bwfan-link-trigger' );
		$link_data  = BWFAN_Model_Link_Triggers::bwfan_get_link_trigger( 0, $link_hash );
		$target_url = isset( $link_data['data']['redirect_url'] ) ? $link_data['data']['redirect_url'] : '';

		/** Record engagement tracking */
		$this->trigger_engagement_click( $target_url );

		/** Link Trigger is inactive or no target URL defined */
		if ( ! isset( $link_data['status'] ) || $link_data['status'] != 2 || empty( $target_url ) ) {
			return;
		}

		/** @var WooFunnels_Contact $db_contact */
		$db_contact = $this->get_contact_obj();

		/** Set object cache */
		if ( $db_contact instanceof WooFunnels_Contact ) {
			$this->contact_obj = $db_contact;
		}

		/** Primary key */
		$link_id = isset( $link_data['ID'] ) ? $link_data['ID'] : 0;

		$run_async = apply_filters( 'bwfan_link_trigger_async', true, $link_id, $link_data );

		/** Check for sync option */
		if ( true === $run_async ) {
			/** Schedule link trigger action */
			$this->schedule_link_trigger_async( $link_id );
		} else {
			/** Direct process link trigger */
			$this->link_trigger_processing( $link_id, $link_data, $db_contact );
		}

		/** Update click count */
		$this->update_link_clicks( $link_id, $link_data );

		/** If URL contains merge tag */
		if ( true === $this->url_contains_merge_tag( $target_url ) ) {

			/** Get the Contact object from object cache */
			$db_contact = $this->contact_obj;
			if ( ! $db_contact instanceof WooFunnels_Contact ) {
				$db_contact = $this->get_contact_obj();

				/** Set object cache */
				if ( $db_contact instanceof WooFunnels_Contact ) {
					$this->contact_obj = $db_contact;
				}
			}

			if ( $db_contact instanceof WooFunnels_Contact && $db_contact->get_id() > 0 ) {
				/** Set contact Id for merge tag */
				BWFAN_Merge_Tag_Loader::set_data( array(
					'contact_id'   => $db_contact->get_id(),
					'contact_mode' => 1
				) );

				/** Decode target URL merge tags */
				$target_url = BWFAN_Common::decode_merge_tags( $target_url );
			}
		}

		/** Link trigger might have auto login enabled */
		$this->maybe_login( $link_data );

		do_action( 'bwfan_before_redirect_link_trigger', $link_data );

		/** Redirect to target url */
		BWFAN_PRO_Common::wp_redirect( $this->may_append_query_arg( $target_url ) );
		exit;
	}

	/**
	 * Record engagement conversation click
	 *
	 * @param $target_url
	 */
	protected function trigger_engagement_click( $target_url ) {
		$track_id = filter_input( INPUT_GET, 'bwfan-track-id' );
		if ( empty( $track_id ) ) {
			return;
		}

		$engagement_ins = BWFAN_Email_Conversations::get_instance();
		$engagement_ins->track_click_skip( $target_url );
	}

	/**
	 * Schedule link trigger action
	 *
	 * @param $link_id
	 */
	protected function schedule_link_trigger_async( $link_id ) {
		$contact_data = $this->get_contact_id();
		if ( empty( $contact_data ) ) {
			return;
		}

		$args = array(
			'contact' => $contact_data,
			'link_id' => $link_id,
		);

		/** Schedule if not scheduled */
		if ( ! bwf_has_action_scheduled( 'bwfcrm_run_link_trigger_async', $args, 'autonami-link-trigger' ) ) {
			bwf_schedule_single_action( time(), 'bwfcrm_run_link_trigger_async', $args, 'autonami-link-trigger' );
		}
	}

	/**
	 * Callback of async event
	 */
	public function link_trigger_async_cb( $contact_data, $link_id ) {
		/** CRM contact object */
		$contact = $this->get_contact_obj( $contact_data );

		/** Get link data */
		$link_data = BWFAN_Model_Link_Triggers::bwfan_get_link_trigger( $link_id );

		/** Process link trigger */
		$this->link_trigger_processing( $link_id, $link_data, $contact );
	}

	/**
	 * Perform link trigger actions
	 *
	 * @param $link_id
	 * @param $link_data
	 * @param $db_contact
	 */
	protected function link_trigger_processing( $link_id, $link_data, $db_contact ) {
		/** Actions to run */
		$actions = ( isset( $link_data['data']['actions'] ) && count( $link_data['data']['actions'] ) > 0 ) ? $link_data['data']['actions'] : array();

		/** No contact found, just redirect. */
		if ( ! $db_contact instanceof WooFunnels_Contact || 0 === $db_contact->get_id() ) {
			return;
		}

		/** CRM contact object */
		$contact = new BWFCRM_Contact( $db_contact );

		/** Set cookie */
		$uid = $db_contact->get_uid();
		if ( ! empty( $uid ) ) {
			setcookie( '_fk_contact_uid', $uid, time() + 10 * 365 * 24 * 60 * 60, '/' );
		}

		/** Trigger hook - link trigger clicked */
		do_action( 'bwfan_link_triggered', $link_id, $contact );

		/** Get contact link triggered array */
		$contact_link_clicked = $contact->get_field_by_slug( 'link-trigger-click' );
		$contact_clicked_ids  = json_decode( $contact_link_clicked, true );
		$contact_clicked_ids  = ( ! empty( $contact_clicked_ids ) && is_array( $contact_clicked_ids ) ) ? $contact_clicked_ids : array();

		if ( empty( $actions ) ) {
			/** Update Contact link click */
			$this->update_contact_link_click( $contact, $link_id, $contact_clicked_ids );

			return;
		}

		/** Check Action runs times */
		$actions_run_time = isset( $link_data['data']['action_run'] ) ? $link_data['data']['action_run'] : 'once';

		/** Filter to run link trigger action multiple times ( by default false ) */
		if ( apply_filters( 'bwfan_link_trigger_multi_execution', false ) ) {
			$actions_run_time = 'multiple';
		}

		/** Clicked multiple times, just redirect */
		if ( $actions_run_time == 'once' && in_array( $link_id, $contact_clicked_ids ) ) {
			return;
		}
		$this->link_id = $link_id;

		/** Run actions */
		$logs = $this->process_actions( $actions, $contact );

		/** Maybe create logs contact note */
		$this->create_contact_logs_note( $logs, $contact, $link_data );

		/** Update Contact link click */
		$this->update_contact_link_click( $contact, $link_id, $contact_clicked_ids );
	}

	/**
	 * Updated link trigger field for contact
	 *
	 * @param array $logs
	 * @param \BWFCRM_Contact $contact
	 * @param array $link_data
	 */
	protected function create_contact_logs_note( $logs, $contact, $link_data ) {
		if ( ! isset( $link_data['data']['add_contact_note'] ) || 1 !== absint( $link_data['data']['add_contact_note'] ) || empty( $logs ) ) {
			return;
		}

		$title = 'Link Trigger "' . $link_data['title'] . '" logs';
		$body  = is_array( $logs ) ? implode( ' | ', $logs ) : $logs;

		new BWFCRM_Note( 0, true, array(
			'cid'   => $contact->get_id(),
			'title' => $title,
			'body'  => $body,
			'type'  => 'log',
		) );
	}

	/**
	 * Updated link trigger field for contact
	 *
	 * @param $contact
	 * @param $link_id
	 * @param array $contact_clicked_ids
	 */
	protected function update_contact_link_click( $contact, $link_id, $contact_clicked_ids = array() ) {
		/** Check if link trigger id already exists */
		if ( ! in_array( $link_id, $contact_clicked_ids ) ) {

			$contact_clicked_ids[] = $link_id;
			$contact_clicked_ids   = wp_json_encode( array_unique( $contact_clicked_ids ) );

			/** Set updated link-trigger-click value */
			$contact->set_field_by_slug( 'link-trigger-click', $contact_clicked_ids );
			$contact->save_fields();
		}
	}

	/**
	 * Get contact Id
	 *
	 * @return array|false
	 */
	protected function get_contact_id() {
		/** check if URL contains contacts uid  */
		$uid = filter_input( INPUT_GET, 'bwfan-uid' );
		if ( ! empty( $uid ) ) {
			return array( 'uid' => $uid );
		}

		/** Check for logged-in user */
		$user = wp_get_current_user();
		if ( $user instanceof WP_User ) {
			return array( 'wpid' => $user->ID );
		}

		return false;
	}

	/**
	 * Get contact obj
	 *
	 * @return stdClass|WooFunnels_Contact
	 */
	protected function get_contact_obj( $data = false ) {
		/** Ready contact data */
		if ( empty( $data ) ) {
			$data = $this->get_contact_id();
		}

		/** Returns if data is empty */
		if ( false === $data ) {
			return new stdClass();
		}

		/** Initialize CRM Object  */
		$bwf_contacts = BWF_Contacts::get_instance();

		/** Get CRM contact object by CRM uid */
		if ( isset( $data['uid'] ) ) {
			return $bwf_contacts->get_contact_by( 'uid', $data['uid'] );
		}

		/** Get CRM contact object by WordPress user id */
		if ( isset( $data['wpid'] ) ) {
			return $bwf_contacts->get_contact_by( 'wpid', $data['wpid'] );
		}

		return new stdClass();
	}

	/**
	 * Process actions
	 *
	 * @param $actions
	 * @param $contact_crm
	 *
	 * @return array|void
	 */
	protected function process_actions( $actions, $contact_crm ) {
		$logs = array();

		/** Check for empty action */
		if ( empty( $actions ) ) {
			return;
		}
		/** Iterate over action */
		foreach ( $actions as $action => $data ) {
			$data = empty( $data ) ? [] : $data;

			/** Initialize Action Object */
			$action_obj = BWFCRM_Core()->actions->get_action_by_slug( $action );

			if ( ! $action_obj instanceof \BWFCRM\Actions\Base ) {
				continue;
			}

			if ( 'end_automation' === $action ) {
				$data['from']    = 'Link Trigger';
				$data['link_id'] = $this->link_id;
			}

			/** Call to action handler function */
			$result = $action_obj->handle_action( $contact_crm, $data );

			/** Collect Logs from actions */
			if ( is_array( $result ) && ! empty( $result['message'] ) ) {
				$message = is_array( $result['message'] ) ? $result['message'] : ( empty( $result['message'] ) ? array() : array( $result['message'] ) );
				$logs    = array_values( array_merge( $logs, $message ) );
			}
		}

		return $logs;
	}

	/**
	 * Update link clicks count
	 *
	 * @param $link_id
	 * @param $link_data
	 */
	protected function update_link_clicks( $link_id, $link_data ) {
		/** Update click count  */
		$clicked = isset( $link_data['total_clicked'] ) ? absint( $link_data['total_clicked'] ) : 0;
		$clicked ++;

		/** Link trigger data */
		$values = array(
			'total_clicked' => $clicked,
			'updated_at'    => current_time( 'mysql', 1 ),
		);

		/** Set condition */
		$where = array(
			'ID' => $link_id,
		);

		/** Update Link Trigger DB data */
		BWFAN_Model_Link_Triggers::update( $values, $where );
	}

	protected function maybe_login( $link_data ) {
		$key = 'enable_auto_login';

		/** If auto login disabled */
		if ( ! isset( $link_data['data'][ $key ] ) || 1 !== absint( $link_data['data'][ $key ] ) ) {
			return;
		}

		/** Get contact object */
		$db_contact = $this->contact_obj;

		if ( ! $db_contact instanceof WooFunnels_Contact ) {
			$db_contact = $this->get_contact_obj();
		}

		/** No contact found */
		if ( ! $db_contact instanceof WooFunnels_Contact || 0 === $db_contact->get_id() ) {
			return;
		}

		/** Is contact associated with WP User */
		$wp_id = $db_contact->get_wpid();
		if ( 0 === absint( $wp_id ) ) {
			return;
		}

		$user = get_user_by( 'ID', $wp_id );
		/** WP User not present */
		if ( ! $user instanceof WP_User ) {
			return;
		}

		/** If user role is admin or editor then return */
		$roles = array( 'administrator', 'editor' );
		if ( array_intersect( $roles, (array) $user->roles ) ) {
			return;
		}

		/** Check if expiry time reached */
		$crm_contact = new BWFCRM_Contact( $db_contact );
		$auth_hash   = filter_input( INPUT_GET, 'bwfan-auth' );
		if ( ! $this->verify_auth_hash( $crm_contact, $link_data, $auth_hash ) ) {
			return;
		}

		/** Auto login user */
		wp_set_current_user( $user->ID, $user->user_login );

		/** if WooCommerce is active */
		if ( function_exists( 'bwfan_is_woocommerce_active' ) && bwfan_is_woocommerce_active() ) {
			wc_set_customer_auth_cookie( $user->ID );
		} else {
			wp_set_auth_cookie( $user->ID );
		}
	}

	/**
	 * Check if URL contains link trigger string
	 *
	 * @param $status
	 * @param $url
	 *
	 * @return bool|mixed
	 */
	public function is_link_trigger_url( $status, $url ) {
		if ( empty( $url ) ) {
			return $status;
		}

		return ( false !== strpos( $url, 'bwfan-link-trigger' ) );
	}

	/**
	 * Check if URL contains merge tag
	 *
	 * @param $url
	 *
	 * @return bool
	 */
	public function url_contains_merge_tag( $url ) {
		return ( false !== strpos( $url, '{{' ) );
	}

	/**
	 * Appends the additional parameters to redirect link
	 *
	 * @param $url
	 *
	 * @return mixed|string
	 */
	public function may_append_query_arg( $url ) {
		$url        = apply_filters( 'bwfan_modify_link_trigger_target_url', $url, $this->contact_obj );
		$args       = filter_input_array( INPUT_GET );
		$filter_arr = [];
		foreach ( $args as $key => $arg ) {
			if ( strpos( $key, 'bwfan-' ) === false ) {
				$val = str_replace( '%20', '+', $arg );
				$val = str_replace( ' ', '+', $val );

				$filter_arr[ $key ] = $val;
			}
		}
		if ( empty( $filter_arr ) ) {
			return $url;
		}

		return add_query_arg( $filter_arr, $url );
	}

	/**
	 * @param BWFCRM_Contact $contact
	 * @param string $url
	 *
	 * @return false|string
	 */
	public function get_auth_hash( $contact, $url ) {
		$parsed_url = parse_url( $url );
		parse_str( $parsed_url['query'], $query );
		$link_hash = $query['bwfan-link-trigger'];

		if ( ! $contact instanceof BWFCRM_Contact || ! $contact->is_contact_exists() || empty( $link_hash ) ) {
			return false;
		}

		/** Get Link and Auto Login Data */
		$link = BWFAN_Model_Link_Triggers::bwfan_get_link_trigger( 0, $link_hash );
		if ( ! isset( $link['data']['enable_auto_login'] ) || empty( $link['data']['enable_auto_login'] ) || ! isset( $link['data']['auto_login'] ) ) {
			return false;
		}

		$auto_login = $link['data']['auto_login'];
		if ( empty( $auto_login ) || empty( $auto_login['text'] ) || empty( $auto_login['unit'] ) ) {
			return false;
		}

		/** Get New Expiry time based on the link data */
		$current_time = new DateTime( 'now' );
		$expiry_time  = ( $current_time )->modify( '+' . $auto_login['text'] . ' ' . $auto_login['unit'] );
		$expiry_time  = $expiry_time->getTimestamp();

		/** Populate Auth Field, if empty */
		$auth = $contact->get_field_by_slug( 'auth' );
		if ( ! empty( $auth ) ) {
			$auth = json_decode( $auth, true );
		}

		/** Populate auth hash if empty */
		$current_time = $current_time->getTimestamp();
		$auth_hash    = '';
		if ( ! is_array( $auth ) || 0 === count( $auth ) ) {
			$contact_id = $contact->get_id();
			$auth_hash  = md5( $contact_id . $current_time );
			$auth       = array( $auth_hash => '' );
		} else {
			$auth_hash = array_keys( $auth )[0];
		}

		/** Populate Expiry if empty or Old Expiry is less than New Expiry */
		$old_expiry_time = array_values( $auth )[0];
		if ( empty( $old_expiry_time ) || absint( $old_expiry_time ) < $expiry_time ) {
			$auth[ $auth_hash ] = $expiry_time;
			$contact->set_field_by_slug( 'auth', wp_json_encode( $auth ) );
			$contact->save_fields();
		}

		return $auth_hash;
	}

	public function verify_auth_hash( $contact, $link_data, $auth_hash ) {
		if ( ! $contact instanceof BWFCRM_Contact || ! $contact->is_contact_exists() || empty( $link_data ) || empty( $auth_hash ) ) {
			return false;
		}

		/** Check if auto login enabled */
		if ( 0 === absint( $link_data['data']['enable_auto_login'] ) ) {
			return false;
		}

		/** Get Auth from Contact Field, Check if it is valid */
		$auth = $contact->get_field_by_slug( 'auth' );
		$auth = ! empty( $auth ) ? json_decode( $auth, true ) : $auth;
		if ( ! is_array( $auth ) || 0 === count( $auth ) ) {
			return false;
		}

		/** Verify contact auth against auth hash */
		return ( isset( $auth[ $auth_hash ] ) && ( absint( $auth[ $auth_hash ] ) > time() ) );
	}

	/**
	 * Return contact link trigger field data
	 *
	 * @param $val
	 * @param $field
	 * @param $contact
	 *
	 * @return mixed
	 */
	public function link_trigger_contact_data( $val, $field, $contact ) {
		if ( $field === 'links' ) {
			$val = $contact->get_field_by_slug( 'link-trigger-click' );
			if ( ! empty( $val ) ) {
				$link_triggers = BWFAN_Model_Link_Triggers::get_link_triggers( '', '', 0, 0, false, json_decode( $val, true ) );
				$links         = [];

				if ( isset( $link_triggers['links'] ) && ! empty( $link_triggers['links'] ) ) {
					foreach ( $link_triggers['links'] as $link ) {
						if ( isset( $link['title'] ) ) {
							$links[] = $link['title'];
						}
					}
					$val = implode( ', ', $links );
				}
			}
		}

		return $val;
	}
}

/**
 * Register action handler to BWFCRM_Core
 */
if ( class_exists( 'BWFCRM_Link_Trigger_Handler' ) ) {
	BWFCRM_Core::register( 'link_trigger_handler', 'BWFCRM_Link_Trigger_Handler' );
}
