<?php
/**
 * Co-Authors Plus commands for the WP-CLI framework
 *
 * @package wp-cli
 * @since 3.0
 * @see https://github.com/wp-cli/wp-cli
 */
WP_CLI::add_command( 'co-authors-plus', 'CoAuthorsPlus_Command' );

class CoAuthorsPlus_Command extends WP_CLI_Command {

	/**
	 * Subcommand to create guest authors based on users
	 *
	 * @since 3.0
	 *
	 * @subcommand create-guest-authors
	 */
	public function create_guest_authors( $args, $assoc_args ) {
		global $coauthors_plus;

		$defaults = array(
				// There are no arguments at this time
			);
		$this->args = wp_parse_args( $assoc_args, $defaults );

		$users = get_users();
		$created = 0;
		$skipped = 0;
		$progress = \WP_CLI\Utils\make_progress_bar( 'Processing guest authors...', count ( $users ) );
		foreach ( $users as $user ) {

			$result = $coauthors_plus->guest_authors->create_guest_author_from_user_id( $user->ID );
			if ( is_wp_error( $result ) ) {
				$skipped++;
			} else {
				$created++;
			}
			$progress->tick();
		}
		$progress->finish();
		WP_CLI::line( 'All done! Here are your results:' );
		WP_CLI::line( "- {$created} guest author profiles were created" );
		WP_CLI::line( "- {$skipped} users already had guest author profiles" );
	}

	/**
	 * Create author terms for all posts that don't have them
	 *
	 * @subcommand create-terms-for-posts
	 */
	public function create_terms_for_posts() {
		global $coauthors_plus, $wp_post_types;

		// Cache these to prevent repeated lookups
		$authors = array();
		$author_terms = array();

		$args = array(
				'order'            => 'ASC',
				'orderby'          => 'ID',
				'post_type'         => $coauthors_plus->supported_post_types,
				'posts_per_page'    => 100,
				'paged'             => 1,
				'update_meta_cache' => false,
			);

		$posts = new WP_Query( $args );
		$affected = 0;
		$count = 0;
		$total_posts = $posts->found_posts;
		WP_CLI::line( "Now inspecting or updating {$posts->found_posts} total posts." );
		while ( $posts->post_count ) {

			foreach ( $posts->posts as $single_post ) {

				$count++;

				$terms = cap_get_coauthor_terms_for_post( $single_post->ID );
				if ( empty( $terms ) ) {
					WP_CLI::line( sprintf( 'No co-authors found for post #%d.', $single_post->ID ) );
				}

				if ( ! empty( $terms ) ) {
					WP_CLI::line( "{$count}/{$posts->found_posts}) Skipping - Post #{$single_post->ID} '{$single_post->post_title}' already has these terms: " . implode( ', ', wp_list_pluck( $terms, 'name' ) ) );
					continue;
				}

				$author = ( ! empty( $authors[ $single_post->post_author ] ) ) ? $authors[ $single_post->post_author ] : get_user_by( 'id', $single_post->post_author );
				$authors[ $single_post->post_author ] = $author;

				$author_term = ( ! empty( $author_terms[ $single_post->post_author ] ) ) ? $author_terms[ $single_post->post_author ] : $coauthors_plus->update_author_term( $author );
				$author_terms[ $single_post->post_author ] = $author_term;

				wp_set_post_terms( $single_post->ID, array( $author_term->slug ), $coauthors_plus->coauthor_taxonomy );
				WP_CLI::line( "{$count}/{$total_posts}) Added - Post #{$single_post->ID} '{$single_post->post_title}' now has an author term for: " . $author->user_nicename );
				$affected++;
			}

			if ( $count && 0 === $count % 500 ) {
				$this->stop_the_insanity();
				sleep( 1 );
			}

			$args['paged']++;
			$posts = new WP_Query( $args );
		}
		WP_CLI::line( 'Updating author terms with new counts' );
		foreach ( $authors as $author ) {
			$coauthors_plus->update_author_term( $author );
		}

		WP_CLI::success( "Done! Of {$total_posts} posts, {$affected} now have author terms." );

	}

