<?php
/**
 * CRM campaign model call.
 *
 * @package Model
 */

/**
 * Campaign Class
 */
class BWFAN_Model_Broadcast extends BWFAN_Model {

	/**
	 * Campaign table fields
	 *
	 * @var array
	 */
	protected static $table_fields = array(
		'id',
		'title',
		'description',
		'type',
		'status',
		'count',
		'processed',
		'created_by',
		'offset',
		'modified_by',
		'execution_time',
		'created_at',
		'data',
	);

	/**
	 * Table order option
	 *
	 * @var array
	 */
	protected static $order_option = array(
		'ASC',
		'DESC',
	);

	/**
	 * Get all campaigns
	 *
	 * @param string $search search string.
	 * @param int $limit data limit.
	 * @param int $offset data offset.
	 * @param string $order data order.
	 * @param string $type broadcast type.
	 * @param string $status broadcast status.
	 *
	 * @return array
	 */
	public static function get_campaigns( $search, $limit, $offset, $order, $type = '', $status = 'all' ) {
		$broadcast_table = self::_table();

		/** data not pulled as for large data API calls sometimes fail */
		$broadcast_query = "SELECT `id`, `title`, `type`, `execution_time`, `status`, `created_at`, `count` FROM $broadcast_table WHERE 1=1 AND `parent` = 0";
		$count_filter    = array(
			'parent' => array(
				'type'     => '%d',
				'value'    => 0,
				'operator' => 'LIKE',
			)
		);
		if ( ! empty( $search ) ) {
			$broadcast_query .= " AND title LIKE '%" . esc_sql( $search ) . "%'";
			$count_filter    = array(
				'title' => array(
					'type'     => '%s',
					'value'    => "%$search%",
					'operator' => 'LIKE',
				),
			);
		}

		if ( ! empty( $type ) ) {
			$broadcast_query      .= " AND type= $type";
			$count_filter['type'] = array(
				'type'     => '%d',
				'value'    => $type,
				'operator' => 'LIKE',
			);
		}

		if ( $status !== 'all' ) {
			$broadcast_status = '';
			switch ( $status ) {
				case 'scheduled':
					$broadcast_status = [ 2 ];
					break;
				case 'ongoing':
					$broadcast_status = [ 3 ];
					break;
				case 'completed':
					$broadcast_status = [ 4 ];
					break;
				case 'paused':
					$broadcast_status = [ 5, 6 ];
					break;
				case 'cancelled':
					$broadcast_status = [ 7 ];
					break;
			}

			if ( ! empty( $broadcast_status ) ) {
				$count_filter['status'] = array(
					'type'     => '%d',
					'value'    => $broadcast_status,
					'operator' => 'IN',
				);

				$broadcast_status = '(' . implode( ',', $broadcast_status ) . ')';
				$broadcast_query  .= " AND status IN $broadcast_status";
			}
		}

		$broadcast_query .= " ORDER BY id $order LIMIT $offset,$limit";

		$broadcasts = self::get_results( $broadcast_query );
		if ( empty( $search ) && ! empty( $broadcasts ) ) {
			$broadcasts_ids = array_map( function ( $broadcast ) {
				return $broadcast['id'];
			}, $broadcasts );

			$broadcasts = self::include_child_broadcast( $broadcasts_ids, $broadcasts );
		}
		$total = self::count( $count_filter );

		$message = __( 'Got all broadcasts', 'wp-marketing-automations-pro' );
		if ( empty( $broadcasts ) ) {
			$message = __( 'No Broadcast found', 'wp-marketing-automations-pro' );
		}

		return array(
			'data'    => array(
				'campaigns' => self::get_formatted_campaign_data( $broadcasts ),
			),
			'message' => $message,
			'total'   => $total,
		);
	}

	static function count( $data = array() ) {
		global $wpdb;

		$sql        = 'SELECT COUNT(*) AS `count` FROM ' . self::_table() . ' WHERE 1=1';
		$sql_params = [];
		if ( is_array( $data ) && count( $data ) > 0 ) {
			foreach ( $data as $key => $val ) {
				if ( 'IN' === $val['operator'] ) {
					$placeholder = array_fill( 0, count( $val['value'] ), $val['type'] );
					$placeholder = implode( ", ", $placeholder );

					$sql .= " AND `{$key}` {$val['operator']} ({$placeholder})";

					$sql_params = array_merge( $sql_params, $val['value'] );
				} else {
					$sql          .= " AND `{$key}` {$val['operator']} {$val['type']}";
					$sql_params[] = $val['value'];
				}
			}

			if ( ! empty( $sql_params ) ) {
				$sql = $wpdb->prepare( $sql, $sql_params ); // WPCS: unprepared SQL OK
			}
		}

		return $wpdb->get_var( $sql ); // WPCS: unprepared SQL OK
	}

	public static function include_open_click_rate( $broadcasts_ids, $broadcasts, $only_engagement_data = false ) {
		global $wpdb;
		$engagement_table  = $wpdb->prefix . 'bwfan_engagement_tracking ';
		$engagement_fields = 'con.`oid`, (SUM(IF(con.`open`>0, 1, 0))/COUNT(con.`ID`)) * 100 AS `open_rate`, SUM(IF(con.`c_status`=2, 1, 0)) AS `sent`, (SUM(IF(con.`click`>0, 1, 0))/COUNT(con.`ID`)) * 100 AS `click_rate`';

		$broadcasts_ids = implode( ',', $broadcasts_ids );

		$engagement_query = "SELECT $engagement_fields FROM $engagement_table AS con WHERE con.`oid` IN ($broadcasts_ids) AND con.`type` = 2 GROUP BY con.`oid`";
		$engagements      = self::get_results( $engagement_query );

		if ( true === $only_engagement_data ) {
			return $engagements;
		}

		foreach ( $engagements as $engagement ) {
			$broadcasts = array_map( function ( $broadcast ) use ( $engagement ) {
				if ( $broadcast['id'] === $engagement['oid'] ) {
					unset( $engagement['oid'] );

					return array_merge( $broadcast, $engagement );
				}

				return $broadcast;
			}, $broadcasts );
		}

		return $broadcasts;
	}

