<?php
/**
 * Handles the Ignored Events functionality.
 *
 * @since 4.5.13
 *
 * @package Tribe\Events
 */

// Don't load directly.
if ( ! defined( 'ABSPATH' ) ) {
	die( '-1' );
}

if ( ! class_exists( 'Tribe__Events__Ignored_Events' ) ) {
	/**
	 * Ignored Events are fully powered by this class.
	 */
	class Tribe__Events__Ignored_Events {
		public static $ignored_status = 'tribe-ignored';

		public static $legacy_deleted_post = 'deleted_event';

		public static $legacy_origin = 'ical-importer';

		/**
		 * Where we save the previous Status when ignoring an Event.
		 *
		 * @since 4.5.13
		 * @var string
		 */
		public static $key_previous_status = '_tribe_ignored_event_previous_status';

		/**
		 * Static Singleton Factory Method.
		 *
		 * @return self
		 */
		public static function instance() {
			return tribe( 'tec.ignored-events' );
		}

		public function action_assets() {
			$plugin   = Tribe__Events__Main::instance();
			$localize = [];

			if ( ! empty( $_GET['post'] ) && $this->can_ignore( $_GET['post'] ) ) {
				$post = get_post( $_GET['post'] );
				if ( self::$ignored_status === $post->post_status ) {
					$localize['single'] = [
						'link_text'   => esc_html__( 'Delete Permanently', 'the-events-calendar' ),
						'link_title'  => esc_attr__( 'Ignored events that are deleted will be removed permanently. They can be recreated via import.', 'the-events-calendar' ),
						'link_nonce'  => wp_create_nonce( 'delete-post_' . $post->ID ),
						'link_post'   => $post->ID,
						'link_status' => esc_html__( 'Ignored', 'the-events-calendar' ),
					];
				} else {
					$localize['single'] = [
						'link_text'  => esc_html__( 'Hide & Ignore', 'the-events-calendar' ),
						'link_title' => esc_attr__( 'Ignored events do not show on the calendar but can be updated with future imports', 'the-events-calendar' ),
					];
				}
			}

			if ( isset( $_GET['post_status'] ) && self::$ignored_status === $_GET['post_status'] ) {
				$localize['archive'] = [
					'delete_label' => esc_html__( 'Delete Permanently', 'the-events-calendar' ),
				];
			}

			$args = [
				'localize'     => [
					'name' => 'tribe_ignore_events',
					'data' => $localize,
				],
				'conditionals' => static function () {
					/** @var Tribe__Admin__Helpers $admin_helpers */
					$admin_helpers = tribe( 'admin.helpers' );
					return $admin_helpers->is_screen() || $admin_helpers->is_post_type_screen();
				},
			];

			tec_asset( $plugin, 'tribe-ignored-events', 'admin-ignored-events.js', [ 'jquery' ], 'admin_enqueue_scripts', $args );
		}

		/**
		 * Filter the displayed bulk actions on the Ignored Events status.
		 *
		 * @param array $actions List of bulk actions.
		 *
		 * @return array
		 */
		public function filter_bulk_actions( $actions ) {

			if ( ! isset( $_GET['post_status'] ) || self::$ignored_status !== $_GET['post_status'] ) {
				return $actions;
			}


			$post_type_obj = get_post_type_object( Tribe__Events__Main::POSTTYPE );

			if ( isset( $actions['trash'] ) ) {
				unset( $actions['trash'] );
			}

			return $actions;
		}

		/**
		 * Makes sure that we have the Required Messages displaying correctly for the Legacy Events Warning.
		 *
		 * @param array $messages  Array of arrays, with the CPT messages for each status.
		 * @param array $counts    Array with the counts of each one of the messages.
		 *
		 * @return array
		 */
		public function filter_updated_messages( $messages, $counts ) {
			if ( ! Tribe__Admin__Helpers::instance()->is_post_type_screen( Tribe__Events__Main::POSTTYPE ) ) {
				return $messages;
			}

			if ( ! isset( $_GET['ids'] ) ) {
				return $messages;
			}

			$check_counts = array_filter( $counts );
			if ( empty( $check_counts ) ) {
				return $messages;
			}

			if ( ! isset( $counts['trashed'] ) || 0 === $counts['trashed'] ) {
				return $messages;
			}

			$ids           = (array) explode( ',', $_GET['ids'] );
			$ignored       = array_filter( $ids, [ $this, 'can_ignore' ] );
			$count_ignored = count( $ignored );

			if ( 0 >= $count_ignored ) {
				return $messages;
			}
			$counts['trashed'] -= $count_ignored;

			$messages[ Tribe__Events__Main::POSTTYPE ] = $messages['post'];

			// We are going to continue to use the language "posts" as that's what WordPress uses for custom post types in this messaging.
			if ( 0 === $counts['trashed'] ) {
				/* translators: %s: Number of posts moved to ignored status. */
				$messages[ Tribe__Events__Main::POSTTYPE ]['trashed'] = '%s ' . _n( 'post moved to Ignored.', 'posts moved to Ignored.', $count_ignored, 'the-events-calendar' );
			} else {
				$GLOBALS['bulk_counts'] = $counts;
				/* translators: %s: Number of posts moved to the trash. */
				$messages[ Tribe__Events__Main::POSTTYPE ]['trashed'] = _n( '%s post moved to the Trash', '%s posts moved to the Trash', $counts['trashed'], 'the-events-calendar' ) . ' ';
				/* translators: %s: Number of posts moved to ignored status. */
				$messages[ Tribe__Events__Main::POSTTYPE ]['trashed'] .= sprintf( _n( 'and %s post moved to Ignored.', 'and %s posts moved to Ignored.', $count_ignored, 'the-events-calendar' ), $count_ignored );
			}

			$args = [
				'ids'          => preg_replace( '/[^0-9,]/', '', $_REQUEST['ids'] ),
				'tribe-action' => 'tribe-restore',
				'post_type'    => Tribe__Events__Main::POSTTYPE,
			];
			$url  = wp_nonce_url( add_query_arg( $args, 'edit.php' ), 'tribe-restore' );

			// Cant `esc_url` this URL because it will make sprintf throw a warning on WP code.
			$messages[ Tribe__Events__Main::POSTTYPE ]['trashed'] .= ' <a href="' . urldecode( $url ) . '" class="tribe-restore-link">' . esc_html__( 'Undo', 'the-events-calendar' ) . '</a>';

			return $messages;
		}

		/**
		 * Returns the HTML for a notice depending on the if we have Legacy Items to be migrated.
		 *
		 * @return string
		 */
		public function render_notice_legacy() {
			if ( ! Tribe__Admin__Helpers::instance()->is_post_type_screen( Tribe__Events__Main::POSTTYPE ) ) {
				return false;
			}

			if ( empty( $_GET['post_status'] ) || $_GET['post_status'] !== self::$ignored_status ) {
				return false;
			}

			if ( ! $this->has_legacy_deleted_posts() ) {
				return false;
			}

			$html  = '<p>' . esc_html__( 'Event Aggregator includes a new, better system for removing unwanted imported events from your calendar. Click the button below to transition previously deleted events. This process will remove unwanted records from your database and include recent or upcoming trashed events in your Ignored archive.', 'the-events-calendar' );
			$html .= ' <a href="https://theeventscalendar.com/knowledgebase/ignored-events/" target="_blank">' . esc_html_x( 'Read more about Ignored Events.', 'link to knowlegebase article', 'the-events-calendar' ) . '</a></p>';
			$html .= '<p style="display:inline-block;">' . get_submit_button( esc_html__( 'Migrate Legacy Ignored Events', 'the-events-calendar' ), 'secondary', 'tribe-migrate-legacy-events', false ) . '<span class="spinner"></span></p>';

			return Tribe__Admin__Notices::instance()->render( 'legacy-ignored-events', $html );
		}

		/**
		 * Action to Restore Events on the Single Page.
		 *
		 * @since 6.15.11 Moved check on action to earlier in the function to prevent warning from being logged.
		 *
		 * @return void|Redirect
		 */
		public function action_restore_events() {
			if ( 'tribe-restore' !== tribe_get_request_var( 'action', '' ) ) {
				return;
			}

			if ( ! wp_verify_nonce( tribe_get_request_var( '_wpnonce', '' ), 'restore-post_' . tribe_get_request_var( 'post', '' ) ) ) {
				return;
			}

			$event = get_post( absint( tribe_get_request_var( 'post', '' ) ) );

			if ( ! $event instanceof WP_Post ) {
				return;
			}

			if ( Tribe__Events__Main::POSTTYPE !== $event->post_type ) {
				return;
			}

			if ( self::$ignored_status !== $event->post_status ) {
				return;
			}

			if ( ! function_exists( 'wp_get_referer' ) ) {
				if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
					$sendback = $_SERVER['REQUEST_URI'];
				} elseif ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
					$sendback = $_REQUEST['_wp_http_referer'];
				} elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
					$sendback = $_SERVER['HTTP_REFERER'];
				}
			} else {
				$sendback = wp_get_referer();
			}

			$sendback = remove_query_arg( [ 'trashed', 'untrashed', 'deleted', 'locked', 'ids' ], $sendback );

			if ( isset( $_REQUEST['ids'] ) ) {
				$post_ids = explode( ',', $_REQUEST['ids'] );
			} elseif ( ! empty( $_REQUEST['post'] ) ) {
				$post_ids = array_map( 'intval', (array) $_REQUEST['post'] );
			}

			$restored = 0;
			foreach ( (array) $post_ids as $post_id ) {
				if ( ! current_user_can( 'delete_post', $post_id ) ) {
					wp_die( esc_html__( 'You do not have permission to restore this post.', 'the-events-calendar' ) );
				}

				if ( ! $this->restore_event( $post_id ) ) {
					wp_die( esc_html__( 'Error restoring from Ignored Events.', 'the-events-calendar' ) );
				}

				++$restored;
			}
			$sendback = add_query_arg( 'restored', $restored, $sendback );

			wp_redirect( $sendback );
			exit;
		}

		/**
		 * Allows Bulk Actions to work its magic (more complex than it needs to be).
		 *
		 * @return void|false
		 */
		public function action_restore_ignored() {
			if ( ! Tribe__Admin__Helpers::instance()->is_post_type_screen( Tribe__Events__Main::POSTTYPE ) ) {
				return false;
			}

			if ( ! isset( $_GET['ids'] ) ) {
				return false;
			}

			if ( ! isset( $_GET['tribe-action'] ) || 'tribe-restore' !== $_GET['tribe-action'] ) {
				return false;
			}

			if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'tribe-restore' ) ) {
				return false;
			}

			$ids      = (array) explode( ',', $_GET['ids'] );
			$restored = [];

			foreach ( $ids as $id ) {
				if ( ! current_user_can( 'delete_post', $id ) ) {
					wp_die( esc_html__( 'You do not have permission to restore this post.', 'the-events-calendar' ) );
				}

				$restore = $this->restore_event( $id );

				if ( ! $restore ) {
					wp_die( esc_html__( 'Error restoring from Ignored Events.', 'the-events-calendar' ) );
				}

				$restored[] = $restore;
			}

			$count_restored = count( $restored );

			$message = '<p>' . sprintf( _n( '%s post restored.', '%s posts restored.', $count_restored, 'the-events-calendar' ), $count_restored ) . '</p>';

			if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
				$_SERVER['REQUEST_URI'] = remove_query_arg( [ 'tribe-action', '_wpnonce' ], $_SERVER['REQUEST_URI'] );
			} elseif ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
				$_REQUEST['_wp_http_referer'] = remove_query_arg(
					[
						'tribe-action',
						'_wpnonce',
					],
					$_REQUEST['_wp_http_referer']
				);
			} elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
				$_SERVER['HTTP_REFERER'] = remove_query_arg( [ 'tribe-action', '_wpnonce' ], $_SERVER['HTTP_REFERER'] );
			}

			return tribe_notice( 'restored-events', $message, 'dismiss=1&type=success' );
		}

		/**
		 * Which Columns are available on Ignored Events view.
		 *
		 * @param array $columns Columns and its labels.
		 *
		 * @return array
		 */
		public function filter_columns( $columns ) {
			if ( empty( $_GET['post_status'] ) || $_GET['post_status'] !== self::$ignored_status ) {
				return $columns;
			}

			// Remove unwanted Columns.
			unset( $columns['author'], $columns['events-cats'], $columns['tags'], $columns['comments'], $columns['recurring'] );

			// Add New Columns backwards.
			$columns = Tribe__Main::array_insert_after_key(
				'title',
				$columns,
				[ 'source' => esc_html__( 'Source', 'the-events-calendar' ) ]
			);

			$info    = ' <span class="dashicons dashicons-editor-help" title="' . esc_attr__( 'The last time this event was imported and/or updated via import.', 'the-events-calendar' ) . '"></span>';
			$columns = Tribe__Main::array_insert_after_key(
				'title',
				$columns,
				[ 'last-import' => esc_html__( 'Last Import', 'the-events-calendar' ) . $info ]
			);

			return $columns;
		}

		/**
		 * Filters the Contents of the Columns for the Ignored Events View.
		 *
		 * @param string      $column Which column we are dealing with.
		 * @param int|WP_Post $post   WP Post ID or Object.
		 *
		 * @return string|null|false
		 */
		public function action_column_contents( $column, $post ) {
			$record = Tribe__Events__Aggregator__Records::instance()->get_by_event_id( $post );

			if ( tribe_is_error( $record ) ) {
				return false;
			}

			$html = [];
			if ( 'source' === $column ) {
				$html[] = '<p>' . esc_html_x( 'via ', 'record via origin', 'the-events-calendar' ) . '<strong>' . $record->get_label() . '</strong></p>';
				if ( 'ea/ics' === $record->post->post_mime_type || 'ea/csv' === $record->post->post_mime_type ) {
					$file_path = get_attached_file( absint( $record->meta['file'] ) );
					$filename  = basename( $file_path );
					$html[]    = '<p>' . esc_html__( 'Source:', 'the-events-calendar' ) . ' <code>' . esc_html( $filename ) . '</code></p>';
				} else {
					$html[] = '<p>' . esc_html__( 'Source:', 'the-events-calendar' ) . ' <code>' . esc_html( $record->meta['source'] ) . '</code></p>';
				}
			} elseif ( 'last-import' === $column ) {
				$last_import = null;
				$original    = $record->post->post_modified;
				$time        = strtotime( $original );
				$now         = time();

				$html[] = '<span title="' . esc_attr( $original ) . '">';
				if ( ( $now - $time ) <= DAY_IN_SECONDS ) {
					$diff = human_time_diff( $time, $now );
					if ( ( $now - $time ) > 0 ) {
						$html[] = sprintf( esc_html_x( 'about %s ago', 'human readable time ago', 'the-events-calendar' ), $diff );
					} else {
						$html[] = sprintf( esc_html_x( 'in about %s', 'in human readable time', 'the-events-calendar' ), $diff );
					}
				} else {
					$html[] = date( Tribe__Date_Utils::DATEONLYFORMAT, $time ) . '<br>' . date( Tribe__Date_Utils::TIMEFORMAT, $time );
				}

				$html[] = '</span>';
			}

			echo implode( "\r\n", (array) $html );
		}

		/**
		 * Add the required Row Actions for the Ignored Events View.
		 *
		 * @since 6.15.14 Add data-ignore-event attribute when ignoring an event.
		 *
		 * @param  array       $actions List of the current actions
		 * @param  int|WP_Post $post    WP Post ID or Object
		 *
		 * @return array
		 */
		public function filter_actions( $actions, $post ) {
			$event = get_post( $post );

			if ( ! $event instanceof WP_Post ) {
				return $actions;
			}

			if ( Tribe__Events__Main::POSTTYPE !== $event->post_type ) {
				return $actions;
			}
			$title = _draft_or_post_title();

			if ( self::$ignored_status !== $event->post_status ) {
				// Modify when it can be ignored.
				if ( $this->can_ignore( $event ) ) {
					$actions['trash'] = sprintf(
						'<a href="%1$s" class="submitdelete" data-ignore-event aria-label="%2$s" title="%3$s">%4$s</a>',
						get_delete_post_link( $event->ID ),
						/* translators: %s: post title */
						esc_attr( sprintf( __( 'Hide and Ignore &#8220;%s&#8221;', 'the-events-calendar' ), $title ) ),
						esc_attr__( 'Ignored events do not show on the calendar but can be updated with future imports', 'the-events-calendar' ),
						__( 'Hide & Ignore', 'the-events-calendar' )
					);
				}

				return $actions;
			}

			$origin = get_post_meta( $event->ID, '_EventOrigin', true );

			foreach ( $actions as $key => $html ) {
				/**
				 * @todo Add logic to prevent removal of edit on EA origin.
				 */
				if ( 'edit' === $key && true === false ) {
					continue;
				}

				// Remove all Actions on.
				unset( $actions[ $key ] );
			}

			$post_type_object = get_post_type_object( $event->post_type );

			if ( current_user_can( 'delete_post', $event->ID ) ) {
				$actions['restore'] = sprintf(
					'<a href="%s" aria-label="%s">%s</a>',
					wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=tribe-restore', $event->ID ) ), 'restore-post_' . $event->ID ),
					/* translators: %s: post title */
					esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Ignored', 'the-events-calendar' ), $title ) ),
					__( 'Restore', 'the-events-calendar' )
				);

				$actions['delete'] = sprintf(
					'<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
					get_delete_post_link( $event->ID, '', true ),
					/* translators: %s: post title */
					esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently', 'the-events-calendar' ), $title ) ),
					__( 'Delete Permanently', 'the-events-calendar' )
				);
			}

			return $actions;
		}

		/**
		 * Add the new Link to the Ignored events on the Events Page.
		 *
		 * @param array $views Array of all the previous Views/Links.
		 *
		 * @return array $views After adding the new Link.
		 */
		public function filter_views( $views = [] ) {
			// This will prevent having the Ignored link twice on the Edit Page.
			if ( $this->has_ignored_posts( false ) ) {
				return $views;
			}

			// Are there any legacy deleted posts?
			$counter = $this->count_legacy_deleted_posts();
			if ( 0 >= $counter ) {
				return $views;
			}

			$args = [
				'post_type'   => Tribe__Events__Main::POSTTYPE,
				'post_status' => self::$ignored_status,
			];

			$url = add_query_arg( $args, 'edit.php' );

			$views['import-deleted'] = '<a class="' . ( isset( $_GET['ignored_events'] ) ? 'current' : '' ) . '" href="' . esc_url( $url ) . '">' . esc_html__( 'Ignored', 'the-events-calendar' )
				. sprintf( ' <span class="count">(%d)</span></a>', $counter );

			return $views;
		}

		/**
		 * Count legacy "ignored" posts.
		 *
		 * @return int
		 */
		public function count_legacy_deleted_posts() {
			// Fetch any posts on the `deleted_event` CPT.
			$counter = wp_count_posts( self::$legacy_deleted_post );

			// Check if we have trash status set
			if ( ! isset( $counter->trash ) ) {
				return 0;
			}

			// // Return the trash count as an integer.
			return absint( $counter->trash );
		}

		/**
		 * Check if there are any legacy posts.
		 *
		 * @return boolean
		 */
		public function has_legacy_deleted_posts() {
			return $this->count_legacy_deleted_posts() > 0;
		}

		/**
		 * Check if there are any ignored posts.
		 *
		 * @param boolean $check_legacy If the method should also check legacy CPTs.
		 *
		 * @return boolean
		 */
		public function has_ignored_posts( $check_legacy = true ) {
			$has_legacy = $this->has_legacy_deleted_posts();
			if ( true === $check_legacy && true === $has_legacy ) {
				return true;
			}

			$query = $this->get_query( [ 'fields' => 'ids' ] );

			return $query->have_posts();
		}

		/**
		 * Gets all ids for events that have been ignored.
		 *
		 * @param null|array $args Array of event IDs/objects/data to check.
		 *
		 * @return array
		 */
		public function get( $args = [] ) {
			$query = $this->get_query( $args );

			return $query->posts;
		}

		/**
		 * Gets all ids for events that have been ignored.
		 *
		 * @param null|array $args Array of event IDs/objects/data to check.
		 *
		 * @return WP_Query
		 */
		public function get_query( $args = [] ) {
			$defaults = [
				'post_type'   => Tribe__Events__Main::POSTTYPE,
				'post_status' => self::$ignored_status,
			];

			$args = wp_parse_args( $args, $defaults );

			return new WP_Query( $args );
		}

		/**
		 * Gets all ids for events that have been ignored.
		 *
		 * @param null|array $data Array of event IDs/objects/data to check.
		 * @param array      $args WP_Query args.
		 *
		 * @return array
		 */
		public function get_by_id( $data = [], $args = [] ) {
			// If there isn't any data, there won't be any results. Bail.
			if ( empty( $data ) ) {
				return [];
			}

			// Fields we'll look for in objects/arrays.
			$search = [
				'ID',
				'id',
				'post_id',
			];

			$ids = [];

			// If the passed in data is not an array, turn it into one.
			if ( ! is_array( $data ) ) {
				$data = [ $data ];
			}

			$first = reset( $data );

			if ( is_scalar( $first ) ) {
				$ids = $data;
			} else {
				$id_field = null;
				$first    = (object) $first;

				// Look through the object for one of the possible ID fields and bail when/if we find it.
				foreach ( $search as $field ) {
					if ( empty( $first->$field ) ) {
						continue;
					}

					$id_field = $field;
					break;
				}

				// If we've found an ID field, let's generate an array of those IDs.
				if ( $id_field ) {
					$ids = wp_list_pluck( $data, $id_field );
				}
			}

			// If there aren't any IDs, let's make sure we don't get any results.
			if ( empty( $ids ) ) {
				return [];
			}

			if ( isset( $args['post__in'] ) ) {
				$args['post__in'] = array_merge( $args['post__in'], $ids );
			} else {
				$args['post__in'] = $ids;
			}

			return $this->get( $args );
		}

		/**
		 * Changes the event to the correct Post Status.
		 *
		 * @param int|WP_Post $event Which event to try to convert.
		 * @param bool        $force Whether to force the event to be ignored.
		 *
		 * @return bool|int|WP_Error
		 */
		public function ignore_event( $event, $force = false ) {
			$event = get_post( $event );

			if ( ! $force && ! $this->can_ignore( $event ) ) {
				return false;
			}

			// Update only what we need.
			$arguments = [
				'ID'          => $event->ID,
				'post_type'   => Tribe__Events__Main::POSTTYPE,
				'post_status' => self::$ignored_status,
			];

			// Set the Required Meta to flag it to the Legacy Posts.
			if ( self::$legacy_deleted_post === $event->post_type ) {
				update_post_meta( $event->ID, '_tribe_legacy_ignored_event', 1 );
			}

			// Try to update back to the Event CPT.
			$updated = wp_update_post( $arguments );

			// Saves on a meta the previous Status.
			if ( $updated && 'trash' !== $event->post_status ) {
				update_post_meta( $event->ID, self::$key_previous_status, $event->post_status );
			}

			return $updated;
		}

		/**
		 * Verify if we can Ignore an Event depending on all the Required rules.
		 *
		 * @param int|WP_Post $post ID of the event or its object.
		 *
		 * @return bool
		 */
		public function can_ignore( $post ) {
			$event = get_post( $post );

			// If we don't have a post (weird) we also leave.
			if ( ! $event instanceof WP_Post ) {
				return false;
			}

			// Verify if it's a Legacy Ignore or Tribe Event.
			if ( ! in_array( $event->post_type, [ Tribe__Events__Main::POSTTYPE, self::$legacy_deleted_post ] ) ) {
				return false;
			}

			$ignored_origins = [
				Tribe__Events__Aggregator__Event::$event_origin,
				'facebook-importer',
				'ical-importer',
			];
			$origin          = get_post_meta( $event->ID, '_EventOrigin', true );

			// Verify the Origin.
			if ( ! in_array( $origin, $ignored_origins ) ) {
				return false;
			}

			if ( Tribe__Events__Aggregator__Event::$event_origin === $origin ) {
				$aggregator_origin = get_post_meta( $event->ID, Tribe__Events__Aggregator__Event::$origin_key, true );
			}

			return true;
		}

		/**
		 * Restore Event.
		 *
		 * @param int|WP_Post $event Which event to try to convert.
		 *
		 * @return bool|int|WP_Error
		 */
		public function restore_event( $event ) {
			$event = get_post( $event );

			if ( ! $event instanceof WP_Post ) {
				return false;
			}

			// If we are not in the Event CPT we don't care either.
			if ( Tribe__Events__Main::POSTTYPE !== $event->post_type ) {
				return null;
			}

			$restore_status = get_post_meta( $event->ID, self::$key_previous_status, true );

			if ( empty( $restore_status ) ) {
				/**
				 * Which is the default Post status to Restore Ignored Events.
				 *
				 * @param  string   $post_status
				 * @param  WP_Post  $event
				 */
				$restore_status = apply_filters( 'tribe_events_ignored_events_default_restore_status', 'publish', $event );
			}

			// Update only what we need.
			$arguments = [
				'ID'          => $event->ID,
				'post_status' => $restore_status,
			];

			// Try to update back to the Event CPT.
			$updated = wp_update_post( $arguments );

			// Delete the previous status stored.
			if ( $updated ) {
				delete_post_meta( $event->ID, self::$key_previous_status );
			}

			return $updated;
		}

		/**
		 * Register the Ignored Post Status.
		 *
		 * @return void
		 */
		public function register_ignored_post_status() {
			$arguments = [
				'label'                     => esc_html__( 'Ignored', 'the-events-calendar' ),
				'label_count'               => _n_noop(
					'Ignored <span class="count">(%s)</span>',
					'Ignored <span class="count">(%s)</span>',
					'the-events-calendar'
				),
				'show_in_admin_all_list'    => false,
				'show_in_admin_status_list' => true,
				'public'                    => false,
				'internal'                  => false,
			];
			register_post_status( self::$ignored_status, $arguments );

			// We need to register this to have the legacy work.
			register_post_type(
				self::$legacy_deleted_post,
				[
					'public' => false,
				]
			);
		}

		/**
		 * Making sure that we have the previous status saved.
		 *
		 * @since 4.5.13
		 *
		 * @param int|WP_Post $event  Which event to track the previous status.
		 *
		 * @return bool
		 */
		public function action_track_previous_status( $event ) {
			$event = get_post( $event );

			if ( ! $event instanceof WP_Post ) {
				return false;
			}

			// If we are not in the Event CPT we don't care either.
			if ( Tribe__Events__Main::POSTTYPE !== $event->post_type ) {
				return false;
			}

			if ( self::$ignored_status === $event->post_type ) {
				return false;
			}

			if ( 'trash' === $event->post_type ) {
				return false;
			}

			return update_post_meta( $event->ID, self::$key_previous_status, $event->post_status );
		}

		/**
		 * On version 4.4 of WP we get a new filter to prevent an event from been trashed and/or deleted.
		 *
		 * @param null|bool $unused_check Boolean or null depending if we need to delete or not.
		 * @param int       $post         WP Post ID.
		 * @param bool      $force_delete Force the event delete.
		 *
		 * @return null|bool
		 */
		public function action_pre_delete_event( $unused_check, $post, $force_delete ) {
			// If someone is trying to delete it for-reals we actually delete it.
			if ( true === $force_delete ) {
				return null;
			}

			$post = get_post( $post );

			if ( self::$ignored_status === $post->post_status ) {
				return wp_delete_post( $post->ID, true );
			}

			// Important to note that this needs to return null for any invalid ignoring.
			if ( ! $this->can_ignore( $post ) ) {
				return null;
			}

			$status = $this->ignore_event( $post );

			// If we couldn't convert we actually trash it.
			return ! is_wp_error( $status ) ? $status : null;
		}

		/**
		 * Used to get an trashed event and move it to the `post_status` of ignored.
		 *
		 * @param int $post ID of the post.
		 *
		 * @return bool|null
		 */
		public function action_from_trash_to_ignored( $post ) {
			$post = get_post( $post );

			if ( self::$ignored_status === $post->post_status ) {
				return wp_delete_post( $post->ID, true );
			}

			// Important to note that this needs to return null for any invalid ignoring.
			if ( ! $this->can_ignore( $post ) ) {
				return null;
			}

			$status = $this->ignore_event( $post );

			// If we couldn't convert we actually trash it.
			return ! is_wp_error( $status ) ? $status : null;
		}

		/**
		 * Method that Handles the AJAX converting of Legacy Ignored Events to the new `post_status`.
		 * AJAX methods will not return anything, only print a JSON string.
		 *
		 * @return void
		 */
		public function ajax_convert_legacy_ignored_events() {
			$response = (object) [
				'status' => false,
				'text'   => esc_html__(
					'Error, a unknown bug happened and it was impossible to migrate the Legacy Ignored Events, try again later.',
					'the-events-calendar'
				),
			];

			$post_type = get_post_type_object( Tribe__Events__Main::POSTTYPE );

			if ( empty( $post_type->cap->edit_posts ) || ! current_user_can( $post_type->cap->edit_posts ) ) {
				$response->status = false;
				$response->text   = esc_html__( 'You do not have permission to migrate Legacy Ignored Events', 'the-events-calendar' );

				wp_send_json( $response );
			}

			if ( ! $this->has_legacy_deleted_posts() ) {
				$response->status = true;
				$response->text   = esc_html__( 'There were no Legacy Events to be Migrated, you are ready to rock!', 'the-events-calendar' );

				wp_send_json( $response );
			}

			$args  = [
				'post_type'   => self::$legacy_deleted_post,
				'post_status' => 'trash',
			];
			$query = new WP_Query( $args );

			foreach ( $query->posts as $event ) {
				$status = $this->ignore_event( $event, true );
				if ( is_wp_error( $status ) ) {
					$response->error[ $event->ID ] = $status->get_error_message();
				} else {
					$response->migrated[ $event->ID ] = true;
				}
			}

			if ( ! empty( $response->error ) ) {
				if ( ! empty( $response->migrated ) ) {
					$response->status = false;
					$response->text   = sprintf(
						_n(
							'Migration: %1$d Legacy Ignored Post was migrated but %2$d failed. To see the migrated event you will first need to refresh this screen.',
							'Migration: %1$d Legacy Ignored Posts were migrated but %2$d failed. To see the migrated events you will first need to refresh this screen.',
							count( $response->migrated ),
							'the-events-calendar'
						),
						count( $response->migrated ),
						count( $response->error )
					);
				} else {
					$response->status = false;
					$response->text   = sprintf(
						_n(
							'Migration: %d Legacy Ignored Post failed to be migrated.',
							'Migration: %d Legacy Ignored Posts failed to be migrated.',
							count( $response->error ),
							'the-events-calendar'
						),
						count( $response->error )
					);
				}

				$response->text .= '<ul>';
				foreach ( $response->error as $event_id => $message ) {
					/* translators: 1: Event ID, 2: Error message. */
					$response->text .= '<li>' . sprintf( __( 'Event %1$d: %2$s', 'the-events-calendar' ), $event_id, $message ) . '</li>';
				}
				$response->text .= '</ul>';

			} elseif ( ! empty( $response->migrated ) ) {
				$response->status = true;
				$response->text   = sprintf(
					_n(
						'Migration: %d Legacy Ignored Post was migrated successfully. To see the migrated event you will first need to refresh this screen.',
						'Migration: %d Legacy Ignored Posts were migrated successfully. To see the migrated events you will first need to refresh this screen.',
						count( $response->migrated ),
						'the-events-calendar'
					),
					count( $response->migrated )
				);
			}

			wp_send_json( $response );
		}

		/**
		 * Hooks the filters and actions needed for the class to work.
		 *
		 * @return bool Whether the filters and actions were hooked or not.
		 */
		public function hook() {
			add_action( 'init', [ $this, 'register_ignored_post_status' ] );
			add_action( 'current_screen', [ $this, 'action_restore_events' ] );
			add_action( 'current_screen', [ $this, 'action_restore_ignored' ] );

			/**
			 * `pre_delete_post` only exists after WP 4.4
			 *
			 * @see https://core.trac.wordpress.org/ticket/12706
			 */
			add_filter( 'pre_delete_post', [ $this, 'action_pre_delete_event' ], 10, 3 );
			add_action( 'trashed_post', [ $this, 'action_from_trash_to_ignored' ] );
			add_action( 'wp_trash_post', [ $this, 'action_track_previous_status' ] );

			add_filter( 'views_edit-' . Tribe__Events__Main::POSTTYPE, [ $this, 'filter_views' ] );
			add_filter( 'bulk_actions-edit-' . Tribe__Events__Main::POSTTYPE, [ $this, 'filter_bulk_actions' ], 15 );
			add_filter( 'post_row_actions', [ $this, 'filter_actions' ], 10, 2 );

			add_filter(
				'manage_' . Tribe__Events__Main::POSTTYPE . '_posts_columns',
				[ $this, 'filter_columns' ],
				100
			);
			add_action(
				'manage_' . Tribe__Events__Main::POSTTYPE . '_posts_custom_column',
				[ $this, 'action_column_contents' ],
				100,
				2
			);

			add_action(
				'wp_ajax_tribe_convert_legacy_ignored_events',
				[ $this, 'ajax_convert_legacy_ignored_events' ]
			);

			// Modify Success messages.
			add_filter( 'bulk_post_updated_messages', [ $this, 'filter_updated_messages' ], 10, 2 );

			// Register assets.
			add_action( 'init', [ $this, 'action_assets' ] );

			/**
			 * Register Notices.
			 */
			tribe_notice( 'legacy-ignored-events', [ $this, 'render_notice_legacy' ], 'dismiss=1&type=warning' );

			add_filter( 'wp_count_posts', [ $this, 'patch_count_posts' ] );

			return true;
		}

		/**
		 * Patch post-list-table queries to include ignored status,
		 * as WP just accesses the stati without checking if they exist.
		 *
		 * @see wp-admin/includes/class-wp-posts-list-table.php->get_views()
		 *
		 * @since 5.1.5
		 *
		 * @param object $counts An object containing the current post_type's post
		 *                       counts by status.
		 *
		 * @return object $counts The modified object containing the current post_type's post
		 *                        counts by status.
		 */
		public function patch_count_posts( $counts ) {
			$status = $this::$ignored_status;


			if ( ! isset( $counts->$status ) ) {
				$counts->$status = 0;
			}

			return $counts;
		}
	}
}