	/**
	 * Subcommand to assign co-authors to a post based on a given meta key
	 *
	 * @since 3.0
	 *
	 * @subcommand assign-coauthors
	 * @synopsis [--meta_key=<key>] [--post_type=<ptype>] [--append_coauthors]
	 */
	public function assign_coauthors( $args, $assoc_args ) {
		global $coauthors_plus;

		$defaults = array(
				'meta_key'         => '_original_import_author',
				'post_type'        => 'post',
				'order'            => 'ASC',
				'orderby'          => 'ID',
				'posts_per_page'   => 100,
				'paged'            => 1,
				'append_coauthors' => false,
			);
		$this->args = wp_parse_args( $assoc_args, $defaults );

		// For global use and not a part of WP_Query
		$append_coauthors = $this->args['append_coauthors'];
		unset( $this->args['append_coauthors'] );

		$posts_total = 0;
		$posts_already_associated = 0;
		$posts_missing_coauthor = 0;
		$posts_associated = 0;
		$missing_coauthors = array();

		$posts = new WP_Query( $this->args );
		while ( $posts->post_count ) {

			foreach ( $posts->posts as $single_post ) {
				$posts_total++;

				// See if the value in the post meta field is the same as any of the existing coauthors
				$original_author = get_post_meta( $single_post->ID, $this->args['meta_key'], true );
				$existing_coauthors = get_coauthors( $single_post->ID );
				$already_associated = false;
				foreach ( $existing_coauthors as $existing_coauthor ) {
					if ( $original_author == $existing_coauthor->user_login ) {
						$already_associated = true;
					}
				}
				if ( $already_associated ) {
					$posts_already_associated++;
					WP_CLI::line( $posts_total . ': Post #' . $single_post->ID . ' already has "' . $original_author . '" associated as a coauthor' );
					continue;
				}

				// Make sure this original author exists as a co-author
				if ( ( ! $coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $original_author ) ) &&
					( ! $coauthor = $coauthors_plus->get_coauthor_by( 'user_login', sanitize_title( $original_author ) ) ) ) {
					$posts_missing_coauthor++;
					$missing_coauthors[] = $original_author;
					WP_CLI::line( $posts_total . ': Post #' . $single_post->ID . ' does not have "' . $original_author . '" associated as a coauthor but there is not a coauthor profile' );
					continue;
				}

				// Assign the coauthor to the post
				$coauthors_plus->add_coauthors( $single_post->ID, array( $coauthor->user_nicename ), $append_coauthors );
				WP_CLI::line( $posts_total . ': Post #' . $single_post->ID . ' has been assigned "' . $original_author . '" as the author' );
				$posts_associated++;
				clean_post_cache( $single_post->ID );
			}

			$this->args['paged']++;
			$this->stop_the_insanity();
			$posts = new WP_Query( $this->args );
		}