	public static function include_child_broadcast( $broadcasts_ids, $broadcasts ) {
		$child_broadcasts = self::get_broadcast_child( $broadcasts_ids );

		if ( empty( $child_broadcasts ) ) {
			return $broadcasts;
		}

		$child = array();
		foreach ( $child_broadcasts as $child_broadcast ) {
			$child[ absint( $child_broadcast['parent'] ) ][] = $child_broadcast;
		}
		$broadcasts = array_map( function ( $broadcast ) use ( $child ) {
			$broadcast_id = absint( $broadcast['id'] );
			if ( isset( $child[ $broadcast_id ] ) ) {
				$child_broadcasts      = array_map( function ( $child_broadcast ) {
					/** Get grand child */
					$grand_child = self::get_broadcast_child( $child_broadcast['id'] );

					if ( ! empty( $grand_child ) ) {
						$child_broadcast['children'] = $grand_child;
					}

					return $child_broadcast;

				}, $child[ $broadcast_id ] );
				$broadcast['children'] = $child_broadcasts;
			}

			return $broadcast;
		}, $broadcasts );

		return $broadcasts;
	}

	public static function get_broadcast_child( $broadcast_ids, $return_ids = false ) {
		$broadcast_table = self::_table();
		$broadcast_query = "SELECT `id`, `title`, `type`, `data`, `created_at`, `execution_time`, `status`, `count`, `parent` FROM $broadcast_table WHERE 1=1";
		if ( is_array( $broadcast_ids ) ) {
			$broadcast_ids   = implode( ',', $broadcast_ids );
			$broadcast_query .= " AND `parent` in ($broadcast_ids)";
		} elseif ( is_numeric( $broadcast_ids ) ) {
			$broadcast_query .= " AND `parent` = $broadcast_ids";
		}
		$broadcast_query  .= ' ORDER BY `id`';
		$child_broadcasts = self::get_results( $broadcast_query );
		$broadcasts_ids   = array_map( function ( $child_broadcast ) {
			return $child_broadcast['id'];
		}, $child_broadcasts );
		if ( empty( $broadcasts_ids ) ) {
			return array();
		}

		if ( true === $return_ids ) {
			return $broadcasts_ids;
		}

		$child_broadcasts = self::include_conversion_into_campaigns( $child_broadcasts );

		return self::get_formatted_campaign_data( $child_broadcasts );
	}

	public static function include_conversion_into_campaigns( $campaigns, $only_conversion_data = false ) {
		global $wpdb;
		if ( ! is_array( $campaigns ) || 0 === count( $campaigns ) ) {
			return $campaigns;
		}

		$ids = array_map( function ( $campaign ) {
			return isset( $campaign['id'] ) ? absint( $campaign['id'] ) : false;
		}, $campaigns );

		$ids = implode( ',', array_filter( $ids ) );

		$query       = "SELECT `oid`, count(`ID`) AS `conversions`, SUM(`wctotal`) AS `revenue` FROM {$wpdb->prefix}bwfan_conversions WHERE `oid` IN ($ids) AND `otype` = 2 GROUP BY `oid`";
		$conversions = $wpdb->get_results( $query, ARRAY_A );
		if ( true === $only_conversion_data ) {
			return $conversions;
		}
		$campaigns = array_map( function ( $campaign ) use ( $conversions ) {
			if ( ! isset( $campaign['id'] ) ) {
				return false;
			}

			foreach ( $conversions as $conversion ) {
				if ( absint( $campaign['id'] ) === absint( $conversion['oid'] ) ) {
					return array_replace( $campaign, $conversion );
				}
			}

			return $campaign;
		}, $campaigns );

		return array_filter( $campaigns );
	}

