<?php

if ( ! BWFAN_PRO_Common::is_lite_3_0() && ! class_exists( 'BWFCRM_Model_Contact' ) ) {
	class BWFCRM_Model_Contact extends BWFAN_Model {
		static $primary_key = 'ID';

		private static $filter_queries = array();
		private static $filter_queries_params = array();
		private static $logs = array();

		protected static function _table() {
			global $wpdb;

			return $wpdb->prefix . 'bwf_contact';
		}

		/**
		 * Get first contact id
		 *
		 * @param $id
		 *
		 * @return string|null
		 */
		public static function get_first_contact_id( $id = 0 ) {
			global $wpdb;
			$where = $id ? "AND id > $id" : '';
			$query = 'SELECT MIN(id) from ' . self::_table() . ' WHERE 1=1 ' . $where;

			return $wpdb->get_var( $query );
		}

		public static function get_contacts( $search = '', $limit = 25, $offset = 0, $normalized_filters = array(), $additional_info = array(), $filter_match = ' AND ' ) {
			$grab_totals = is_array( $additional_info ) && isset( $additional_info['grab_totals'] ) && true === $additional_info['grab_totals'];
			$only_count  = is_array( $additional_info ) && isset( $additional_info['only_count'] ) && true === $additional_info['only_count'];

			/** Fallback for limit */
			if ( false === $only_count && empty( $limit ) ) {
				$limit = 25;
			}

			global $wpdb;

			self::set_log( array(
				'search'             => $search,
				'limit'              => $limit,
				'offset'             => $offset,
				'normalized_filters' => $normalized_filters,
				'additional_info'    => $additional_info,
				'filter_match'       => $filter_match,
			) );

			/** Get Contacts */
			$sql_queries = self::_get_contacts_sql( $search, $limit, $offset, $normalized_filters, $additional_info, $filter_match );
			self::set_log( 'contact sql: ' . $sql_queries['base'] );
			self::log();

			$total_count = '';
			if ( (bool) $grab_totals || (bool) $only_count ) {
				/** Total Count (For Pagination) */
				$total_count = $wpdb->get_var( $sql_queries['total'] );
				if ( (bool) $only_count ) {
					return array(
						'contacts' => array(),
						'total'    => $total_count,
					);
				}
			}

			$contacts = $wpdb->get_results( $sql_queries['base'], ARRAY_A );

			/** In case there is DB error and no contacts */
			if ( empty( $contacts ) && ! empty( $wpdb->last_error ) ) {
				BWFAN_Common::log_test_data( $wpdb->last_error, 'collation-issue', true );
				BWFAN_Fix_Collation::maybe_fix_collation_issue();
				$contacts = $wpdb->get_results( $sql_queries['base'], ARRAY_A );
			}

			if ( empty( $contacts ) ) {
				$error_text = ob_get_clean();

				return ! empty( $error_text ) ? $error_text : array();
			}

			/** Set Contacts in Cache */
			$ins = new WooFunnels_Contact();
			foreach ( $contacts as $contact ) {
				$ins->set_cache_object( 'cid', $contact['id'], (object) $contact );
			}
			unset( $ins );

			return array(
				'contacts' => $contacts,
				'total'    => $total_count,
			);
		}

		public static function _get_contacts_sql( $search = '', $limit = 25, $offset = 0, $filters = array(), $additional_info = array(), $filter_match = ' AND ', $column_preference = false ) {
			$should_send_wc = is_array( $additional_info ) && isset( $additional_info['customer_data'] ) && true === $additional_info['customer_data'];
			$should_send_cf = is_array( $additional_info ) && isset( $additional_info['grab_custom_fields'] ) && true === $additional_info['grab_custom_fields'];
			$contact_mode   = is_array( $additional_info ) && isset( $additional_info['fetch_base'] ) ? absint( $additional_info['fetch_base'] ) : 1;
			$exclude_unsubs = is_array( $additional_info ) && isset( $additional_info['exclude_unsubs'] ) && true === $additional_info['exclude_unsubs'];
			$order_by       = is_array( $additional_info ) && isset( $additional_info['order_by'] ) && ! empty( $additional_info['order_by'] ) ? $additional_info['order_by'] : 'last_modified';
			$order          = is_array( $additional_info ) && isset( $additional_info['order'] ) && ! empty( $additional_info['order'] ) ? $additional_info['order'] : 'DESC';

			$start_id       = is_array( $additional_info ) && isset( $additional_info['start_id'] ) && ! empty( $additional_info['start_id'] ) ? absint( $additional_info['start_id'] ) : 0;
			$end_id         = is_array( $additional_info ) && isset( $additional_info['end_id'] ) && ! empty( $additional_info['end_id'] ) ? absint( $additional_info['end_id'] ) : 0;
			$exclude_end_id = is_array( $additional_info ) && isset( $additional_info['exclude_end_id'] ) && true === $additional_info['exclude_end_id'];

			$exclude_ids = is_array( $additional_info ) && isset( $additional_info['exclude_ids'] ) && is_array( $additional_info['exclude_ids'] ) ? $additional_info['exclude_ids'] : array();
			$exclude_ids = array_map( 'absint', $exclude_ids );
			$exclude_ids = implode( ',', $exclude_ids );

			$include_ids = is_array( $additional_info ) && isset( $additional_info['include_ids'] ) && is_array( $additional_info['include_ids'] ) ? $additional_info['include_ids'] : array();
			$include_ids = array_map( 'absint', $include_ids );
			$include_ids = implode( ',', $include_ids );

			global $wpdb;
			$contact_table        = $wpdb->prefix . 'bwf_contact';
			$customer_table       = $wpdb->prefix . 'bwf_wc_customers';
			$contact_fields_table = $wpdb->prefix . 'bwf_contact_fields';

			/** Extract status filter, and unset the status filter if unsubscribe value */
			$status_filter = array();
			if ( ! empty( $filters ) && isset( $filters['c'] ) && ! empty( $filters['c'] ) ) {
				/** Extract the status filter */
				$status_filter = array_filter( $filters['c'], function ( $v ) {
					return ( isset( $v['key'] ) && 'status' === $v['key'] );
				}, ARRAY_FILTER_USE_BOTH );

				/** Remove un-subscribe status filter from Contact (c) filters */
				if ( ! empty( $status_filter ) ) {
					$status_filter = reset( $status_filter );
					$filters['c']  = array_filter( $filters['c'], function ( $v ) {
						return ( 'status' !== $v['key'] || 3 !== absint( $v['value'] ) );
					}, ARRAY_FILTER_USE_BOTH );
				}

				/** Unset contact filters array if there are no items, to prevent buggy SQL ANDs */
				if ( empty( $filters['c'] ) ) {
					unset( $filters['c'] );
				}
			}

			/** Contact (wp) and Customer (wc) SQL Queries */
			$filter_query = self::_get_filters_sql( $filters, $filter_match );

			/** Columns needed and JOINS */
			$wp_wc_columns = $should_send_wc ? 'DISTINCT c.*, wc.aov, wc.id as customer_id, wc.l_order_date, wc.f_order_date, wc.total_order_count, wc.total_order_value, wc.purchased_products, wc.purchased_products_cats, wc.purchased_products_tags, wc.used_coupons' : 'DISTINCT c.*';
			$join_wc_query = ( isset( $filters['wc'] ) || $should_send_wc ) ? "LEFT JOIN $customer_table as wc ON c.id = wc.cid" : '';
			$join_cf_query = '';

			/**
			 * $should_send_cf - passed from contact export as need to show the data from contact field table
			 */
			if ( $should_send_cf || isset( $filters['cm'] ) ) {
				$cf_columns = self::_get_contact_fields_columns_sql();
				if ( ! empty( $cf_columns ) ) {
					$wp_wc_columns = $should_send_cf ? $wp_wc_columns . ", " . $cf_columns : $wp_wc_columns;
					$join_cf_query = "LEFT JOIN $contact_fields_table as `cm` ON c.id = cm.cid";
				}
			}

			if ( isset( $filters['custom'] ) ) {
				$join_cf_query = apply_filters( 'bwfan_contact_sql_join_query', $join_cf_query, $filters['custom'] );
			}

			$group_by_query = '';
			if ( isset( $filters['custom'] ) ) {
				$group_by_query = apply_filters( 'bwfan_contact_sql_group_by_query', $group_by_query, $filters['custom'] );
				$group_by_query = ! empty( $group_by_query ) ? 'GROUP BY ' . $group_by_query : '';
			}

			/** Checking column preferences  */
			if ( true !== $column_preference ) {
				/** Columns needed and JOINS */
				$wp_wc_columns = $should_send_wc ? 'DISTINCT c.*, wc.aov, wc.id as customer_id, wc.l_order_date, wc.f_order_date, wc.total_order_count, wc.total_order_value, wc.purchased_products, wc.purchased_products_cats, wc.purchased_products_tags, wc.used_coupons' : 'DISTINCT c.*';
				$join_wc_query = ( isset( $filters['wc'] ) || $should_send_wc ) ? "LEFT JOIN $customer_table as wc ON c.id = wc.cid" : '';
				$join_cf_query = '';
				if ( $should_send_cf || isset( $filters['cm'] ) ) {
					$contact_field_columns = self::_get_contact_fields_columns_sql();
					$wp_wc_columns         = $should_send_cf && ! empty( $contact_field_columns ) ? "$wp_wc_columns, " . $contact_field_columns : $wp_wc_columns;
					$join_cf_query         = "LEFT JOIN $contact_fields_table as `cm` ON c.id = cm.cid";
				}

				if ( isset( $filters['custom'] ) ) {
					$join_cf_query = apply_filters( 'bwfan_contact_sql_join_query', $join_cf_query, $filters['custom'] );
				}

			} else {
				$join_cf_query = '';
				if ( isset( $filters['custom'] ) ) {
					$join_cf_query = apply_filters( 'bwfan_contact_sql_join_query', $join_cf_query, $filters['custom'] );
				}
			}

			/** Implode Filters SQL */
			$filter_query = implode( $filter_match, $filter_query );
			$filter_query = ! empty( $filter_query ) ? "AND ( $filter_query ) " : '';

			/** Filter for final WHERE SQL */
			$filter_query = apply_filters( 'bwfan_contact_sql_final_where_query', $filter_query );

			$search_query = '';
			if ( ! empty( trim( $search ) ) ) {
				$search = trim( $search );
				/** Search Contact by f_name, l_name, contact_no (phone), email */
				$search_query = "AND ( c.email like '%" . esc_sql( $search ) . "%' OR c.f_name like '%" . esc_sql( $search ) . "%' OR c.l_name LIKE '%" . esc_sql( $search ) . "%' OR c.contact_no LIKE '%" . esc_sql( $search ) . "%' )";

				/** Get f_name and l_name from search string and append in query */
				if ( false !== strpos( $search, ' ' ) ) {
					$search_arr   = explode( ' ', $search );
					$first_name   = isset( $search_arr[0] ) ? $search_arr[0] : '';
					$last_name    = ! empty( end( $search_arr ) ) ? end( $search_arr ) : '';
					$search_query .= ! empty( $first_name ) && ! empty( $last_name ) ? " OR ( c.f_name like '%" . esc_sql( $first_name ) . "%' AND c.l_name like '%" . esc_sql( $last_name ) . "%' )" : '';
				}
			}

			/** Check if one of Email or Phone must not empty */
			$empty_email_check = ( 2 === $contact_mode ) ? "AND ( c.contact_no != '' AND c.contact_no IS NOT NULL )" : "AND ( c.email != '' AND c.email IS NOT NULL )";
			/** Excludes Un-subscribers or not */
			$unsubs_query = self::_get_unsubscribers_query( $status_filter, $exclude_unsubs );
			/** Order, Order By, Limit, Offset */
			$order_column_alias = in_array( $order_by, BWFCRM_Filters::$wc_filters ) ? 'wc' : 'c';
			$order_by_query     = "ORDER BY {$order_column_alias}.{$order_by} {$order}";
			$pagination_query   = empty( $limit ) ? '' : "limit $offset, $limit";

			/** Exclude Contacts from this query */
			$exclude_ids_query = ! empty( $exclude_ids ) ? "AND c.id NOT IN ({$exclude_ids})" : '';

			/** Include Contacts into this query */
			$include_ids_query = ! empty( $include_ids ) ? "AND c.id IN ({$include_ids})" : '';

			/** Start ID and End ID queries */
			$start_id_query  = ! empty( $start_id ) ? "AND c.id > $start_id" : '';
			$end_id_operator = ( true === $exclude_end_id ) ? '<' : '<=';
			$end_id_query    = ! empty( $end_id ) ? "AND c.id $end_id_operator $end_id" : '';

			if ( true === $column_preference ) {
				$queries           = self::get_column_preferences_query( $join_cf_query, $join_wc_query, $status_filter, $filters, $additional_info );
				$base_query        = $queries['base_query'] . " $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query $group_by_query $order_by_query $pagination_query";
				$total_count_query = $queries['count_query'] . " $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query ";

				return array(
					'base'           => $base_query,
					'total'          => $total_count_query,
					'needs_to_added' => $queries['needs_to_added']
				);
			}
			$base_query = "SELECT $wp_wc_columns FROM $contact_table as c $join_wc_query $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query $group_by_query $order_by_query $pagination_query";

			$total_count_query = "SELECT COUNT(DISTINCT c.id) FROM $contact_table as c $join_wc_query $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query";

			return array(
				'base'  => $base_query,
				'total' => $total_count_query,
			);
		}

		public static function _get_unsubscribers_query( $filter, $exclude_unsubs = false ) {
			$exclude_unsub_query = "  NOT EXISTS ";
			$include_unsub_query = " EXISTS ";
			global $wpdb;
			$email_query      = "(SELECT 1 FROM {$wpdb->prefix}bwfan_message_unsubscribe AS unsub WHERE c.email = unsub.recipient )";
			$contact_no_query = "(SELECT 1 FROM {$wpdb->prefix}bwfan_message_unsubscribe AS unsub1 WHERE c.contact_no = unsub1.recipient )";

			if ( true === $exclude_unsubs ) {
				return "AND " . "( $exclude_unsub_query  $email_query OR $exclude_unsub_query $contact_no_query )";
			}

			/** Has to be valid filter */
			if ( ! is_array( $filter ) || ! isset( $filter['rule'] ) ) {
				return '';
			}

			/** For subscribed contacts */
			if ( true === $exclude_unsubs ) {
				return "AND " . "( $exclude_unsub_query  $email_query AND $exclude_unsub_query $contact_no_query )";
			}

			$filter_value = isset( $filter['value'] ) ? absint( $filter['value'] ) : '';
			/** when "status is not unsubscribed" */
			if ( 'is_not' === $filter['rule'] && 3 === $filter_value ) {
				return "AND " . "( $exclude_unsub_query  $email_query AND $exclude_unsub_query $contact_no_query )";
			}

			/** Include Un-subscribers when "status is unsubscribed" */
			if ( 'is' === $filter['rule'] && 3 === $filter_value ) {
				return "AND " . "( $include_unsub_query  $email_query OR $include_unsub_query $contact_no_query )";
			}

			/** Exclude Un-subscribers if any status other than 3 (unsubscribed) and rule = 'is' */
			if ( 'is' === $filter['rule'] && in_array( $filter_value, array( 0, 1, 2 ) ) ) {
				return "AND " . "( $exclude_unsub_query  $email_query AND $exclude_unsub_query $contact_no_query )";
			}

			return '';
		}

		public static function _get_filters_sql( $filters, $filter_match ) {
			$filter_query = array();
			foreach ( $filters as $filter_group_key => $filter_group ) {
				foreach ( $filter_group as $filter ) {
					/** Skip if filter doesn't have any or empty value */
					if ( ! isset( $filter['value'] ) ) {
						continue;
					}

					switch ( $filter['type'] ) {
						case BWFCRM_Filters::$TYPE_JSON_ARRAY:
							self::_set_json_array_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_STRING:
							self::_set_string_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_STRING_EXACT:
							self::_set_string_exact_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_DATE:
							self::_set_date_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_BOOL:
							self::_set_bool_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_NUMBER:
							self::_set_number_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_NUMBER_EXACT:
							self::_set_number_exact_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_CURRENCY:
							self::_set_currency_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_DATE_RELATIVE:
							self::_set_date_relative_filter_sql( $filter, $filter_group_key );
							break;
						case BWFCRM_Filters::$TYPE_CUSTOM_FILTER:
							self::_set_custom_filter_sql( $filter, $filter_group_key );
							break;
					}
				}

				if ( ! empty( self::$filter_queries ) ) {
					/** remove duplicate filter queries */
					self::$filter_queries = array_unique( self::$filter_queries );
					$filter_query[]       = implode( $filter_match, self::$filter_queries );
					self::$filter_queries = array();
				}
			}

			return $filter_query;
		}

		public static function _get_contact_fields_columns_sql() {
			$contact_fields = array_merge( array_keys( BWFCRM_Fields::$contact_fields ), array_keys( BWFCRM_Fields::$contact_address_fields ) );
			$db_fields      = BWFAN_Model_Fields::get_fields_by_multiple_slugs( $contact_fields );
			if ( empty( $db_fields ) ) {
				return '';
			}
			$columns = [];
			foreach ( $db_fields as $slug => $db_field ) {
				$columns[] = "cm.f{$db_field['ID']} AS '$slug'";
			}

			return implode( ', ', $columns );
		}

		public static function _set_json_array_filter_sql( $filter, $filter_group_key ) {
			$filter_value = $filter['value'];
			/** Checking if value is not array and group key is from custom field */
			if ( ! is_array( $filter_value ) || 'cm' === $filter_group_key ) {
				$filter_value = array( $filter_value );
			}

			foreach ( $filter_value as $val_item ) {
				$val_item = ! is_array( $val_item ) ? explode( ',', $val_item ) : $val_item;
				$val_item = array_map( 'trim', $val_item );
				$key      = $filter['key'];
				if ( 'is_blank' === $filter['rule'] ) {
					self::$filter_queries[] = "( {$filter_group_key}.{$key} IS NULL OR {$filter_group_key}.{$key} LIKE '[]' )";
					continue;
				}

				if ( 'is_not_blank' === $filter['rule'] ) {
					self::$filter_queries[] = "( {$filter_group_key}.{$key} IS NOT NULL AND {$filter_group_key}.{$key} NOT LIKE '[]' )";
					continue;
				}
				$rule     = in_array( $filter['rule'], array( 'contains', 'is', 'any', 'all' ) ) ? 'LIKE' : 'NOT LIKE';
				$sub_rule = ( 'LIKE' === $rule ) && ( 'all' !== $filter['rule'] ) ? 'OR' : 'AND';

				$val_item = array_map( function ( $val ) use ( $rule, $key, $sub_rule, $filter_group_key ) {
					return "$filter_group_key.$key $rule '%\"$val\"%'";
				}, $val_item );

				$val_item = array_filter( $val_item );
				$val_item = implode( " {$sub_rule} ", $val_item );

				$if_column_exists = ( 'NOT LIKE' === $rule ) ? "{$filter_group_key}.{$key} IS NULL OR" : '';
				$val_item         = ! empty( $if_column_exists ) ? "($if_column_exists ($val_item))" : "($val_item)";

				/** Group query set, not need to go further, so continue */
				self::$filter_queries[] = $val_item;
			}
		}

		public static function _set_string_filter_sql( $filter, $filter_group_key ) {
			$filter_value = $filter['value'];
			$filter_rule  = $filter['rule'];
			$filter_key   = $filter['key'];

			if ( ( 'country' === $filter_key || 'state' === $filter_key ) && strpos( $filter_value, ',' ) ) {
				$filter_value = explode( ',', $filter_value );
			}

			switch ( $filter_rule ) {
				case 'is':
					$filter_rule = is_array( $filter_value ) ? 'IN' : '=';
					break;
				case 'is_not':
					$filter_rule = is_array( $filter_value ) ? 'NOT IN' : '!=';
					break;
				case 'contains':
					$filter_rule  = 'LIKE';
					$filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) {
						$fil_val = trim( $fil_val );

						return "{$filter_group_key}.{$filter_key} LIKE '%$fil_val%'";
					}, $filter_value ) : "%{$filter_value}%";
					break;
				case 'not_contains':
					$filter_rule  = 'NOT LIKE';
					$filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) {
						$fil_val = trim( $fil_val );

						return "{$filter_group_key}.{$filter_key} NOT LIKE '%$fil_val%'";
					}, $filter_value ) : "%{$filter_value}%";
					break;
				case 'starts_with':
					$filter_rule  = 'LIKE';
					$filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) {
						$fil_val = trim( $fil_val );

						return "{$filter_group_key}.{$filter_key} LIKE '$fil_val%'";
					}, $filter_value ) : "{$filter_value}%";
					break;
				case 'ends_with':
					$filter_rule  = 'LIKE';
					$filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) {
						$fil_val = trim( $fil_val );

						return "{$filter_group_key}.{$filter_key} LIKE '%$fil_val'";
					}, $filter_value ) : "%{$filter_value}";
					break;
				case 'is_blank':
					$filter_rule  = '=';
					$filter_value = '';
					break;
				case 'is_not_blank':
					$filter_rule  = '!=';
					$filter_value = '';
					break;
				default:
					return;
			}

			$if_column_exists = '';
			if ( in_array( $filter['rule'], array( 'not_contains', 'is_not', 'is_blank' ) ) ) {
				$if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR";
			}

			if ( 'is_not_blank' === $filter['rule'] ) {
				$if_column_exists = "{$filter_group_key}.{$filter_key} IS NOT NULL AND";
			}

			if ( 'state' === $filter_key && is_array( $filter_value ) && in_array( $filter['rule'], array( 'contains', 'not_contains', 'starts_with', 'ends_with' ), true ) ) {
				$filter_value           = array_filter( $filter_value );
				$sub_rule               = ( 'LIKE' === $filter_rule ) ? 'OR' : 'AND';
				$filter_value           = implode( " {$sub_rule} ", $filter_value );
				self::$filter_queries[] = "($if_column_exists $filter_value)";

				return;
			}

			if ( is_array( $filter_value ) ) {
				$filter_value = "('" . implode( "','", array_map( 'trim', $filter_value ) ) . "')";
			} else {
				$filter_value = "'$filter_value'";
			}

			self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $filter_value)";
		}

		public static function _set_string_exact_filter_sql( $filter, $filter_group_key ) {
			self::_set_string_filter_sql( $filter, $filter_group_key );
		}

		public static function _set_number_exact_filter_sql( $filter, $filter_group_key ) {
			self::_set_number_filter_sql( $filter, $filter_group_key );
		}

		public static function _set_date_filter_sql( $filter, $filter_group_key, $null_checking = true ) {
			$filter_key = $filter['key'];
			if ( empty( $filter['value'] ) ) {
				return '';
			}

			if ( is_array( $filter['value'] ) && 1 === count( $filter['value'] ) ) {
				$filter['value'] = $filter['value'][0];
			}

			if ( is_array( $filter['value'] ) ) {
				/** For 'between' rule */
				$filter_before_date = bwfcrm_maybe_get_datetime( $filter['value'][0] );
				$filter_after_date  = bwfcrm_maybe_get_datetime( $filter['value'][1] );

				$filter_before_date = date( 'Y-m-d', $filter_before_date->getTimestamp() );
				$filter_after_date  = date( 'Y-m-d', $filter_after_date->getTimestamp() );

				$filter_before_date = "$filter_before_date 00:00:00";
				$filter_after_date  = "$filter_after_date 23:59:59";

				$filter_rule = '=';
			} else {
				$filter_value = bwfcrm_maybe_get_datetime( $filter['value'] );

				$filter_date        = date( 'Y-m-d', $filter_value->getTimestamp() );
				$filter_before_date = "$filter_date 00:00:00";
				$filter_after_date  = "$filter_date 23:59:59";

				$filter_rule = ( 'is' === $filter['rule'] ) ? '=' : ( 'before' === $filter['rule'] ? '<' : '>' );
			}

			$where = '';
			if ( isset( $filter_rule ) && '=' !== $filter_rule ) {
				/** For 'more_than' and 'less_than' rules */
				$date_value = ( ( '>' === $filter_rule ) ? $filter_after_date : $filter_before_date );
				$where      = "($filter_group_key.$filter_key $filter_rule '$date_value')";
			} else {
				/** For 'is' and 'between' rules */
				$where = "($filter_group_key.$filter_key >= '$filter_before_date' AND $filter_group_key.$filter_key <= '$filter_after_date')";
			}

			self::$filter_queries[] = $where;

			return $where;
		}

		public static function _set_bool_filter_sql( $filter, $filter_group_key ) {
			$filter_value = $filter['value'];
			$filter_key   = $filter['key'];
			switch ( $filter_key ) {
				case 'status':
					$filter_value           = ( ( 'yes' === $filter_value ) ? 1 : 0 );
					self::$filter_queries[] = "$filter_group_key.$filter_key = $filter_value";

					return;
				case 'has_purchased':
					if ( 'yes' === $filter_value ) {
						self::$filter_queries[] = 'wc.total_order_count > 0';
					} else {
						self::$filter_queries[] = '(wc.total_order_count = 0 OR wc.total_order_count IS NULL )';
					}

					return;
				case 'has_used_any_coupons':
					if ( 'yes' === $filter_value ) {
						self::$filter_queries[] = "(wc.used_coupons IS NOT NULL AND wc.used_coupons != '' AND wc.used_coupons != '[]')";
					} else {
						self::$filter_queries[] = "(wc.used_coupons IS NULL OR wc.used_coupons = '' OR wc.used_coupons = '[]')";
					}

					return;
			}
		}

		public static function _set_number_filter_sql( $filter, $filter_group_key ) {
			$filter_value = $filter['value'];
			$filter_rule  = $filter['rule'];
			$filter_key   = $filter['key'];

			/** Only on Status filter */
			/** Include Un-subscribers if any status other than 3 (unsubscribed) and rule = 'is_not' */
			$include_unsub_query_or = '';
			if ( 'status' === $filter_key && 'is_not' === $filter_rule && in_array( $filter_value, array( 0, 1, 2 ) ) ) {
				/** Using OR query because contact maybe not fall within status (0,1,2), but is present in unsubscribed table */ global $wpdb;
				$include_unsub_query_or = "OR EXISTS (SELECT 1 FROM {$wpdb->prefix}bwfan_message_unsubscribe AS unsub WHERE c.email = unsub.recipient )";
			}

			switch ( $filter_rule ) {
				case 'is':
					$filter_rule = '=';
					break;
				case 'is_not':
					$filter_rule = '!=';
					break;
				case 'more_than':
					$filter_rule = '>';
					break;
				case 'more_than_equal':
					$filter_rule = '>=';
					break;
				case 'less_than':
					$filter_rule = '<';
					break;
				case 'less_then_equal':
					$filter_rule = '<=';
					break;
				case 'is_blank':
					$filter_rule  = '=';
					$filter_value = 0;
					break;
				case 'is_not_blank':
					$filter_rule  = '!=';
					$filter_value = 0;
					break;
			}

			$if_column_exists = '';
			if ( in_array( $filter['rule'], array( 'less_than', 'is_not', 'is_blank', 'less_than_equal' ) ) ) {
				$if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR";
			}

			if ( 'is_not_blank' === $filter['rule'] ) {
				$if_column_exists = "{$filter_group_key}.{$filter_key} IS NOT NULL AND";
			}

			/** check for property in case value 0 and rule is equal */
			if ( in_array( $filter['rule'], array( 'less_than', 'is' ) ) && 0 === absint( $filter_value ) ) {
				$if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR";
			}

			/** check for property in case value 0 and rule is not equal */
			if ( 'is_not' === $filter['rule'] && 0 === absint( $filter_value ) ) {
				$if_column_exists = '';
			}

			if ( ! is_array( $filter_value ) ) {
				self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $filter_value $include_unsub_query_or)";

				return;
			}

			foreach ( $filter_value as $values ) {
				$values                 = trim( $values );
				self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $values $include_unsub_query_or)";
			}
		}

		public static function _set_currency_filter_sql( $filter, $filter_group_key ) {
			$filter_value = $filter['value'];
			$filter_rule  = $filter['rule'];
			$filter_key   = $filter['key'];

			switch ( $filter_rule ) {
				case 'more_than':
					$filter_rule = '>';
					break;
				case 'more_than_equal':
					$filter_rule = '>=';
					break;
				case 'less_than':
					$filter_rule = '<';
					break;
				case 'less_then_equal':
					$filter_rule = '<=';
					break;
				default:
					return;
			}

			$if_column_exists = '';
			if ( in_array( $filter['rule'], array( 'less_than', 'less_than_equal' ) ) ) {
				$if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR";
			}

			if ( ! is_array( $filter_value ) ) {
				self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $filter_value)";

				return;
			}
			foreach ( $filter_value as $values ) {
				$values                 = trim( $values );
				self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $values)";
			}
		}

		public static function _set_date_relative_filter_sql( $filter, $filter_group_key ) {
			$filter_key = $filter['key'];
			if ( empty( $filter['value'] ) ) {
				return;
			}

			$filter_value = '';
			if ( ! is_array( $filter['value'] ) ) {
				$filter_value = absint( $filter['value'] );
				$date         = $datetime = new DateTime( 'now', wp_timezone() );
				$date->modify( "-$filter_value days" );
				$filter_value = $date->format( 'Y-m-d' );
			} else {
				$filter_value = array();

				$from = absint( $filter['value'][0] );
				$date = $datetime = new DateTime( 'now', wp_timezone() );
				$date->modify( "-$from days" );
				$filter_value[] = $date->format( 'Y-m-d' );

				$to   = absint( $filter['value'][1] );
				$date = $datetime = new DateTime( 'now', wp_timezone() );
				$date->modify( "-$to days" );
				$filter_value[] = $date->format( 'Y-m-d' );
			}

			switch ( $filter['rule'] ) {
				case 'over':
					$filter_rule = '<';
					break;
				case 'past':
					$filter_rule = '>=';
					break;
				case 'between':
					$filter_rule = 'between';
					break;
				default:
					return;
			}

			if ( ! is_array( $filter_value ) ) {
				self::$filter_queries[] = "($filter_group_key.$filter_key $filter_rule '$filter_value')";
			} else {
				self::$filter_queries[] = "($filter_group_key.$filter_key >= '" . $filter_value[1] . "' AND $filter_group_key.$filter_key <= '" . $filter_value[0] . "')";
			}
		}

		public static function _set_custom_filter_sql( $filter, $filter_group_key ) {
			$filter_value = $filter['value'];
			$filter_rule  = $filter['rule'];
			$filter_key   = $filter['key'];

			$filter_query = apply_filters( 'bwfan_contact_sql_where_query', '', $filter_key, $filter_rule, $filter_value );
			if ( empty( $filter_query ) ) {
				return;
			}

			self::$filter_queries[] = $filter_query;
		}

		public static function get_automations( $contact_id ) {
			if ( 0 === $contact_id ) {
				return array();
			}

			global $wpdb;
			$query               = "Select automation_id, time from {$wpdb->prefix}bwfan_contact_automations where contact_id ='" . $contact_id . "' ORDER BY time DESC";
			$contact_automations = $wpdb->get_results( $query, ARRAY_A );
			if ( empty( $contact_automations ) ) {
				return array();
			}

			return $contact_automations;
		}

		/**
		 * @param $contact_id
		 *
		 * @return bool
		 */
		public static function delete_contact( $contact_id ) {
			global $wpdb;

			$contact_table                 = $wpdb->prefix . 'bwf_contact';
			$contact_meta_table            = $wpdb->prefix . 'bwf_contact_meta';
			$contact_customer_table        = $wpdb->prefix . 'bwf_wc_customers';
			$contact_fields_table          = $wpdb->prefix . 'bwf_contact_fields';
			$contact_note_table            = $wpdb->prefix . 'bwfan_contact_note';
			$contact_automations_table     = $wpdb->prefix . 'bwfan_contact_automations';
			$contact_conversions_table     = $wpdb->prefix . 'bwfan_conversions';
			$contact_engagement_table      = $wpdb->prefix . 'bwfan_engagement_tracking';
			$contact_engagement_meta_table = $wpdb->prefix . 'bwfan_engagement_trackingmeta';
			$contact_unsubscribe_table     = $wpdb->prefix . 'bwfan_message_unsubscribe';
			$automation_contact            = $wpdb->prefix . 'bwfan_automation_contact';
			$automation_complete_contact   = $wpdb->prefix . 'bwfan_automation_complete_contact';
			$automation_contact_trail      = $wpdb->prefix . 'bwfan_automation_contact_trail';

			$contact_email = $wpdb->get_col( "select email from {$contact_table} where id=$contact_id" );
			$contact_phone = $wpdb->get_col( "select contact_no from {$contact_table} where id=$contact_id" );

			$delete_contact = $wpdb->delete( $contact_table, array( 'id' => $contact_id ) );
			if ( ! $delete_contact ) {
				/** If no contact then return false */
				return false;
			}

			$wpdb->delete( $contact_meta_table, array( 'contact_id' => $contact_id ) );
			$wpdb->delete( $contact_customer_table, array( 'cid' => $contact_id ) );
			$wpdb->delete( $contact_fields_table, array( 'cid' => $contact_id ) );
			$wpdb->delete( $contact_note_table, array( 'cid' => $contact_id ) );

			/** check if the automation run for v1 */
			if ( BWFAN_Common::is_automation_v1_active() ) {
				$wpdb->delete( $contact_automations_table, array( 'contact_id' => $contact_id ) );
			}

			$wpdb->delete( $contact_conversions_table, array( 'cid' => $contact_id ) );

			/** Delete complete and active Automation by contact id */
			$wpdb->delete( $automation_contact, array( 'cid' => $contact_id ) );
			$wpdb->delete( $automation_complete_contact, array( 'cid' => $contact_id ) );
			$wpdb->delete( $automation_contact_trail, array( 'cid' => $contact_id ) );

			$wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE meta_key LIKE '_woofunnel_cid' AND meta_value = $contact_id" );
			$wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE meta_key LIKE '_woofunnel_custid' AND meta_value = $contact_id" );

			$engagement_meta_query = "DELETE $contact_engagement_meta_table
				FROM $contact_engagement_meta_table
				INNER JOIN $contact_engagement_table ON $contact_engagement_table.id = $contact_engagement_meta_table.eid
				WHERE $contact_engagement_table.cid = $contact_id";

			/** delete from engagement meta and engagement table */
			$wpdb->query( $engagement_meta_query );
			$wpdb->delete( $contact_engagement_table, array( 'cid' => $contact_id ) );

			/** delete from the unsubscribe table */

			if ( is_array( $contact_email ) && ! empty( $contact_email[0] ) ) {
				$wpdb->delete( $contact_unsubscribe_table, array( 'recipient' => $contact_email[0] ) );
			}

			if ( is_array( $contact_phone ) && ! empty( $contact_phone[0] ) ) {
				$wpdb->delete( $contact_unsubscribe_table, array( 'recipient' => $contact_phone[0] ) );
			}

			return true;
		}

		/**
		 * @param $contact_ids
		 *
		 * @return bool
		 */
		public static function delete_multiple_contacts( $contact_ids ) {
			global $wpdb;

			/** Remove Contacts from every other place */
			$contact_table          = $wpdb->prefix . 'bwf_contact';
			$contact_meta_table     = $wpdb->prefix . 'bwf_contact_meta';
			$contact_customer_table = $wpdb->prefix . 'bwf_wc_customers';
			$contact_fields_table   = $wpdb->prefix . 'bwf_contact_fields';

			$ids    = implode( ',', array_map( 'absint', $contact_ids ) );
			$status = true;
			foreach ( $contact_ids as $contact_id ) {
				$status = self::delete_contact( $contact_id );
			}

			return $status;
		}

		public static function get_contact_aov( $contact_id ) {
			global $wpdb;
			if ( empty( $contact_id ) ) {
				return 0;
			}

			$contact_id = intval( $contact_id );

			$contact_orders_total_results = [];
			if ( method_exists( 'BWF_WC_Compatibility', 'is_hpos_enabled' ) && BWF_WC_Compatibility::is_hpos_enabled() ) {
				$contact_orders_total_query = "SELECT count(wc_stats.order_id) as orders, SUM(wc_stats.total_sales) as total 
									FROM {$wpdb->prefix}wc_order_stats as wc_stats 
									LEFT JOIN {$wpdb->prefix}wc_orders_meta pm 
									ON wc_stats.order_id =pm.order_id 
									WHERE wc_stats.status = 'wc-completed' 
									and pm.meta_key='_woofunnel_cid'
									and pm.meta_value=$contact_id";

				$contact_orders_total_results = $wpdb->get_results( $contact_orders_total_query, ARRAY_A );
				if ( empty( $contact_orders_total_results ) ) {
					return 0;
				}
				$aov_contact_order_total = 0;
				if ( ! empty( $contact_orders_total_results[0]['orders'] ) && ! empty( $contact_orders_total_results[0]['total'] ) ) {
					$aov_contact_order_total = ( $contact_orders_total_results[0]['total'] / $contact_orders_total_results[0]['orders'] );
				}

				return $aov_contact_order_total;
			}

			if ( empty( $contact_orders_total_results ) ) {
				$contact_orders_total_query = "SELECT count(wc_stats.order_id) as orders, SUM(wc_stats.total_sales) as total 
									FROM {$wpdb->prefix}wc_order_stats as wc_stats 
									LEFT JOIN {$wpdb->prefix}postmeta pm 
									ON wc_stats.order_id =pm.post_id 
									WHERE wc_stats.status = 'wc-completed' 
									and pm.meta_key='_woofunnel_cid'
									and pm.meta_value=$contact_id";

				$contact_orders_total_results = $wpdb->get_results( $contact_orders_total_query, ARRAY_A );
			}

			if ( empty( $contact_orders_total_results ) ) {
				return 0;
			}

			$aov_contact_order_total = 0;

			if ( ! empty( $contact_orders_total_results[0]['orders'] ) && ! empty( $contact_orders_total_results[0]['total'] ) ) {
				$aov_contact_order_total = ( $contact_orders_total_results[0]['total'] / $contact_orders_total_results[0]['orders'] );
			}

			return $aov_contact_order_total;
		}

		/**
		 * @param $contact_id
		 *
		 * @return array|object
		 */
		public static function get_bump_details( $contact_id ) {
			if ( empty( $contact_id ) ) {
				return array();
			}

			global $wpdb;
			$bump_data['accepted'] = 0;
			$bump_data['revenue']  = 0;

			$bump_query   = "SELECT bump.bid as 'object_id',
						bump.total as 'total_revenue', 
						'bump' as 'type' FROM {$wpdb->prefix}wfob_stats AS bump WHERE bump.cid='" . $contact_id . "' AND bump.converted=1";
			$bump_results = $wpdb->get_results( $bump_query, ARRAY_A );
			if ( empty( $bump_results ) ) {
				return $bump_data;
			}

			$bump_data['accepted'] = count( $bump_results );
			$total_revenue_array   = array_column( $bump_results, 'total_revenue' );
			$bump_data['revenue']  = number_format( array_sum( $total_revenue_array ), 2 );

			return $bump_data;
		}

		/**
		 * @param $contact_id
		 *
		 * @return array|object
		 */
		public static function get_upstroke_details( $contact_id ) {
			if ( empty( $contact_id ) ) {
				return array();
			}
			global $wpdb;
			$upstroke_data['accepted'] = 0;

			$upstroke_query  = "SELECT event.action_type_id,event.value 
						   FROM {$wpdb->prefix}wfocu_event as event LEFT JOIN {$wpdb->prefix}wfocu_session as session 
						   ON event.sess_id = session.id 
						   WHERE (event.action_type_id = 4) AND session.cid='" . $contact_id . "'";
			$upstroke_result = $wpdb->get_results( $upstroke_query, ARRAY_A );
			if ( empty( $upstroke_result ) ) {
				return $upstroke_data;
			}

			$upstroke_data['accepted'] = count( $upstroke_result );
			$total_revenue_array       = array_column( $upstroke_result, 'value' );
			$upstroke_data['revenue']  = array_sum( $total_revenue_array );

			return $upstroke_data;
		}

		/**
		 * @param $contact_id
		 * @param $note_id
		 *
		 * @return bool
		 */
		public static function delete_notes( $contact_id, $note_id ) {
			if ( empty( $contact_id ) ) {
				return false;
			}

			global $wpdb;
			$contact_note_table = $wpdb->prefix . 'bwfan_contact_note';
			$where              = array();

			if ( empty( $note_id ) ) {
				$where['cid'] = $contact_id;
			} else {
				$where['id']  = $note_id;
				$where['cid'] = $contact_id;
			}

			$delete_contact_notes = $wpdb->delete( $contact_note_table, $where );
			if ( ! $delete_contact_notes ) {
				return false;
			}

			return true;
		}

		/**
		 * @param $contact_id
		 *
		 * @return mixed
		 */
		public static function get_checkout_details( $contact_id ) {
			global $wpdb;

			$query         = "SELECT  count(aero.wfacp_id) as total_orders, sum(aero.total_revenue) as 'total_revenue' FROM " . $wpdb->prefix . 'bwf_contact' . ' AS contact JOIN ' . $wpdb->prefix . 'wfacp_stats' . " AS aero ON contact.id=aero.cid WHERE aero.cid=$contact_id  ORDER BY aero.date DESC";
			$checkout_data = $wpdb->get_results( $query, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			$return = array();

			$return['orders']  = ( isset( $checkout_data[0] ) && isset( $checkout_data[0]['total_orders'] ) ) ? absint( $checkout_data[0]['total_orders'] ) : 0;
			$return['revenue'] = ( isset( $checkout_data[0] ) && isset( $checkout_data[0]['total_revenue'] ) ) ? $checkout_data[0]['total_revenue'] : 0;

			return $return;
		}

		/**
		 * @param $contact_id
		 *
		 * @return int|mixed
		 */
		public static function get_optin_details( $contact_id ) {
			global $wpdb;
			$optin_submission = 0;

			$query      = 'SELECT count(optin.id) as total_submission FROM ' . $wpdb->prefix . 'bwf_contact' . ' AS contact JOIN ' . $wpdb->prefix . 'bwf_optin_entries' . " AS optin ON contact.id=optin.cid WHERE optin.cid=$contact_id ORDER BY optin.date DESC";
			$optin_data = $wpdb->get_results( $query, ARRAY_A );
			if ( ! isset( $optin_data[0]['total_submission'] ) || empty( $optin_data[0]['total_submission'] ) ) {
				return $optin_submission;
			}

			return $optin_data[0]['total_submission'];
		}

		/**
		 * @param int $limit
		 * @param int $offset
		 * @param false $wc_data_send
		 *
		 * @return array
		 */
		public static function get_recent_contacts( $limit = 10, $offset = 0, $wc_data_send = false ) {
			$additional_info = array(
				'order_by'       => 'creation_date',
				'order'          => 'DESC',
				'exclude_unsubs' => true,
				'customer_data'  => $wc_data_send,
			);
			// $saved_last_id   = get_option( 'bwfan_show_contacts_from' );
			// if ( ! empty( $saved_last_id ) ) {
			// $additional_info['start_id'] = $saved_last_id;
			// }

			return self::get_contacts( '', $limit, $offset, array(), $additional_info );
		}

		/**
		 * @param int $limit
		 * @param int $offset
		 * @param false $wc_data_send
		 *
		 * @return array
		 */
		public static function get_recent_unsubscribers( $limit = 10, $offset = 0, $wc_data_send = false ) {
			return self::get_contacts( '', $limit, $offset, array(), array(
				'order_by'       => 'last_modified',
				'order'          => 'DESC',
				'exclude_unsubs' => false,
				'customer_data'  => $wc_data_send,
			) );
		}

		/**
		 * @param int $limit
		 * @param int $offset
		 * @param false $wc_get_data
		 *
		 * @return array
		 */
		public static function get_recent_abandoned( $limit = 10, $offset = 0, $wc_get_data = false ) {
			global $wpdb;

			$contact_table  = $wpdb->prefix . 'bwf_contact';
			$cart_table     = $wpdb->prefix . 'bwfan_abandonedcarts';
			$customer_table = $wpdb->prefix . 'bwf_wc_customers';
			$wp_wc_columns  = $wc_get_data ? 'c.*, wc.id as customer_id, wc.l_order_date, wc.f_order_date, wc.total_order_count, wc.total_order_value, wc.purchased_products, wc.purchased_products_cats, wc.purchased_products_tags, wc.used_coupons' : 'c.*';
			$join_wc_query  = $wc_get_data ? "LEFT JOIN $customer_table as wc ON c.id = wc.cid" : '';
			$cart_join      = "LEFT JOIN $contact_table as c ON ab.email = c.email";
			$base_query     = "SELECT $wp_wc_columns FROM $cart_table as ab $cart_join $join_wc_query WHERE 1=1 and ab.status IN (0,1,3,4) and c.id IS NOT NULL ORDER BY ab.last_modified DESC LIMIT $offset,$limit";
			$contacts       = $wpdb->get_results( $base_query, ARRAY_A );

			if ( ! is_array( $contacts ) || 0 === count( $contacts ) ) {
				return array(
					'contacts' => array(),
					'total'    => 0,
				);
			}

			$total_count = count( $contacts );

			return array(
				'contacts' => $contacts,
				'total'    => $total_count,
			);
		}

		public static function get_last_contact_id() {
			global $wpdb;
			$table_name = self::_table();
			$query      = "SELECT max(id) as last_id FROM $table_name";

			return $wpdb->get_var( $query );
		}

		public static function set_log( $log ) {
			if ( empty( $log ) ) {
				return;
			}
			self::$logs[] = array(
				't' => microtime( true ),
				'd' => $log,
			);
		}

		public static function log() {
			if ( ! is_array( self::$logs ) || 0 === count( self::$logs ) ) {
				return;
			}
			if ( false === apply_filters( 'bwfan_allow_contact_query_logging', false ) ) {
				return;
			}
			add_filter( 'bwfan_before_making_logs', '__return_true' );
			BWFAN_Core()->logger->log( print_r( self::$logs, true ), 'crm_contacts_query' );

			self::$logs = [];
		}

		/**
		 * Get contacts with column preferences
		 *
		 */
		public static function get_contact_listing( $search = '', $limit = 25, $offset = 0, $normalized_filters = array(), $additional_info = array(), $filter_match = ' AND ' ) {
			global $wpdb;
			/** Get Contacts */
			$sql_queries = self::_get_contacts_sql( $search, $limit, $offset, $normalized_filters, $additional_info, $filter_match, true );
			$contacts    = $wpdb->get_results( $sql_queries['base'], ARRAY_A );

			/** In case there is DB error and no contacts */
			if ( empty( $contacts ) && ! empty( $wpdb->last_error ) ) {
				BWFAN_Common::log_test_data( $wpdb->last_error, 'collation-issue', true );
				BWFAN_Fix_Collation::maybe_fix_collation_issue();
				$contacts = $wpdb->get_results( $sql_queries['base'], ARRAY_A );
			}

			$contacts = self::prepared_data( $contacts, $sql_queries['needs_to_added'] );

			return array(
				'contacts' => $contacts,
				'total'    => $wpdb->get_var( $sql_queries['total'] )
			);
		}

		/**
		 * Preparing the data for get contacts with column preference
		 *
		 * @param $contacts
		 * @param $columns_needs_to_add
		 *
		 * @return array|void[]
		 */
		public static function prepared_data( $contacts, $columns_needs_to_add ) {
			if ( empty( $contacts ) ) {
				return [];
			}

			return array_map( function ( $contact ) use ( $columns_needs_to_add ) {
				foreach ( $contact as $slug => $value ) {
					$date_columns = [ 'dob', 'last-login', 'last-open', 'last-click', 'last-sent' ];
					if ( false !== strpos( $slug, 'date' ) || in_array( $slug, $date_columns, true ) ) {
						$contact[ $slug ] = self::get_formatted_date( $value );
						continue;
					}

					if ( false !== strpos( $slug, 'bwf_cf' ) ) {
						$field_id   = str_replace( 'bwf_cf', '', $slug );
						$field_type = BWFAN_Model_Fields::get_field_type( $field_id );

						switch ( absint( $field_type ) ) {
							case BWFCRM_Fields::$TYPE_DATE :
								$value = self::get_formatted_date( $value );
								break;
							case BWFCRM_Fields::$TYPE_CHECKBOX :
								$options = ! empty( $value ) ? json_decode( $value, true ) : [];
								$value   = [];
								if ( ! empty( $options ) ) {
									foreach ( $options as $option ) {
										$value[] = [
											'id'   => $option,
											'name' => $option,
										];
									}
								}
								break;
						}

						$contact[ $slug ] = $value;
					}
				}

				/** if contact is un-subscribed then status set as 3 */
				if ( isset( $contact['status'] ) ) {
					$email             = isset( $contact['email'] ) ? $contact['email'] : '';
					$contact_no        = isset( $contact['contact_no'] ) ? $contact['contact_no'] : '';
					$is_unsubscribed   = self::is_contact_unsubscribed( $email, $contact_no );
					$contact['status'] = ! empty( $is_unsubscribed ) ? 3 : $contact['status'];
				}

				/** Get Purchased Products titles */
				if ( isset( $contact['purchased_products'] ) ) {
					$purchased_products            = ! empty( $contact['purchased_products'] ) ? json_decode( $contact['purchased_products'], true ) : [];
					$contact['purchased_products'] = bwfan_is_woocommerce_active() ? array_filter( array_map( function ( $product_id ) {
						$product = wc_get_product( $product_id );
						if ( ! $product instanceof WC_Product ) {
							return false;
						}
						$pid = $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id();

						return ! $product->is_type( 'variable' ) ? array(
							'id'   => $pid,
							'name' => $product->get_name( 'edit' ),
							'link' => get_edit_post_link( $pid ),
						) : false;
					}, array_unique( $purchased_products ) ) ) : [];
				}

				/** Get Purchased Products Cats */
				if ( isset( $contact['purchased_products_cats'] ) ) {
					$purchased_products_cats            = ! empty( $contact['purchased_products_cats'] ) ? json_decode( $contact['purchased_products_cats'], true ) : [];
					$purchased_products_cats            = array_map( function ( $term_id ) {
						$cat = get_term( absint( $term_id ) );

						return $cat instanceof WP_Term ? array(
							'id'   => $cat->term_id,
							'name' => $cat->name,
							'link' => get_edit_term_link( $cat ),
						) : false;
					}, array_unique( $purchased_products_cats ) );
					$contact['purchased_products_cats'] = array_values( array_filter( $purchased_products_cats ) );
				}

				/** Get Purchased Products Tags */
				if ( isset( $contact['purchased_products_tags'] ) ) {
					$purchased_products_tags            = ! empty( $contact['purchased_products_tags'] ) ? json_decode( $contact['purchased_products_tags'], true ) : [];
					$purchased_products_tags            = array_map( function ( $term_id ) {
						$tag = get_term( absint( $term_id ) );

						return $tag instanceof WP_Term ? array(
							'id'   => $tag->term_id,
							'name' => $tag->name,
							'link' => get_edit_term_link( $tag ),
						) : false;
					}, array_unique( $purchased_products_tags ) );
					$contact['purchased_products_tags'] = array_values( array_filter( $purchased_products_tags ) );
				}

				/** Get Used coupons */
				if ( isset( $contact['used_coupons'] ) ) {
					$contact['used_coupons'] = isset( $contact['used_coupons'] ) ? json_decode( $contact['used_coupons'], true ) : [];
					$contact['used_coupons'] = array_map( function ( $coupon ) {

						return [
							'id'   => $coupon,
							'name' => $coupon,
						];
					}, $contact['used_coupons'] );
				}
				/** Get list and tags */
				if ( isset( $contact['tags'] ) ) {
					$tags            = json_decode( $contact['tags'], true );
					$contact['tags'] = ! empty( $tags ) ? BWFCRM_Tag::get_tags( $tags ) : [];
				}
				if ( isset( $contact['lists'] ) ) {
					$lists            = json_decode( $contact['lists'], true );
					$contact['lists'] = ! empty( $lists ) ? BWFCRM_Lists::get_lists( $lists ) : [];
				}

				if ( bwfcrm_is_woocommerce_active() && isset( $contact['total_order_value'] ) ) {
					$contact['total_order_value'] = ! empty( $contact['total_order_value'] ) ? wc_price( $contact['total_order_value'] ) : '';
				}

				if ( bwfcrm_is_woocommerce_active() && isset( $contact['aov'] ) ) {
					$contact['aov'] = ! empty( $contact['aov'] ) ? wc_price( $contact['aov'] ) : '';
				}


				if ( ! empty( $columns_needs_to_add ) && is_array( $columns_needs_to_add ) ) {
					foreach ( $columns_needs_to_add as $column ) {
						$value = '';
						switch ( true ) {
							case 'has_purchased' === $column :
								$value = ! empty( $contact['purchased_products'] );
								break;
							case 'has_used_any_coupons' === $column :
								$value = ! empty( $contact['used_coupons'] );
								break;
						}

						$contact[ $column ] = $value;
					}
				}

				return apply_filters( 'bwfcrm_modify_contact_columns_preferences_data', $contact );
			}, $contacts );
		}

		public static function get_formatted_date( $date ) {
			if ( empty( $date ) || ! strtotime( $date ) ) {
				return '';
			}
			$format = get_option( 'date_format', 'Y-m-d' );

			return date( $format, strtotime( $date ) );
		}

		/**
		 * Get query for get contacts with columns preference
		 */
		public static function get_column_preferences_query( $join_cf_query, $join_wc_query, $status_filter, $filters, $additional_info ) {
			global $wpdb;
			$contact_table        = $wpdb->prefix . 'bwf_contact';
			$customer_table       = $wpdb->prefix . 'bwf_wc_customers';
			$contact_fields_table = $wpdb->prefix . 'bwf_contact_fields';
			$unsubscribe_table    = $wpdb->prefix . 'bwfan_message_unsubscribe';

			$columns = self::get_columns_for_query();

			$contact_details = isset( $columns['contact_details'] ) && is_array( $columns['contact_details'] ) ? $columns['contact_details'] : [];
			$segments        = isset( $columns['segments'] ) && is_array( $columns['segments'] ) ? $columns['segments'] : [];
			$custom_fields   = isset( $columns['contact_custom_fields'] ) && is_array( $columns['contact_custom_fields'] ) ? $columns['contact_custom_fields'] : [];
			$wc_columns      = isset( $columns['woocommerce'] ) && is_array( $columns['woocommerce'] ) ? $columns['woocommerce'] : [];
			$geography       = isset( $columns['geography'] ) && is_array( $columns['geography'] ) ? $columns['geography'] : [];
			$engagement      = isset( $columns['engagement'] ) && is_array( $columns['engagement'] ) ? $columns['engagement'] : [];

			$total_columns = array_merge( $contact_details, $custom_fields );
			$total_columns = array_merge( $total_columns, $segments );
			$total_columns = array_merge( $total_columns, $wc_columns );
			$total_columns = array_merge( $total_columns, $geography );
			$total_columns = array_merge( $total_columns, $engagement );

			$default_columns = "c.id,c.f_name,c.l_name,c.email,c.contact_no,c.creation_date";
			$join_query      = '';
			/** If Join is already in custom filter query */
			if ( false === strpos( $join_cf_query, 'bwf_contact_fields' ) ) {
				$join_query = " LEFT JOIN $contact_fields_table as `cm` ON c.id = cm.cid ";
			}

			if ( false === strpos( $join_cf_query, 'bwf_wc_customers' ) ) {
				$join_query .= ( ! empty( $wc_columns ) || isset( $filters['wc'] ) ) || ( empty( $wc_columns ) && ! empty( $join_wc_query ) ) ? " LEFT JOIN $customer_table as wc ON c.id = wc.cid" : '';
			}

			$query_columns = apply_filters( 'bwfcrm_contact_columns_preferences', array( 'total_columns' => $total_columns, 'join_query' => $join_query ), $columns, $join_cf_query );
			$join_query    = isset( $query_columns['join_query'] ) ? $query_columns['join_query'] : $join_query;
			$total_columns = isset( $query_columns['total_columns'] ) ? $query_columns['total_columns'] : $total_columns;

			$total_columns = isset( $total_columns ) ? implode( ',', array_filter( $total_columns ) ) : '';
			$total_columns = ! empty( $total_columns ) ? $default_columns . ',' . $total_columns : $default_columns;

			return [
				'base_query'     => "SELECT $total_columns FROM $contact_table AS c $join_query ",
				'count_query'    => "SELECT COUNT(DISTINCT c.id) FROM $contact_table as c $join_query ",
				'needs_to_added' => $columns['need_to_add']
			];
		}

		/**
		 * Get preferences columns for query
		 *
		 */
		public static function get_columns_for_query() {
			$saved_format = BWFAN_Common::get_contact_columns();

			if ( empty( $saved_format ) ) {
				return [ 'need_to_add' => [] ];
			}

			$columns = [];

			$modified_columns    = [
				'has_purchased'        => 'purchased_products',
				'has_used_any_coupons' => 'used_coupons',
			];
			$system_fields       = [ 'address-1', 'address-2', 'city', 'postcode', 'company', 'gender', 'dob', 'link-trigger-click', 'last-login', 'last-open', 'last-click', 'last-sent' ];
			$need_to_add_columns = [];
			$added_columns       = [];

			foreach ( $saved_format as $fields ) {
				foreach ( $fields as $slug => $field ) {
					if ( 'label' === $slug || 'groupKey' === $slug || 'groupLabel' === $slug ) {
						continue;
					}

					/** If system and default field is not exists in DB */
					if ( in_array( $slug, $system_fields, true ) && empty( BWFCRM_Fields::get_field_id_by_slug( $slug ) ) ) {
						continue;
					}

					/** Get need to add and modify columns */
					if ( in_array( $slug, array_keys( $modified_columns ), true ) ) {
						$need_to_add_columns[] = $slug;
					}

					/** Default columns so no need to add */
					if ( in_array( $slug, [ 'f_name', 'l_name', 'email', 'creation_date', 'contact_no' ], true ) ) {
						continue;
					}

					switch ( true ) {
						case 'contact_details' === $fields['groupKey'] || 'segments' === $fields['groupKey']:
							if ( 'creation_days' === $slug ) {
								$slug = " DATEDIFF(now(), c.creation_date) as `creation_days` ";
								break;
							}
							if ( 'company' === $slug || 'dob' === $slug || 'gender' === $slug ) {
								$slug = "cm.f" . BWFCRM_Fields::get_field_id_by_slug( $slug ) . " AS `$slug`";
								break;
							}
							$slug = "c." . $slug;
							break;
						case 'geography' === $fields['groupKey'] :
							if ( 'state' !== $slug && 'country' !== $slug ) {
								$slug = "cm.f" . BWFCRM_Fields::get_field_id_by_slug( $slug ) . " AS `$slug`";
							} else {
								$slug = "c." . $slug;
							}
							break;
						case 'engagement' === $fields['groupKey'] :
							if ( 'links' === $slug ) {
								$slug = 'link-trigger-click';
							}
							$slug = "cm.f" . BWFCRM_Fields::get_field_id_by_slug( $slug ) . " AS `$slug`";
							break;
						case 'woocommerce' === $fields['groupKey'] :
							if ( 'l_order_days' === $slug ) {
								$slug = ! in_array( $slug, $added_columns ) ? " DATEDIFF(now(), wc.l_order_date) as `l_order_days` " : '';
								break;
							}
							if ( 'f_order_days' === $slug ) {
								$slug = ! in_array( $slug, $added_columns ) ? " DATEDIFF(now(), wc.f_order_date) as `f_order_days` " : '';
								break;
							}
							if ( isset( $modified_columns[ $slug ] ) ) {
								$slug = $modified_columns[ $slug ];
							}
							$slug = ! in_array( $slug, $added_columns ) ? "wc." . $slug : '';
							break;
						case 'contact_custom_fields' === $fields['groupKey'] :
							$custom_field_slug = str_replace( 'bwf_cf', '', $slug );
							/** if column is not exists in bwf_contact_fields table */
							if ( empty( BWF_Model_Contact_Fields::column_already_exists( $custom_field_slug ) ) ) {
								$slug = '';
								break;
							}

							$slug = "cm.f" . $custom_field_slug . " AS `$slug`";
							break;
					}

					if ( empty( $slug ) ) {
						continue;
					}
					$added_columns[]                  = $slug;
					$columns[ $fields['groupKey'] ][] = apply_filters( 'bwfcrm_contact_columns_group', $slug, $fields );
				}
			}

			$columns['need_to_add'] = $need_to_add_columns;

			return $columns;
		}

		/**
		 * Check contact email in unsubscribe table
		 *
		 * @param $email
		 * @param $contact_no
		 *
		 * @return array|false|object|string|null
		 */
		public static function is_contact_unsubscribed( $email, $contact_no ) {
			if ( empty( $email ) && empty( $contact_no ) ) {
				return false;
			}

			$data = [
				'recipient' => [ $email, $contact_no ],
			];

			return BWFAN_Model_Message_Unsubscribe::get_message_unsubscribe_row( $data );
		}
	}
}