		WP_CLI::line( 'All done! Here are your results:' );
		if ( $posts_already_associated ) {
			WP_CLI::line( "- {$posts_already_associated} posts already had the coauthor assigned" );
		}
		if ( $posts_missing_coauthor ) {
			WP_CLI::line( "- {$posts_missing_coauthor} posts reference coauthors that don't exist. These are:" );
			WP_CLI::line( '  ' . implode( ', ', array_unique( $missing_coauthors ) ) );
		}
		if ( $posts_associated ) {
			WP_CLI::line( "- {$posts_associated} posts now have the proper coauthor" );
		}

	}

	/**
	 * Assign posts associated with a WordPress user to a co-author
	 * Only apply the changes if there aren't yet co-authors associated with the post
	 *
	 * @since 3.0
	 *
	 * @subcommand assign-user-to-coauthor
	 * @synopsis --user_login=<user-login> --coauthor=<coauthor>
	 */
	public function assign_user_to_coauthor( $args, $assoc_args ) {
		global $coauthors_plus, $wpdb;

		$defaults = array(
				'user_login'        => '',
				'coauthor'          => '',
			);
		$assoc_args = wp_parse_args( $assoc_args, $defaults );

		$user = get_user_by( 'login', $assoc_args['user_login'] );
		$coauthor = $coauthors_plus->get_coauthor_by( 'login', $assoc_args['coauthor'] );

		if ( ! $user ) {
			WP_CLI::error( __( 'Please specify a valid user_login', 'co-authors-plus' ) );
		}

		if ( ! $coauthor ) {
			WP_CLI::error( __( 'Please specify a valid co-author login', 'co-authors-plus' ) );
		}

		$post_types = implode( "','", $coauthors_plus->supported_post_types );
		$posts = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author=%d AND post_type IN ('$post_types')", $user->ID ) );
		$affected = 0;
		foreach ( $posts as $post_id ) {
			$coauthors = cap_get_coauthor_terms_for_post( $post_id );
			if ( ! empty( $coauthors ) ) {
				WP_CLI::line( sprintf(
					__( 'Skipping - Post #%d already has co-authors assigned: %s', 'co-authors-plus' ),
					$post_id,
					implode( ', ', wp_list_pluck( $coauthors, 'slug' ) )
				) );
				continue;
			}

			$coauthors_plus->add_coauthors( $post_id, array( $coauthor->user_login ) );
			WP_CLI::line( sprintf( __( "Updating - Adding %s's byline to post #%d", 'co-authors-plus' ), $coauthor->user_login, $post_id ) );
			$affected++;
			if ( $affected && 0 === $affected % 100 ) {
				sleep( 2 );
			}
		}
		WP_CLI::success( sprintf( __( 'All done! %d posts were affected.', 'co-authors-plus' ), $affected ) );

	}

	/**
	 * Subcommand to reassign co-authors based on some given format
	 * This will look for terms with slug 'x' and rename to term with slug and name 'y'
	 * This subcommand can be helpful for cleaning up after an import if the usernames
	 * for authors have changed. During the import process, 'author' terms will be
	 * created with the old user_login value. We can use this to migrate to the new user_login
	 *
	 * @todo support reassigning by CSV
	 *
	 * @since 3.0
	 *
	 * @subcommand reassign-terms
	 * @synopsis [--author-mapping=<file>] [--old_term=<slug>] [--new_term=<slug>]
	 */
	public function reassign_terms( $args, $assoc_args ) {
		global $coauthors_plus;

		$defaults = array(
				'author_mapping'    => null,
				'old_term'          => null,
				'new_term'          => null,
			);
		$this->args = wp_parse_args( $assoc_args, $defaults );

		$author_mapping = $this->args['author_mapping'];
		$old_term = $this->args['old_term'];
		$new_term = $this->args['new_term'];

		// Get the reassignment data
		if ( $author_mapping && file_exists( $author_mapping ) ) {
			require_once( $author_mapping );
			$authors_to_migrate = $cli_user_map;
		} else if ( $author_mapping ) {
			WP_CLI::error( "author_mapping doesn't exist: " . $author_mapping );
			exit;
		}

		// Alternate reassigment approach
		if ( $old_term && $new_term ) {
			$authors_to_migrate = array(
					$old_term => $new_term,
				);
		}

		// For each author to migrate, check whether the term exists,
		// whether the target term exists, and only do the migration if both are met
		$results = (object) array(
				'old_term_missing' => 0,
				'new_term_exists' => 0,
				'success' => 0,
			);
		foreach ( $authors_to_migrate as $old_user => $new_user ) {

			if ( is_numeric( $new_user ) ) {
				$new_user = get_user_by( 'id', $new_user )->user_login;
			}

			// The old user should exist as a term
			$old_term = $coauthors_plus->get_author_term( $coauthors_plus->get_coauthor_by( 'login', $old_user ) );
			if ( ! $old_term ) {
				WP_CLI::line( "Error: Term '{$old_user}' doesn't exist, skipping" );
				$results->old_term_missing++;
				continue;
			}

			// If the new user exists as a term already, we want to reassign all posts to that
			// new term and delete the original
			// Otherwise, simply rename the old term
			$new_term = $coauthors_plus->get_author_term( $coauthors_plus->get_coauthor_by( 'login', $new_user ) );
			if ( is_object( $new_term ) ) {
				WP_CLI::line( "Success: There's already a '{$new_user}' term for '{$old_user}'. Reassigning {$old_term->count} posts and then deleting the term" );
				$args = array(
						'default' => $new_term->term_id,
						'force_default' => true,
					);
				wp_delete_term( $old_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
				$results->new_term_exists++;
			} else {
				$args = array(
						'slug' => $new_user,
						'name' => $new_user,
					);
				wp_update_term( $old_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
				WP_CLI::line( "Success: Converted '{$old_user}' term to '{$new_user}'" );
				$results->success++;
			}
			clean_term_cache( $old_term->term_id, $coauthors_plus->coauthor_taxonomy );
		}

		WP_CLI::line( 'Reassignment complete. Here are your results:' );
		WP_CLI::line( "- $results->success authors were successfully reassigned terms" );
		WP_CLI::line( "- $results->new_term_exists authors had their old term merged to their new term" );
		WP_CLI::line( "- $results->old_term_missing authors were missing old terms" );

	}

	/**
	 * Change a term from representing one user_login value to another
	 * If the term represents a guest author, the post_name will be changed
	 * in addition to the term slug/name
	 *
	 * @since 3.0.1
	 *
	 * @subcommand rename-coauthor
	 * @synopsis --from=<user-login> --to=<user-login>
	 */
	public function rename_coauthor( $args, $assoc_args ) {
		global $coauthors_plus, $wpdb;

		$defaults = array(
				'from'      => null,
				'to'        => null,
			);
		$assoc_args = array_merge( $defaults, $assoc_args );

		$to_userlogin = $assoc_args['to'];
		$to_userlogin_prefixed = 'cap-' . $to_userlogin;

		$orig_coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $assoc_args['from'] );
		if ( ! $orig_coauthor ) {
			WP_CLI::error( "No co-author found for {$assoc_args['from']}" );
		}

		if ( ! $to_userlogin ) {
			WP_CLI::error( '--to param must not be empty' );
		}

		if ( $coauthors_plus->get_coauthor_by( 'user_login', $to_userlogin ) ) {
			WP_CLI::error( 'New user_login value conflicts with existing co-author' );
		}

		$orig_term = $coauthors_plus->get_author_term( $orig_coauthor );

		WP_CLI::line( "Renaming {$orig_term->name} to {$to_userlogin}" );
		$rename_args = array(
				'name'         => $to_userlogin,
				'slug'         => $to_userlogin_prefixed,
			);
		wp_update_term( $orig_term->term_id, $coauthors_plus->coauthor_taxonomy, $rename_args );

		if ( 'guest-author' == $orig_coauthor->type ) {
			$wpdb->update( $wpdb->posts, array( 'post_name' => $to_userlogin_prefixed ), array( 'ID' => $orig_coauthor->ID ) );
			clean_post_cache( $orig_coauthor->ID );
			update_post_meta( $orig_coauthor->ID, 'cap-user_login', $to_userlogin );
			$coauthors_plus->guest_authors->delete_guest_author_cache( $orig_coauthor->ID );
			WP_CLI::line( 'Updated guest author profile value too' );
		}

		WP_CLI::success( 'All done!' );
	}

	/**
	 * Swap one co-author with another on all posts for which they are a co-author. Unlike rename-coauthor,
	 * this leaves the original co-author term intact and works when the 'to' user already has a co-author term.
	 *
	 * @subcommand swap-coauthors
	 * @synopsis --from=<user-login> --to=<user-login> [--post_type=<ptype>] [--dry=<dry>]
	 */
	public function swap_coauthors( $args, $assoc_args ) {
		global $coauthors_plus, $wpdb;

		$defaults = array(
			'from'      => null,
			'to'        => null,
			'post_type'	=> 'post',
			'dry'		=> false,
		);

		$assoc_args = array_merge( $defaults, $assoc_args );

		$dry 						= $assoc_args['dry'];

		$from_userlogin 			= $assoc_args['from'];
		$to_userlogin 				= $assoc_args['to'];

		$from_userlogin_prefixed	= 'cap-' . $from_userlogin;
		$to_userlogin_prefixed 		= 'cap-' . $to_userlogin;

		$orig_coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $from_userlogin );

		if ( ! $orig_coauthor ) {
			WP_CLI::error( "No co-author found for $from_userlogin" );
		}

		if ( ! $to_userlogin ) {
			WP_CLI::error( '--to param must not be empty' );
		}

		$to_coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $to_userlogin );

		if ( ! $to_coauthor ) {
			WP_CLI::error( "No co-author found for $to_userlogin" );
		}

		WP_CLI::line( "Swapping authorship from {$from_userlogin} to {$to_userlogin}" );

		$query_args = array(
			'post_type'        	=> $assoc_args['post_type'],
			'order'            	=> 'ASC',
			'orderby'          	=> 'ID',
			'posts_per_page'   	=> 100,
			'paged'            	=> 1,
			'tax_query'			=> array(
				array(
					'taxonomy' 	=> $coauthors_plus->coauthor_taxonomy,
					'field'		=> 'slug',
					'terms'		=> array( $from_userlogin_prefixed ),
				),
			),
		);

		$posts = new WP_Query( $query_args );

		$posts_total = 0;

		WP_CLI::line( "Found $posts->found_posts posts to update." );

		while ( $posts->post_count ) {
			foreach ( $posts->posts as $post ) {
				$coauthors = get_coauthors( $post->ID );

				if ( ! is_array( $coauthors ) || ! count( $coauthors ) ) {
					continue;
				}

				$coauthors = wp_list_pluck( $coauthors, 'user_login' );

				$posts_total++;

				if ( ! $dry ) {
					// Remove the $from_userlogin from $coauthors
					foreach ( $coauthors as $index => $user_login ) {
						if ( $from_userlogin === $user_login ) {
							unset( $coauthors[ $index ] );

							break;
						}
					}

					// Add the 'to' author on
					$coauthors[] = $to_userlogin;

					// By not passing $append = false as the 3rd param, we replace all existing coauthors
					$coauthors_plus->add_coauthors( $post->ID, $coauthors, false );

					WP_CLI::line( $posts_total . ': Post #' . $post->ID . ' has been assigned "' . $to_userlogin . '" as a co-author' );

					clean_post_cache( $post->ID );
				} else {
					WP_CLI::line( $posts_total . ': Post #' . $post->ID . ' will be assigned "' . $to_userlogin . '" as a co-author' );
				}
			}

			// In dry mode, we must manually advance the page
			if ( $dry ) {
				$query_args['paged']++;
			}

			$this->stop_the_insanity();

			$posts = new WP_Query( $query_args );
		}

		WP_CLI::success( 'All done!' );
	}

	/**
	 * List all of the posts without assigned co-authors terms
	 *
	 * @since 3.0
	 *
	 * @subcommand list-posts-without-terms
	 * @synopsis [--post_type=<ptype>]
	 */
	public function list_posts_without_terms( $args, $assoc_args ) {
		global $coauthors_plus;

		$defaults = array(
				'post_type'         => 'post',
				'order'             => 'ASC',
				'orderby'           => 'ID',
				'year'              => '',
				'posts_per_page'    => 300,
				'paged'             => 1,
				'no_found_rows'     => true,
				'update_meta_cache' => false,
			);
		$this->args = wp_parse_args( $assoc_args, $defaults );

		$posts = new WP_Query( $this->args );
		while ( $posts->post_count ) {

			foreach ( $posts->posts as $single_post ) {

				$terms = cap_get_coauthor_terms_for_post( $single_post->ID );
				if ( empty( $terms ) ) {
					$saved = array(
							$single_post->ID,
							addslashes( $single_post->post_title ),
							get_permalink( $single_post->ID ),
							$single_post->post_date,
						);
					WP_CLI::line( '"' . implode( '","', $saved ) . '"' );
				}
			}

			$this->stop_the_insanity();

			$this->args['paged']++;
			$posts = new WP_Query( $this->args );
		}

	}

	/**
	 * Migrate author terms without prefixes to ones with prefixes
	 * Pre-3.0, all author terms didn't have a 'cap-' prefix, which means
	 * they can easily collide with terms in other taxonomies
	 *
	 * @since 3.0
	 *
	 * @subcommand migrate-author-terms
	 */
	public function migrate_author_terms( $args, $assoc_args ) {
		global $coauthors_plus;

		$author_terms = get_terms( $coauthors_plus->coauthor_taxonomy, array( 'hide_empty' => false ) );
		WP_CLI::line( 'Now migrating up to ' . count( $author_terms ) . ' terms' );
		foreach ( $author_terms as $author_term ) {
			// Term is already prefixed. We're good.
			if ( preg_match( '#^cap\-#', $author_term->slug, $matches ) ) {
				WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) is already prefixed, skipping" );
				continue;
			}
			// A prefixed term was accidentally created, and the old term needs to be merged into the new (WordPress.com VIP)
			if ( $prefixed_term = get_term_by( 'slug', 'cap-' . $author_term->slug, $coauthors_plus->coauthor_taxonomy ) ) {
				WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) has a new term too: $prefixed_term->slug ($prefixed_term->term_id). Merging" );
				$args = array(
					'default' => $author_term->term_id,
					'force_default' => true,
				);
				wp_delete_term( $prefixed_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
			}

			// Term isn't prefixed, doesn't have a sibling, and should be updated
			WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) isn't prefixed, adding one" );
			$args = array(
					'slug' => 'cap-' . $author_term->slug,
				);
			wp_update_term( $author_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
		}
		WP_CLI::success( 'All done! Grab a cold one (Affogatto)' );
	}

	/**
	 * Update the post count and description for each author and guest author
	 *
	 * @since 3.0
	 *
	 * @subcommand update-author-terms
	 */
	public function update_author_terms() {
		global $coauthors_plus;
		$author_terms = get_terms( $coauthors_plus->coauthor_taxonomy, array( 'hide_empty' => false ) );
		WP_CLI::line( 'Now updating ' . count( $author_terms ) . ' terms' );
		foreach ( $author_terms as $author_term ) {
			$old_count = $author_term->count;
			$coauthor = $coauthors_plus->get_coauthor_by( 'user_nicename', $author_term->slug );
			$coauthors_plus->update_author_term( $coauthor );
			$coauthors_plus->update_author_term_post_count( $author_term );
			wp_cache_delete( $author_term->term_id, $coauthors_plus->coauthor_taxonomy );
			$new_count = get_term_by( 'id', $author_term->term_id, $coauthors_plus->coauthor_taxonomy )->count;
			WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) changed from {$old_count} to {$new_count} and the description was refreshed" );
		}
		// Create author terms for any users that don't have them
		$users = get_users();
		foreach ( $users as $user ) {
			$term = $coauthors_plus->get_author_term( $user );
			if ( empty( $term ) || empty( $term->description ) ) {
				$coauthors_plus->update_author_term( $user );
				WP_CLI::line( "Created author term for {$user->user_login}" );
			}
		}

		// And create author terms for any Guest Authors that don't have them
		if ( $coauthors_plus->is_guest_authors_enabled() && $coauthors_plus->guest_authors instanceof CoAuthors_Guest_Authors ) {
			$args = array(
				'order'             => 'ASC',
				'orderby'           => 'ID',
				'post_type'         => $coauthors_plus->guest_authors->post_type,
				'posts_per_page'    => 100,
				'paged'             => 1,
				'update_meta_cache' => false,
				'fields'            => 'ids',
			);

			$posts = new WP_Query( $args );
			$count = 0;
			WP_CLI::line( "Now inspecting or updating {$posts->found_posts} Guest Authors." );

			while ( $posts->post_count ) {
				foreach ( $posts->posts as $guest_author_id ) {
					$count++;

					$guest_author = $coauthors_plus->guest_authors->get_guest_author_by( 'ID', $guest_author_id );

					if ( ! $guest_author ) {
						WP_CLI::line( 'Failed to load guest author ' . $guest_author_id );

						continue;
					}

					$term = $coauthors_plus->get_author_term( $guest_author );

					if ( empty( $term ) || empty( $term->description ) ) {
						$coauthors_plus->update_author_term( $guest_author );

						WP_CLI::line( "Created author term for Guest Author {$guest_author->user_nicename}" );
					}
				}

				$this->stop_the_insanity();

				$args['paged']++;
				$posts = new WP_Query( $args );
			}
		}

		WP_CLI::success( 'All done' );
	}

	/**
	 * Remove author terms from revisions, which we've been adding since the dawn of time
	 *
	 * @since 3.0.1
	 *
	 * @subcommand remove-terms-from-revisions
	 */
	public function remove_terms_from_revisions() {
		global $wpdb;

		$ids = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type='revision' AND post_status='inherit'" );

		WP_CLI::line( 'Found ' . count( $ids ) . ' revisions to look through' );
		$affected = 0;
		foreach ( $ids as $post_id ) {

			$terms = cap_get_coauthor_terms_for_post( $post_id );
			if ( empty( $terms ) ) {
				continue;
			}

			WP_CLI::line( "#{$post_id}: Removing " . implode( ',', wp_list_pluck( $terms, 'slug' ) ) );
			wp_set_post_terms( $post_id, array(), 'author' );
			$affected++;
		}
		WP_CLI::line( "All done! {$affected} revisions had author terms removed" );
	}

	/**
	 * Subcommand to create guest authors from an author list in a WXR file
	 *
	 * @subcommand create-guest-authors-from-wxr
	 * @synopsis --file=<file>
	 */
	public function create_guest_authors_from_wxr( $args, $assoc_args ) {
		global $coauthors_plus;

		$defaults = array(
			'file' => '',
		);
		$this->args = wp_parse_args( $assoc_args, $defaults );

		if ( empty( $this->args['file'] ) || ! is_readable( $this->args['file'] ) ) {
			WP_CLI::error( 'Please specify a valid WXR file with the --file arg.' );
		}

		if ( ! class_exists( 'WXR_Parser' ) ) {
			require_once( WP_CONTENT_DIR . '/plugins/wordpress-importer/parsers.php' );
		}

		$parser = new WXR_Parser();
		$import_data = $parser->parse( $this->args['file'] );

		if ( is_wp_error( $import_data ) ) {
			WP_CLI::error( 'Failed to read WXR file.' );
		}

		// Get author nodes
		$authors = $import_data['authors'];

		foreach ( $authors as $author ) {
			WP_CLI::line( sprintf( 'Processing author %s (%s)', $author['author_login'], $author['author_email'] ) );

			$guest_author_data = array(
				'display_name' => $author['author_display_name'],
				'user_login' => $author['author_login'],
				'user_email' => $author['author_email'],
				'first_name' => $author['author_first_name'],
				'last_name' => $author['author_last_name'],
				'ID' => $author['author_id'],
			);

			$guest_author_id = $this->create_guest_author( $guest_author_data );
		}

		WP_CLI::line( 'All done!' );
	}

	/**
	 * Subcommand to create guest authors from an author list in a CSV file
	 *
	 * @subcommand create-guest-authors-from-csv
	 * @synopsis --file=<file>
	 */
	public function create_guest_authors_from_csv( $args, $assoc_args ) {
		global $coauthors_plus;

		$defaults = array(
			'file' => '',
		);
		$this->args = wp_parse_args( $assoc_args, $defaults );

		if ( empty( $this->args['file'] ) || ! is_readable( $this->args['file'] ) ) {
			WP_CLI::error( 'Please specify a valid CSV file with the --file arg.' );
		}

		$file = fopen( $this->args['file'], 'r' );

		if ( ! $file ) {
			WP_CLI::error( 'Failed to read file.' );
		}

		$authors = array();

		$row = 0;
		while ( false !== ( $data = fgetcsv( $file ) ) ) {
			if ( 0 === $row ) {
				$field_keys = array_map( 'trim', $data );
				// TODO: bail if required fields not found
			} else {
				$row_data = array_map( 'trim', $data );
				$author_data = array();
				foreach ( (array) $row_data as $col_num => $val ) {
						// Don't use the value of the field key isn't set
					if ( empty( $field_keys[ $col_num ] ) ) {
						continue;
					}
					$author_data[ $field_keys[ $col_num ] ] = $val;
				}

				$authors[] = $author_data;
			}
			$row++;
		}
		fclose( $file );

		WP_CLI::line( 'Found ' . count( $authors ) . ' authors in CSV' );

		foreach ( $authors as $author ) {
			WP_CLI::line( sprintf( 'Processing author %s (%s)', $author['user_login'], $author['user_email'] ) );

			$guest_author_data = array(
				'display_name' => sanitize_text_field( $author['display_name'] ),
				'user_login' => sanitize_user( $author['user_login'] ),
				'user_email' => sanitize_email( $author['user_email'] ),
				'website' => esc_url_raw( $author['website'] ),
				'description' => wp_filter_post_kses( $author['description'] ),
				'avatar' => absint( $author['avatar'] ),
			);

			$display_name_space_pos = strpos( $author['display_name'], ' ' );

			if ( false !== $display_name_space_pos && empty( $author['first_name'] ) && empty( $author['last_name'] ) ) {
				$first_name = substr( $author['display_name'], 0, $display_name_space_pos );
				$last_name = substr( $author['display_name'], ( $display_name_space_pos + 1 ) );

				$guest_author_data['first_name'] = sanitize_text_field( $first_name );
				$guest_author_data['last_name'] = sanitize_text_field( $last_name );
			} elseif ( ! empty( $author['first_name'] ) && ! empty( $author['last_name'] ) ) {
				$guest_author_data['first_name'] = sanitize_text_field( $author['first_name'] );
				$guest_author_data['last_name'] = sanitize_text_field( $author['last_name'] );
			}

			$guest_author_id = $this->create_guest_author( $guest_author_data );
		}

		WP_CLI::line( 'All done!' );
	}

	private function create_guest_author( $author ) {
		global $coauthors_plus;
		$guest_author = $coauthors_plus->guest_authors->get_guest_author_by( 'user_email', $author['user_email'], true );

		if ( ! $guest_author ) {
			$guest_author = $coauthors_plus->guest_authors->get_guest_author_by( 'user_login', $author['user_login'], true );
		}

		if ( ! $guest_author ) {
			WP_CLI::line( '-- Not found; creating profile.' );

			$guest_author_id = $coauthors_plus->guest_authors->create( array(
				'display_name' => $author['display_name'],
				'user_login' => $author['user_login'],
				'user_email' => $author['user_email'],
				'first_name' => $author['first_name'],
				'last_name' => $author['last_name'],
				'website' => $author['website'],
				'description' => $author['description'],
				'avatar' => $author['avatar'],
			) );

			if ( $guest_author_id ) {
				WP_CLI::line( sprintf( '-- Created as guest author #%s', $guest_author_id ) );

				if ( isset( $author['author_id'] ) ) {
					update_post_meta( $guest_author_id, '_original_author_id', $author['ID'] );
				}

				update_post_meta( $guest_author_id, '_original_author_login', $author['user_login'] );
			} else {
				WP_CLI::warning( '-- Failed to create guest author.' );
			}
		} else {
			WP_CLI::line( sprintf( '-- Author already exists (ID #%s); skipping.', $guest_author->ID ) );
		}
	}

	/**
	 * Clear all of the caches for memory management
	 */
	private function stop_the_insanity() {
		global $wpdb, $wp_object_cache;

		$wpdb->queries = array(); // or define( 'WP_IMPORTING', true );

		if ( ! is_object( $wp_object_cache ) ) {
			return;
		}

		$wp_object_cache->group_ops = array();
		$wp_object_cache->stats = array();
		$wp_object_cache->memcache_debug = array();
		$wp_object_cache->cache = array();

		if ( is_callable( $wp_object_cache, '__remoteset' ) ) {
			$wp_object_cache->__remoteset(); // important
		}
	}
}