	/**
	 * Get campaign details
	 *
	 * @param $campaign_id
	 * @param false $steps
	 * @param false $ab_mode
	 *
	 * @return array
	 */
	public static function get_campaign( $campaign_id, $steps = false, $ab_mode = false ) {
		$response = array(
			'data'    => array(),
			'message' => __( 'No broadcast found for the given ID.', 'wp-marketing-automations-pro' ),
			'status'  => 404,
		);
		global $wpdb;

		$campaign = self::get_specific_rows( 'id', $campaign_id );
		if ( ! is_array( $campaign ) || ! isset( $campaign[0] ) || ! isset( $campaign[0]['id'] ) || absint( $campaign[0]['id'] ) !== absint( $campaign_id ) ) {
			return $response;
		}

		/** Check if status is draft or scheduled */
		if ( in_array( absint( $campaign[0]['status'] ), array( BWFCRM_Campaigns::$CAMPAIGN_DRAFT, BWFCRM_Campaigns::$CAMPAIGN_SCHEDULED ) ) ) {
			$data                = self::get_formatted_campaign_data( $campaign )[0];
			$response['data']    = $data;
			$response['status']  = 200;
			$response['message'] = __( 'Broadcast loaded', 'wp-marketing-automations-pro' );

			return $response;
		}

		/** Get Broadcast */
		$broadcast = array();
		if ( ! $ab_mode ) {
			$broadcast_table = self::_table();
			$query           = "SELECT * from {$broadcast_table} WHERE `id` = '$campaign_id' LIMIT 0, 1";
			$broadcast       = $wpdb->get_row( $query, ARRAY_A );
		}

		/** Check if the broadcast is grandchild */
		if ( isset( $broadcast['parent'] ) && ! empty( absint( $broadcast['parent'] ) ) ) {
			$parent = absint( $broadcast['parent'] );
			$parent = self::get( $parent );
			if ( is_array( $parent ) && isset( $parent['parent'] ) && ! empty( absint( $parent['parent'] ) ) ) {
				$broadcast['is_unopen_grandchild'] = true;
			}
		}

		/** Get broadcast's engagement Data grouped by TID (Template ID) */
		$engagement_table = $wpdb->prefix . 'bwfan_engagement_tracking';
		$columns          = 'SUM(`open`) AS `open_count`, (SUM(IF(`open`>0, 1, 0))/COUNT(`ID`)) * 100 AS `open_rate`, SUM(`click`) AS `click_count`, (SUM(IF(`click`>0, 1, 0))/COUNT(`ID`)) * 100 AS `click_rate`, SUM(IF(`c_status`=2, 1, 0)) AS `sent`, `tid`';
		$group_sql        = $ab_mode ? 'GROUP BY `tid`' : '';
		$query            = "SELECT $columns FROM {$engagement_table} WHERE `oid` = '$campaign_id' AND `type` = 2 $group_sql";
		$engagements      = $wpdb->get_results( $query, ARRAY_A );

		/** Get Conversions Grouped by TID */
		$conversions = array();
		if ( ! empty( $engagements ) ) {
			$campaign = array_map( function ( $data ) {
				$data['data'] = json_decode( $data['data'], true );

				return $data;
			}, $campaign );

			$template_ids = [];
			if ( isset( $campaign[0]['data']['content'] ) ) {
				foreach ( $campaign[0]['data']['content'] as $content ) {
					if ( isset( $content['template_id'] ) ) {
						$template_ids[] = $content['template_id'];
					}
				}
			}
			$conversions = self::get_conversions_by_tids( $campaign_id, $ab_mode, $template_ids );
		}

		/** Combine Engagements and Conversion data by TID */
		$final_broadcast = array();
		$conversion_data = $conversions;
		if ( ! empty( $engagements ) ) {
			foreach ( $engagements as $engagement ) {
				$tid             = absint( $engagement['tid'] );
				$broadcast['id'] = $campaign_id;
				$engagement      = array_replace( $engagement, $broadcast );
				if ( isset( $conversions[ $tid ] ) && is_array( $conversions[ $tid ] ) ) {
					$conversion_data = $conversions[ $tid ];
				}
				$engagement        = array_replace( $engagement, $conversion_data );
				$final_broadcast[] = $engagement;
			}
		}

		if ( empty( $final_broadcast ) ) {
			$final_broadcast[] = $broadcast;
		}

		$campaign = $final_broadcast;
		if ( is_array( $campaign ) && count( $campaign ) > 0 && isset( $campaign[0]['id'] ) && ! empty( $campaign[0]['id'] ) ) {
			$campaign = self::get_formatted_campaign_data( $campaign );
			$campaign = $ab_mode ? $campaign : $campaign[0];
		} else {
			$campaign = false;
		}

		if ( ! empty( $campaign ) ) {
			$response['data']    = $campaign;
			$response['status']  = 200;
			$response['message'] = __( 'Broadcast loaded for ID: ', 'wp-marketing-automations-pro' ) . $campaign_id;
		}
		if ( $steps ) {
			$response['data']['step'] = 1;
		}

		return $response;
	}

	/**
	 * Get conversions group by template id of broadcast
	 *
	 * @param $broadcast_id
	 * @param $ab_mode
	 * @param $tids
	 *
	 * @return array|int[]
	 */
	public static function get_conversions_by_tids( $broadcast_id, $ab_mode = true, $tids = [] ) {
		if ( empty( $tids ) ) {
			return [ 'conversions' => 0, 'revenue' => 0 ];
		}
		global $wpdb;

		/** Now from grouped Engagement IDs, Get Conversions, and arrange them by TIDs */
		$conversion_data  = array();
		$conversion_count = 0;
		$revenue          = 0;
		foreach ( $tids as $tid ) {
			$conversion_table = $wpdb->prefix . 'bwfan_conversions';
			$engagement_table = $wpdb->prefix . 'bwfan_engagement_tracking';
			$columns          = 'COUNT(con.ID) AS conversions, SUM(con.wctotal) AS `revenue` ';
			$query            = $wpdb->prepare( "SELECT $columns FROM {$engagement_table} AS `et` JOIN {$conversion_table} AS `con` ON et.`ID` = con.`trackid` WHERE et.`oid` = %d AND et.`type` = %d AND et.`tid` = %d", $broadcast_id, 2, $tid );
			$conversions      = $wpdb->get_row( $query, ARRAY_A );
			if ( false === $ab_mode ) {
				$conversion_count += isset( $conversions['conversions'] ) ? absint( $conversions['conversions'] ) : 0;
				$revenue          += isset( $conversions['revenue'] ) ? floatval( $conversions['revenue'] ) : 0;
				continue;
			}
			$conversion_data[ absint( $tid ) ] = $conversions;
		}

		if ( false === $ab_mode ) {
			return [
				'conversions' => $conversion_count,
				'revenue'     => $revenue
			];
		}

		return $conversion_data;
	}

	/**
	 * Get un-subscribers count of a broadcast campaign
	 *
	 * @param $campaign_id
	 *
	 * @return int
	 */
	public static function get_campaign_unsubscribers( $campaign_id ) {
		global $wpdb;
		$query = "SELECT COUNT(*) FROM {$wpdb->prefix}bwfan_message_unsubscribe WHERE `automation_id` = $campaign_id AND `c_type` = 2";

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

	/**
	 * Create a new campaign
	 *
	 * @param string $title campaign title.
	 * @param string $desc campaign description.
	 * @param int $type campaign type.
	 * @param int $created_by created user id.
	 *
	 * @return array
	 */
	public static function create_campaign( $title, $desc, $type, $created_by, $createUnopen = false ) {
		$status  = 404;
		$data    = array();
		$message = __( 'Unable to create broadcast.', 'wp-marketing-automations-pro' );

		$db_arr = array(
			'title'         => $title,
			'description'   => $desc,
			'created_by'    => $created_by,
			'type'          => $type,
			'modified_by'   => $created_by,
			'created_at'    => current_time( 'mysql', 1 ),
			'last_modified' => current_time( 'mysql', 1 ),
		);

		/** Create as Unopen Broadcast */
		if ( ! empty( $createUnopen ) ) {
			/** Set Parent ID of broadcast */
			$parent           = absint( $createUnopen );
			$db_arr['parent'] = $parent;

			$parent = self::get( $parent );
			if ( ! empty( $parent['data'] ) ) {
				$parent_data = json_decode( $parent['data'], true );

				/** Remove Unnecessary Parent data */
				if ( is_array( $parent_data ) ) {
					unset( $parent_data['exclude'] );
					unset( $parent_data['filters'] );
					unset( $parent_data['is_promotional'] );
					unset( $parent_data['halt_reason'] );

					unset( $parent_data['winner'] );
					unset( $parent_data['hold_broadcast'] );
					unset( $parent_data['smart_sending_done'] );
				}

				$db_arr['data'] = wp_json_encode( $parent_data );
			}
		}

		self::insert( $db_arr );
		$campaign_id = self::insert_id();

		if ( $campaign_id ) {
			$campaign = self::get_specific_rows( 'id', $campaign_id );
			$data     = self::get_formatted_campaign_data( $campaign )[0];
			$status   = 200;
			$message  = __( 'Broadcast created', 'wp-marketing-automations-pro' );
		}

		return array(
			'data'    => $data,
			'message' => $message,
			'status'  => $status,
		);
	}

	/**
	 * Delete a particular campaign
	 *
	 * @param int $campaign_id campaign Id.
	 *
	 * @return  array
	 */
	public static function delete_campaign( $campaign_id ) {
		$status  = 404;
		$data    = array();
		$message = __( 'Unable to delete the broadcast.', 'wp-marketing-automations-pro' );

		$campaign = self::delete( $campaign_id );

		if ( (bool) $campaign ) {
			$data    = array();
			$status  = 200;
			$message = __( 'Broadcast deleted', 'wp-marketing-automations-pro' );
			self::remove_parent_from_child( [ $campaign_id ] );
		}

		return array(
			'data'    => $data,
			'message' => $message,
			'status'  => $status,
		);
	}

	/**
	 * Update campaign
	 *
	 * @param int $campaign_id campaign id.
	 * @param int $step step id.
	 * @param array $data campaign data to update.
	 *
	 * @return array
	 */
	public static function update_campaign( $campaign_id, $step, $data ) {
		$status          = 404;
		$res_data        = array();
		$message         = __( 'Unable to update the broadcast.', 'wp-marketing-automations-pro' );
		$campaign        = self::get_specific_rows( 'id', $campaign_id );
		$current_user_id = get_current_user_id();

		/** checking if user not exists than return in broadcast update */
		if ( empty( $current_user_id ) ) {
			$message = __( 'Permission denied, no valid user available', 'wp-marketing-automations-pro' );

			return array(
				'data'    => $res_data,
				'status'  => $status,
				'message' => $message,
			);
		}

		if ( empty( $campaign ) ) {
			return array(
				'data'    => $res_data,
				'status'  => $status,
				'message' => $message,
			);
		}

		$campaign_data = isset( $campaign[0]['data'] ) && ! empty( $campaign[0]['data'] ) ? json_decode( $campaign[0]['data'], true ) : array();
		$error         = false;
		if ( 1 == $step ) {
			if ( empty( $data['title'] ) ) {
				$error   = true;
				$message = __( 'Broadcast name is required.', 'wp-marketing-automations-pro' );
			}
			if ( ! $error && ( intval( $data['type'] ) <= 0 || ! in_array( intval( $data['type'] ), array( 1, 2, 3 ), true ) ) ) {
				$error   = true;
				$message = __( 'Broadcast type is required.', 'wp-marketing-automations-pro' );
			}
			if ( ! $error ) {
				$campaign_data['is_promotional'] = $data['promotional'];
				$campaign_data['ab_type']        = $data['ab_type'];
				$campaign_data['exclude']        = $data['exclude'];
				$campaign_data['includeSoftBounce']        = isset( $data['includeSoftBounce'] ) ? $data['includeSoftBounce'] : false;
				$campaign_data['includeUnverified']        = isset( $data['includeUnverified'] ) ? $data['includeUnverified'] : false;
				self::update( array(
					'title'         => $data['title'],
					'type'          => $data['type'],
					'description'   => $data['description'],
					'modified_by'   => $current_user_id,
					'data'          => wp_json_encode( $campaign_data ),
					'last_modified' => current_time( 'mysql', 1 ),
				), array(
					'id' => $campaign_id,
				) );
			}
		}

		if ( 2 == $step ) {
			$campaign_data['filters'] = $data['filters'];
			$campaign_data['exclude'] = $data['exclude'];
			$total_count              = isset( $data['count'] ) ? $data['count'] : 0;

			self::update( array(
				'data'          => wp_json_encode( $campaign_data ),
				'count'         => $total_count,
				'modified_by'   => $current_user_id,
				'last_modified' => current_time( 'mysql', 1 ),
			), array(
				'id' => $campaign_id,
			) );

		}

		if ( 3 == $step ) {
			$campaign_data['content'] = isset( $data['content'] ) ? $data['content'] : array();

			/** Checking utm is enabled or not */
			$campaign_data['content'] = array_map( function ( $content ) {
				/** if utm is disabled then unset utm parameters */
				if ( empty( $content['utmEnabled'] ) ) {
					unset( $content['utm'] );
				}

				return $content;
			}, $campaign_data['content'] );

			$campaign_data['senders_name']  = isset( $data['senders_name'] ) ? $data['senders_name'] : '';
			$campaign_data['senders_email'] = isset( $data['senders_email'] ) ? $data['senders_email'] : '';
			$campaign_data['replyto']       = isset( $data['replyto'] ) ? $data['replyto'] : '';
			$campaign_data['smart_send']    = isset( $data['smart_send'] ) ? $data['smart_send'] : '';

			if ( ! $error ) {
				self::update( array(
					'data'          => wp_json_encode( $campaign_data ),
					'modified_by'   => $current_user_id,
					'last_modified' => current_time( 'mysql', 1 ),
				), array(
					'id' => $campaign_id,
				) );
			}
		}

		if ( 4 == $step ) {
			$schedule_time = isset( $data['schedule_time'] ) ? $data['schedule_time'] : '';

			try {
				$schedule_date = new DateTime( $schedule_time );
				self::update( array(
					'execution_time' => $schedule_date->format( 'Y-m-d H:i:s' ),
					'status'         => 2,
					'data'           => wp_json_encode( $campaign_data ),
					'modified_by'    => $current_user_id,
					'last_modified'  => date( 'Y-m-d H:i:s', time() - 6 ),
				), array(
					'id' => $campaign_id,
				) );

				/** Start the campaign if scheduled time is less than or equal to current time (i.e.: on Broadcast Now) */
				if ( strtotime( $schedule_time ) <= time() ) {
					$campaign_data = self::get( absint( $campaign_id ) );
					if ( ! is_array( $campaign_data ) || empty( $campaign_data ) ) {
						$error   = true;
						$message = __( 'Broadcast not found', 'wp-marketing-automations-pro' );
					} else {
						BWFCRM_Core()->campaigns->process_single_scheduled_broadcasts( $campaign_data );
						BWFCRM_Common::reschedule_broadcast_action();
					}
				} else {
					BWFAN_PRO_Common::validate_scheduled_recurring_actions();
				}
			} catch ( Exception $e ) {
				$error   = true;
				$message = $e->getMessage();
			}

		}

		if ( ! $error ) {
			$res_data = self::get_formatted_campaign_data( self::get_specific_rows( 'id', $campaign_id ) )[0];
			$status   = 200;
			$message  = __( 'Broadcast updated', 'wp-marketing-automations-pro' );
		}

		return array(
			'data'    => $res_data,
			'status'  => $status,
			'message' => $message,
		);
	}

	/**
	 * Clone campaign
	 *
	 * @param int $campaign_id campaign id.
	 * @param int $created_by user id.
	 *
	 * @return array
	 */
	public static function clone_campaign( $campaign_id, $created_by, $send_to_unopen = false ) {
		$status   = 404;
		$data     = array();
		$message  = __( 'Broadcast details missing.', 'wp-marketing-automations-pro' );
		$campaign = self::get_specific_rows( 'id', $campaign_id );

		if ( ! empty( $campaign ) ) {
			$campaign      = $campaign[0];
			$clone_title   = true === $send_to_unopen ? $campaign['title'] . ' ' . __( '(Unopen Broadcast)', 'wp-marketing-automations-pro' ) : $campaign['title'] . ' ' . __( '(Copy)', 'wp-marketing-automations-pro' );
			$campaign_data = ! empty( $campaign['data'] ) ? json_decode( $campaign['data'], true ) : array();
			if ( is_array( $campaign_data ) ) {
				unset( $campaign_data['halt_reason'] );
				unset( $campaign_data['winner'] );
				unset( $campaign_data['hold_broadcast'] );
				unset( $campaign_data['smart_sending_done'] );
				unset( $campaign_data['includeSoftBounce'] );
				unset( $campaign_data['includeUnverified'] );
				if ( isset( $campaign_data['content'] ) ) {
					$content = $campaign_data['content'];
					foreach ( $content as $key => $value ) {
						if ( isset( $value['utm'] ) && isset( $value['utm']['name'] ) ) {
							$campaign_data['content'][ intval( $key ) ]['utm']['name'] = $clone_title;
						}
					}
				}
			}

			if ( true === $send_to_unopen ) {
				$campaign_data['parent'] = absint( $campaign_id );
			}

			$db_arr = array(
				'title'         => $clone_title,
				'description'   => $campaign['description'],
				'created_by'    => $created_by,
				'type'          => $campaign['type'],
				'parent'        => $campaign['parent'],
				'count'         => 0,
				'modified_by'   => $created_by,
				'data'          => wp_json_encode( $campaign_data ),
				'created_at'    => current_time( 'mysql', 1 ),
				'last_modified' => current_time( 'mysql', 1 ),
			);
			self::insert( $db_arr );
			$campaign_id = self::insert_id();

			if ( $campaign_id ) {
				$campaign = self::get_specific_rows( 'id', $campaign_id );
				$data     = self::get_formatted_campaign_data( $campaign )[0];
				$status   = 200;
				$message  = __( 'Broadcast cloned', 'wp-marketing-automations-pro' );
				if ( true === $send_to_unopen ) {
					$message = __( 'Broadcast created', 'wp-marketing-automations-pro' );
				}
			}
		}

		return array(
			'data'    => $data,
			'status'  => $status,
			'message' => $message,
		);
	}

	/**
	 * Returns formatted data
	 *
	 * @param array $data campaign data.
	 *
	 * @return array
	 */
	public static function get_formatted_campaign_data( $data ) {
		if ( empty( $data ) ) {
			return [];
		}
		$final_data = array();
		foreach ( $data as $campaign ) {
			if ( isset( $campaign['data'] ) && ! empty( $campaign['data'] ) ) {
				$campaign['data'] = json_decode( $campaign['data'], true );
			}

			/** Setting default values 0 if not found */
			$campaign['click_count'] = ( isset( $campaign['click_count'] ) && absint( $campaign['click_count'] ) > 0 ) ? $campaign['click_count'] : 0;
			$campaign['click_rate']  = ( isset( $campaign['click_rate'] ) && floatval( $campaign['click_rate'] ) > 0 ) ? number_format( $campaign['click_rate'], 2 ) : 0;
			$campaign['open_count']  = ( isset( $campaign['open_count'] ) && absint( $campaign['open_count'] ) > 0 ) ? $campaign['open_count'] : 0;
			$campaign['open_rate']   = ( isset( $campaign['open_rate'] ) && floatval( $campaign['open_rate'] ) > 0 ) ? number_format( $campaign['open_rate'], 2 ) : 0;
			$campaign['revenue']     = ( isset( $campaign['revenue'] ) && floatval( $campaign['revenue'] ) > 0 ) ? $campaign['revenue'] : 0;
			$campaign['conversions'] = ( isset( $campaign['conversions'] ) && absint( $campaign['conversions'] ) > 0 ) ? $campaign['conversions'] : 0;
			$campaign['sent']        = ( isset( $campaign['sent'] ) && absint( $campaign['sent'] ) > 0 ) ? $campaign['sent'] : 0;
			$campaign['subject']     = isset( $campaign['tid'] ) && ! empty( $campaign['tid'] ) ? self::get_template_subject( $campaign['tid'] ) : '';
			$campaign['parent']      = isset( $campaign['parent'] ) && absint( $campaign['parent'] ) > 0 ? absint( $campaign['parent'] ) : 0;
			$campaign['unsubs']      = isset( $campaign['id'] ) ? self::get_campaign_unsubscribers( absint( $campaign['id'] ) ) : 0;
			$campaign['created_at']  = isset( $campaign['created_at'] ) ? get_date_from_gmt( $campaign['created_at'] ) : '';
			$final_data[]            = $campaign;
		}

		return $final_data;
	}

	public static function get_template_subject( $tid ) {
		$template = BWFAN_Model_Templates::get( absint( $tid ) );
		if ( empty( $template ) || ! is_array( $template ) || ! isset( $template['subject'] ) ) {
			return '';
		}

		return $template['subject'];
	}

	public static function get_broadcasts_by_status( $status = 1, $check_execution_time = false ) {
		global $wpdb;
		$table_name = self::_table();
		$query      = "SELECT * FROM $table_name WHERE `status` = %s";
		$query_args = array( $status );
		if ( $check_execution_time ) {
			$current_time = current_time( 'mysql', 1 );
			$query        .= ' AND `execution_time` <= %s';
			$query_args[] = $current_time;
		}

		/** Fetch running broadcasts order by execution time ascending */
		if ( BWFCRM_Broadcast_Processing::$CAMPAIGN_RUNNING === intval( $status ) ) {
			$query .= " ORDER BY `execution_time` ASC";
		}

		$query      = $wpdb->prepare( $query, $query_args );
		$broadcasts = $wpdb->get_results( $query, ARRAY_A );

		return self::get_formatted_campaign_data( $broadcasts );
	}

	public static function get_scheduled_broadcasts_to_run() {
		global $wpdb;
		$table_name   = self::_table();
		$status       = BWFCRM_Campaigns::$CAMPAIGN_SCHEDULED;
		$current_time = current_time( 'mysql', 1 );

		$query     = $wpdb->prepare( "SELECT * FROM $table_name WHERE `status` = %s AND `execution_time` <= %s ORDER BY `execution_time` ASC", $status, $current_time );
		$campaigns = $wpdb->get_results( $query, ARRAY_A );

		return self::get_formatted_campaign_data( $campaigns );
	}

	public static function update_status_to_run( $campaign_id, $offset, $campaign_data, $contact_count ) {
		self::update( array(
			'offset'        => $offset,
			'data'          => json_encode( $campaign_data ),
			'count'         => absint( $contact_count ),
			'status'        => BWFCRM_Campaigns::$CAMPAIGN_RUNNING,
			'last_modified' => date( 'Y-m-d H:i:s', time() - 6 ),
		), array(
			'id' => absint( $campaign_id ),
		) );
	}

	public static function update_status_to_halt( $campaign_id, $campaign_data ) {
		self::update( array(
			'data'          => json_encode( $campaign_data ),
			'status'        => BWFCRM_Campaigns::$CAMPAIGN_HALT,
			'last_modified' => current_time( 'mysql', 1 ),
		), array(
			'id' => absint( $campaign_id ),
		) );
	}

	public static function update_status_to_complete( $campaign_id, $campaign_data, $processed, $count ) {
		self::update( array(
			'data'          => json_encode( $campaign_data ),
			'status'        => BWFCRM_Campaigns::$CAMPAIGN_COMPLETED,
			'count'         => absint( $processed ) > absint( $count ) ? $processed : $count,
			'last_modified' => current_time( 'mysql', 1 ),
		), array(
			'id' => absint( $campaign_id ),
		) );
	}

	public static function update_campaign_offsets( $campaign_id, $offset, $processed ) {
		self::update( array(
			'offset'        => absint( $offset ),
			'processed'     => absint( $processed ),
			'last_modified' => current_time( 'mysql', 1 ),
		), array(
			'id' => absint( $campaign_id ),
		) );
	}

	public static function update_status_to_pause( $campaign_id ) {
		$rows = self::update( array(
			'status' => BWFCRM_Campaigns::$CAMPAIGN_PAUSED,
		), array(
			'id'     => absint( $campaign_id ),
			'status' => BWFCRM_Campaigns::$CAMPAIGN_RUNNING,
		) );

		return ! empty( $rows );
	}

	public static function update_status_to_ongoing( $campaign_id ) {
		$rows = self::update( array(
			'status' => BWFCRM_Campaigns::$CAMPAIGN_RUNNING,
		), array(
			'id'     => absint( $campaign_id ),
			'status' => BWFCRM_Campaigns::$CAMPAIGN_PAUSED,
		) );

		return ! empty( $rows );
	}

	public static function update_status_to_draft( $campaign_id ) {
		$rows = self::update( array(
			'status'         => BWFCRM_Campaigns::$CAMPAIGN_DRAFT,
			'execution_time' => null,
		), array(
			'id' => absint( $campaign_id ),
		) );

		return ! empty( $rows );
	}

	public static function update_status_to_cancel( $campaign_id ) {
		$rows = self::update( array(
			'status'         => BWFCRM_Campaigns::$CAMPAIGN_CANCELLED,
			'execution_time' => null,
		), array(
			'id' => absint( $campaign_id ),
		) );

		return ! empty( $rows );
	}

	public static function get_interaction_stats_by_date_range( $intervals, $oid ) {
		global $wpdb;

		$campaign = self::get( $oid );
		$mode     = absint( $campaign['type'] );

		$open_queries = array_map( function ( $interval ) {
			return "SUM(ROUND ( ( LENGTH(o_interaction) - LENGTH( REPLACE ( o_interaction, '$interval', '') ) ) / LENGTH('$interval') )) AS '$interval'";
		}, $intervals );
		$open_queries = implode( ' , ', $open_queries );

		$click_queries = array_map( function ( $interval ) {
			return "SUM(ROUND ( ( LENGTH(c_interaction) - LENGTH( REPLACE ( c_interaction, '$interval', '') ) ) / LENGTH('$interval') )) AS '$interval'";
		}, $intervals );
		$click_queries = implode( ' , ', $click_queries );

		$open_query  = "SELECT $open_queries FROM {$wpdb->prefix}bwfan_engagement_tracking WHERE `oid` = $oid AND `type` = 2";
		$click_query = "SELECT $click_queries FROM {$wpdb->prefix}bwfan_engagement_tracking WHERE `oid` = $oid AND `type` = 2";

		return array(
			'open'  => ! empty( $open_queries ) && BWFCRM_Campaigns::$CAMPAIGN_EMAIL === $mode ? $wpdb->get_results( $open_query, ARRAY_A ) : array(),
			'click' => ! empty( $click_queries ) ? $wpdb->get_results( $click_query, ARRAY_A ) : array(),
		);
	}

	/**
	 * Get top broadcast
	 *
	 * @param $type
	 *
	 * @return array|object|stdClass[]|null
	 */
	public static function get_top_broadcast( $type = 1 ) {
		global $wpdb;
		$campaign_table = $wpdb->prefix . 'bwfan_broadcast';

		if ( ! bwfan_is_woocommerce_active() ) {
			$base_query = "SELECT COALESCE(b.`id`,'') AS `id`, COALESCE(b.`title`,'') AS `title`, COALESCE(b.`processed`,0) AS `count`, SUM(IF(open>0, 1, 0)) AS `open_count` FROM {$campaign_table} AS b LEFT JOIN {$wpdb->prefix}bwfan_engagement_tracking AS et ON et.oid = b.id WHERE b.`type` = %d AND et.type = 2 GROUP BY et.`oid` ORDER BY `open_count` DESC LIMIT 0,5";
		} else {
			$conversion_table = $wpdb->prefix . 'bwfan_conversions';
			$base_query       = "SELECT COALESCE(cm.`id`,'') AS `id`, COALESCE(cm.`title`,'') AS `title`, cm.`type`, COALESCE(cm.`processed`,0) AS `count`, SUM(c.`wctotal`) AS `total_revenue` FROM $campaign_table AS cm LEFT JOIN $conversion_table AS c ON c.`oid` = cm.`id` WHERE c.`otype` = 2 AND cm.`type` = %d GROUP BY cm.`id` ORDER BY `total_revenue` DESC LIMIT 0,5";
		}

		return $wpdb->get_results( $wpdb->prepare( $base_query, $type ), ARRAY_A );
	}

	public static function hold_broadcast_for_smart_send( $broadcast_id, $hours_to_delay ) {
		$broadcast                = self::get( absint( $broadcast_id ) );
		$b_data                   = ! empty( $broadcast['data'] ) ? json_decode( $broadcast['data'], true ) : array();
		$b_data                   = ! is_array( $b_data ) ? array() : $b_data;
		$b_data['hold_broadcast'] = 1;

		return self::set_delay( $broadcast_id, $hours_to_delay, 'H', $b_data );
	}

	public static function remove_hold_flag_smart_sending( $broadcast_id ) {
		$broadcast                    = self::get( absint( $broadcast_id ) );
		$b_data                       = ! empty( $broadcast['data'] ) ? json_decode( $broadcast['data'], true ) : array();
		$b_data                       = ! is_array( $b_data ) ? array() : $b_data;
		$b_data['smart_sending_done'] = 1;
		if ( isset( $b_data['hold_broadcast'] ) ) {
			unset( $b_data['hold_broadcast'] );
		}

		return ! ! self::update( array(
			'data'          => json_encode( $b_data ),
			'last_modified' => current_time( 'mysql', 1 ),
		), array(
			'id' => absint( $broadcast_id ),
		) );
	}

	public static function declare_the_winner_email( $broadcast_id, $tid ) {
		$broadcast                    = self::get( absint( $broadcast_id ) );
		$b_data                       = ! empty( $broadcast['data'] ) ? json_decode( $broadcast['data'], true ) : array();
		$b_data                       = ! is_array( $b_data ) ? array() : $b_data;
		$b_data                       = array_replace( $b_data, array( 'winner' => absint( $tid ) ) );
		$b_data['smart_sending_done'] = 1;
		if ( isset( $b_data['hold_broadcast'] ) ) {
			unset( $b_data['hold_broadcast'] );
		}

		return ! ! self::update( array(
			'data'          => json_encode( $b_data ),
			'last_modified' => current_time( 'mysql', 1 ),
		), array(
			'id' => absint( $broadcast_id ),
		) );
	}

	public static function update_broadcast_data( $broadcast_id, $data ) {
		if ( ! is_array( $data ) ) {
			return false;
		}

		$broadcast = self::get( absint( $broadcast_id ) );
		$b_data    = ! empty( $broadcast['data'] ) ? json_decode( $broadcast['data'], true ) : array();
		$b_data    = ! is_array( $b_data ) ? array() : $b_data;
		$b_data    = array_replace( $b_data, $data );

		return ! ! self::update( array(
			'data'          => json_encode( $b_data ),
			'last_modified' => current_time( 'mysql', 1 ),
		), array(
			'id' => absint( $broadcast_id ),
		) );
	}

	/**
	 * Get first broadcast id
	 */
	public static function get_first_broadcast_id() {
		global $wpdb;
		$query = 'SELECT MIN(`id`) FROM ' . self::_table();

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

	public static function get_broadcast_data( $broadcast_id ) {
		$broadcast = self::get( absint( $broadcast_id ) );
		if ( ! is_array( $broadcast ) || empty( $broadcast ) || ! isset( $broadcast['data'] ) || empty( $broadcast['data'] ) ) {
			return array();
		}

		$data = json_decode( $broadcast['data'], true );

		return ( ! is_array( $data ) || empty( $data ) ) ? array() : $data;
	}

	public static function get_broadcast_basic_stats( $oid ) {
		global $wpdb;

		$tracking_table  = "{$wpdb->prefix}bwfan_engagement_tracking";
		$broadcast_table = self::_table();

		$query = $wpdb->prepare( "SELECT oid as id, SUM(open) as open_count, (SUM(IF(open>0, 1, 0))/COUNT(ID)) * 100 as open_rate, SUM(click) as click_count, (SUM(IF(click>0, 1, 0))/COUNT(ID)) * 100 as click_rate, SUM(IF(c_status=2, 1, 0)) as sent FROM {$tracking_table} WHERE oid = %d AND type= %d", absint( $oid ), BWFAN_Email_Conversations::$TYPE_CAMPAIGN );
		$stats = $wpdb->get_row( $query, ARRAY_A );
		$stats = ! is_array( $stats ) ? array() : self::get_formatted_campaign_data( array( $stats ) )[0];

		$query   = $wpdb->prepare( "SELECT id,count,status, last_modified FROM {$broadcast_table} WHERE id = %d LIMIT 0, 1", $oid );
		$b_stats = $wpdb->get_row( $query, ARRAY_A );
		$b_stats = ! is_array( $b_stats ) ? array() : $b_stats;

		$stats = array_replace( $stats, $b_stats );

		return $stats;
	}

	/**
	 * Return broadcast count by type
	 *
	 * @return int[]
	 */
	public static function get_broadcast_total_count() {
		global $wpdb;
		$response = [
			'all' => 0,
			'1'   => 0,
			'2'   => 0,
			'3'   => 0,
		];
		$all      = 0;
		$table    = self::_table();
		$query    = "SELECT type, COUNT(*) as count FROM {$table} WHERE 1=1 AND parent=0 GROUP BY type ";
		$result   = $wpdb->get_results( $query, ARRAY_A );

		if ( empty( $result ) ) {
			return $response;
		}
		foreach ( $result as $row ) {
			$response[ $row['type'] ] = intval( $row['count'] );
			$all                      += intval( $row['count'] );
		}
		$response['all'] = $all;

		return $response;
	}

	/**
	 * Return broadcast count by status
	 *
	 * @param int $type broadcast type
	 *
	 * @return int[]
	 */
	public static function get_broadcast_total_count_by_status( $type = 1 ) {
		global $wpdb;
		$response = [
			'2' => 0,
			'3' => 0,
			'4' => 0,
			'5' => 0,
			'6' => 0,
			'7' => 0,
		];
		$all      = 0;
		$table    = self::_table();
		$query    = "SELECT `status`, COUNT(*) as `count` FROM {$table} WHERE 1=1 AND `parent`=0 AND `type`={$type} GROUP BY `status`";
		$result   = $wpdb->get_results( $query, ARRAY_A );

		if ( empty( $result ) ) {
			return $response;
		}
		foreach ( $result as $row ) {
			$response[ $row['status'] ] = intval( $row['count'] );
			$all                        += intval( $row['count'] );
		}
		$response['status_all'] = $all;

		return $response;
	}

	public static function set_delay( $broadcast_id, $delay_time = "10", $time_type = "M", $b_data = [] ) {
		$execute_time = new DateTime( 'now' );
		$execute_time->add( new DateInterval( "PT{$delay_time}{$time_type}" ) );

		$data = array(
			'execution_time' => $execute_time->format( 'Y-m-d H:i:s' ),
			'last_modified'  => current_time( 'mysql', 1 ),
		);

		if ( is_array( $b_data ) && ! empty( $b_data ) ) {
			$data['data'] = json_encode( $b_data );
		}

		return ! ! self::update( $data, array(
			'id' => absint( $broadcast_id ),
		) );
	}

	/**
	 * Remove parent from child's broadcast
	 *
	 * @param $ids
	 *
	 * @return bool|int|mysqli_result|resource|void|null
	 */
	public static function remove_parent_from_child( $ids ) {
		if ( empty( $ids ) ) {
			return false;
		}
		global $wpdb;

		$child_ids = BWFAN_Model_Broadcast::get_broadcast_child( $ids, true );
		if ( empty( $child_ids ) ) {
			return;
		}

		$table       = self::_table();
		$placeholder = array_fill( 0, count( $child_ids ), '%d' );
		$placeholder = implode( ", ", $placeholder );
		$query       = "UPDATE `{$table}` SET `parent` = 0 WHERE `id` IN ( $placeholder )";
		$query       = $wpdb->prepare( $query, $child_ids );

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

	/**
	 * Get broadcast title
	 *
	 * @param $search
	 * @param $limit
	 * @param $offset
	 *
	 * @return array|object|stdClass[]|null
	 */
	public static function get_broadcasts_titles( $search = '', $limit = 10, $offset = 0 ) {
		$broadcast_table = self::_table();

		$broadcast_query = "SELECT `id`, `title` FROM $broadcast_table WHERE `parent` = 0";
		if ( ! empty( $search ) ) {
			$broadcast_query .= " AND title LIKE '%" . esc_sql( $search ) . "%'";
		}

		if ( ! empty( $limit ) ) {
			global $wpdb;
			$broadcast_query .= " LIMIT %d, %d";
			$broadcast_query = $wpdb->prepare( $broadcast_query, $offset, $limit );
		}

		return self::get_results( $broadcast_query );
	}

}
