<?php
/**
 * Handles AJAX-based post management actions for the Author Kit plugin.
 *
 * Provides functionality for loading posts, publishing, trashing, restoring,
 * and permanently deleting posts, with permission checks and localized responses.
 *
 * @package Author_Kit
 * @since 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles AJAX-based Post Management actions for the Author Kit plugin.
 *
 * This class provides methods to manage WordPress posts via AJAX requests,
 * including loading posts into custom tables, publishing, trashing,
 * restoring, and permanently deleting posts.
 *
 * The methods ensure proper user capability checks, error handling, and
 * localized responses for seamless frontend integration.
 *
 * @package Author_Kit
 * @since 1.0.0
 */
class Author_Kit_Post_Table {


	/**
	 * Registers AJAX actions for post table operations.
	 *
	 * Binds internal methods to WordPress AJAX hooks for:
	 * - Loading posts
	 * - Getting post counts by status
	 * - Publishing a post
	 * - Trashing posts
	 * - Restoring posts
	 * - Deleting posts permanently
	 *
	 * @return void
	 */
	public function __construct() {
		add_action( 'wp_ajax_author_kit_load_posts', array( $this, 'load_post_table' ) );
		add_action( 'wp_ajax_author_kit_get_total_posts_by_status', array( $this, 'get_total_posts_by_status' ) );
		add_action( 'wp_ajax_author_kit_publish_post', array( $this, 'publish_post' ) );
		add_action( 'wp_ajax_author_kit_trash_posts', array( $this, 'handle_trash_posts' ) );
		add_action( 'wp_ajax_author_kit_restore_posts', array( $this, 'handle_restore_posts' ) );
		add_action( 'wp_ajax_author_kit_delete_posts_permanently', array( $this, 'handle_delete_posts_permanently' ) );
	}

	/**
	 * Handles AJAX request to return post counts by status.
	 *
	 * Retrieves total post counts across all non-internal statuses,
	 * adds current user's post count ("mine"), and includes "all" and "trash".
	 * Returns the result as a JSON success response.
	 *
	 * @return void Outputs JSON response with post counts.
	 */
	public function get_total_posts_by_status() {
		check_ajax_referer( 'post-table-loader-nonce', 'nonce' );

		// Get all registered post statuses (excluding internal statuses).
		$all_statuses = get_post_stati( array( 'internal' => false ) );

		// Explicitly (precautionary) exclude unwanted internal statuses.
		$excluded_statuses = array( 'auto-draft', 'inherit', 'trash' );
		$post_statuses     = array_diff( $all_statuses, $excluded_statuses );

		$post_counts       = array();
		$total_posts_all   = 0;
		$trash_posts_count = 0;

		// Get all post counts at once.
		$all_post_counts = wp_count_posts();

		foreach ( $post_statuses as $status ) {
			$count = $all_post_counts->$status ?? 0;

			$total_posts_all += $count;

			if ( $count > 0 ) {
				$post_counts[ $status ] = $count;
			}
		}

		// Get current user ID.
		$current_user_id  = get_current_user_id();
		$mine_posts_count = 0;

		if ( $current_user_id ) {
			// Single WP_Query for all "mine" posts across all statuses.
			$mine_query = new WP_Query(
				array(
					'post_type'      => 'post',
					'post_status'    => $post_statuses, // Get all statuses at once.
					'author'         => $current_user_id,
					'posts_per_page' => -1,
					'fields'         => 'ids', // Only get IDs for efficiency.
				)
			);

			$mine_posts_count = $mine_query->found_posts;
			wp_reset_postdata();
		}

		// Add "mine" count if applicable.
		if ( $mine_posts_count > 0 ) {
			$post_counts = array_merge( array( 'mine' => $mine_posts_count ), $post_counts );
		}

		// Add "all" (excluding trash).
		if ( $total_posts_all > 0 ) {
			$post_counts = array_merge( array( 'all' => $total_posts_all ), $post_counts );
		}

		$trash_posts_count = wp_count_posts()->trash;
		// Add "trash" separately.
		if ( $trash_posts_count > 0 ) {
			$post_counts['trash'] = $trash_posts_count;
		}

		wp_send_json_success( $post_counts );
	}

	/**
	 * Handles AJAX request to load paginated post table data.
	 *
	 * Applies filters for search, sorting, status, and author.
	 * Returns post details and pagination info as JSON.
	 *
	 * @return void Outputs JSON response with posts and pagination data.
	 */
	public function load_post_table() {
		check_ajax_referer( 'post-table-loader-nonce', 'nonce' );

		$post_limit        = isset( $_POST['limit'] ) ? intval( $_POST['limit'] ) : 10;
		$current_page      = isset( $_POST['pageId'] ) || -1 !== $post_limit ? intval( $_POST['pageId'] ) : 1;
		$search_value      = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '';
		$order_by          = isset( $_POST['order_by'] ) ? sanitize_text_field( wp_unslash( $_POST['order_by'] ) ) : 'date';
		$order_direction   = isset( $_POST['order_direction'] ) ? sanitize_text_field( wp_unslash( $_POST['order_direction'] ) ) : 'DESC';
		$post_status_value = isset( $_POST['postStatus'] ) ? sanitize_text_field( wp_unslash( $_POST['postStatus'] ) ) : '';

		$current_user_id = '';

		// Get all public post statuses dynamically.
		$public_statuses = get_post_stati( array( 'internal' => false ) );
		$all_statuses    = array_diff( $public_statuses, array( 'auto-draft', 'inherit', 'trash' ) );

		// Define the status based on the input.
		if ( 'all' === $post_status_value || 'All' === $post_status_value ) {
			$post_status_value = $all_statuses;
		} elseif ( 'mine' === $post_status_value ) {
			$post_status_value = $all_statuses;
			$current_user_id   = get_current_user_id();
		} else {
			$post_status_value = array( $post_status_value );
		}

		// Query args (fetch posts + total count efficiently).
		$args = array(
			'post_type'      => 'post',
			'post_status'    => $post_status_value,
			'posts_per_page' => $post_limit,
			'paged'          => $current_page,
			'orderby'        => $order_by,
			'order'          => $order_direction,
			's'              => $search_value,
			'fields'         => 'all', // Fetch full post objects.
		);

		if ( ! empty( $current_user_id ) ) {
			$args['author'] = $current_user_id;
		}

		$query = new WP_Query( $args );

		// Efficiently get total matching posts count.
		$total_posts = $query->found_posts;

		$post_data    = array();
		$total_pages  = -1 !== $post_limit ? ceil( $total_posts / $post_limit ) : min( 1, 1 * $total_posts );
		$start_index  = -1 !== $post_limit ? min( ( $current_page - 1 ) * $post_limit + 1, $total_posts ) : min( 1, 1 * $total_posts );
		$ending_index = -1 !== $post_limit ? min( $current_page * $post_limit, $total_posts ) : $total_posts;

		$current_user_id = get_current_user_id();

		$author_kit_coauthors = new Author_Kit_Coauthors();

		foreach ( $query->posts as $post ) {
			$post_status   = get_post_status( $post->ID );
			$was_published = 'trash' === $post_status && get_post_meta( $post->ID, '_wp_trash_meta_status', true ) === 'publish' ? true : false;
			$author_id     = $post->post_author;
			$author_name   = get_the_author_meta( 'display_name', $author_id );
			$author_avatar = get_avatar_url( intval( $author_id ) );

			// Fetch coauthors dynamically (replace with your logic).
			$get_post_coauthors = $author_kit_coauthors->get_coauthors_by_post( $post->ID );
			$coauthors          = $get_post_coauthors ? $get_post_coauthors : array();
			$coauthors_data     = array();

			foreach ( $coauthors as $coauthor_id ) {
				$coauthor_name    = get_the_author_meta( 'display_name', $coauthor_id );
				$coauthor_avatar  = get_avatar_url( intval( $coauthor_id ) );
				$coauthors_data[] = array(
					'name'   => $coauthor_name,
					'avatar' => $coauthor_avatar,
				);
			}

			$default_image  = AUTHOR_KIT_PLUGIN_URL . 'admin/assets/files/default_post_thumbnail.png';
			$thumbnail_url  = get_the_post_thumbnail_url( $post->ID );
			$featured_image = $thumbnail_url ? $thumbnail_url : $default_image;
			$post_url       = get_permalink( $post->ID );

			$post_data[] = array(
				'ID'              => $post->ID,
				'postTitle'       => get_the_title( $post->ID ),
				'author_id'       => $author_id,
				'current_user_id' => $current_user_id,
				'author_name'     => $author_name,
				'coauthors'       => $coauthors_data,
				'avatar'          => $author_avatar,
				'status'          => ucfirst( $post_status ),
				'was_published'   => $was_published,
				'postDate'        => get_the_date( 'j F Y', $post->ID ) . ' ' . get_the_time( 'g:i A', $post->ID ),
				'postThumbnail'   => $featured_image,
				'url'             => $post_url,
				'nonce'           => array(
					'trash'   => wp_create_nonce( 'trash-post_' . $post->ID ),
					'untrash' => wp_create_nonce( 'untrash-post_' . $post->ID ),
					'delete'  => wp_create_nonce( 'delete-post_' . $post->ID ),
				),
			);
		}

		$pagination_data = array(
			'total_posts'  => $total_posts,
			'total_pages'  => $total_pages,
			'current_page' => $current_page,
			'start_index'  => $start_index,
			'ending_index' => $ending_index,
		);

		wp_send_json(
			array(
				'pagination_data' => $pagination_data,
				'post_data'       => $post_data,
			)
		);
	}

	/**
	 * Handles AJAX request to publish a post or submit it for review.
	 *
	 * Validates the post ID and user capabilities. If the user can publish,
	 * the post is published. Otherwise, it is submitted for review if editable.
	 *
	 * @return void Outputs JSON success or error message.
	 */
	public function publish_post() {
		check_ajax_referer( 'post-table-loader-nonce', 'nonce' );

		// Step 1: Validate Post ID.
		if ( ! isset( $_POST['post_id'] ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid Request.', 'author-kit' ) ) );
			return;
		}

		$post_id = intval( $_POST['post_id'] );
		if ( $post_id <= 0 ) {
			wp_send_json_error( array( 'message' => __( 'Invalid Post ID.', 'author-kit' ) ) );
			return;
		}

		// Step 2: Get Post Data.
		$post = get_post( $post_id );
		if ( ! $post ) {
			wp_send_json_error( array( 'message' => __( 'Post Not Found.', 'author-kit' ) ) );
			return;
		}

		$current_user_id = get_current_user_id();
		$post_author     = intval( $post->post_author );

		// Step 3: Check if User Can Publish.
		if ( current_user_can( 'publish_posts' ) ) {
			// If the user can publish posts, check if they own this post.
			if ( $post_author === $current_user_id || current_user_can( 'edit_others_posts' ) ) {
				wp_publish_post( $post_id );
				wp_send_json_success( array( 'message' => __( 'Post Published Successfully.', 'author-kit' ) ) );
			} else {
				wp_send_json_error( array( 'message' => __( 'Permission Denied: Cannot Publish Post', 'author-kit' ) ) );
			}
		} elseif ( current_user_can( 'edit_posts' ) ) {
			// Step 4: If the user can edit but not publish, submit for review.
			$post_data = array(
				'ID'          => $post_id,
				'post_status' => 'pending',
			);
			wp_update_post( $post_data );
			wp_send_json_success( array( 'message' => __( 'Post Submitted for Review.', 'author-kit' ) ) );
		} else {
			// Step 5: If the user cannot edit posts at all, deny access.
			wp_send_json_error( array( 'message' => __( 'Permission Denied: Cannot Publish Post', 'author-kit' ) ) );
		}
	}

	/**
	 * Handles AJAX request to move one or more posts to trash.
	 *
	 * Validates post IDs and user permissions. Responds with success or partial error.
	 *
	 * @return void Outputs JSON response with message and status.
	 */
	public function handle_trash_posts() {
		check_ajax_referer( 'post-table-loader-nonce', 'nonce' );

		// Decode and sanitize the JSON-encoded array of post IDs.
		$post_ids = isset( $_POST['post_ids'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['post_ids'] ) ), true ) : array();

		if ( ! is_array( $post_ids ) || empty( $post_ids ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid or Empty Post IDs.', 'author-kit' ) ) );
			return;
		}

		$trashed_posts   = array();
		$errors          = array();
		$current_user_id = get_current_user_id();

		foreach ( $post_ids as $post_id ) {
			$post_id     = intval( $post_id );
			$post        = get_post( $post_id );
			$post_author = intval( $post->post_author );

			if ( ! $post ) {
				$errors[] = sprintf(
					// translators: %d is the post ID.
					__( 'Post not found (ID: %d)', 'author-kit' ),
					$post_id
				);
				continue;
			}

			// Determine if user can delete this post.
			$can_delete = false;

			// If user owns the post.
			if ( $post_author === $current_user_id ) {
				if (
					( 'publish' === $post->post_status && current_user_can( 'delete_published_posts' ) ) ||
					( 'publish' !== $post->post_status && current_user_can( 'delete_posts' ) )
				) {
					$can_delete = true;
				}
			} elseif ( current_user_can( 'delete_others_posts' ) ) {
				// If user is an Editor/Admin with permission to delete others' posts.
				if ( 'publish' === $post->post_status && current_user_can( 'delete_published_posts' ) ) {
					$can_delete = true;
				} elseif ( 'private' === $post->post_status && current_user_can( 'delete_private_posts' ) ) {
					$can_delete = true;
				} elseif ( ! in_array( $post->post_status, array( 'publish', 'private' ), true ) ) {
					$can_delete = true;
				}
			}

			if ( $can_delete ) {
				$result = wp_trash_post( $post_id );

				if ( $result ) {
					$trashed_posts[] = $post_id;
				} else {
					$errors[] = sprintf(
						// Translators: %d is the post ID of the post that failed to be trashed.
						__( 'Failed to trash post (ID: %d)', 'author-kit' ),
						$post_id
					);
				}
			} else {
				$errors[] = sprintf(
					// Translators: %d is the post ID the user does not have permission to trash.
					__( 'You do not have permission to trash post (ID: %d)', 'author-kit' ),
					$post_id
				);
			}
		}

		// Determine the response message based on success/failure.
		if ( empty( $trashed_posts ) ) {
			$message = __( 'No Posts Trashed', 'author-kit' );
			wp_send_json_error( array( 'message' => $message ) );
		} elseif ( empty( $errors ) ) {
			$message = ( count( $trashed_posts ) === 1 )
				? __( 'Post Trashed Successfully.', 'author-kit' )
				: __( 'All Posts Trashed Successfully', 'author-kit' );
			wp_send_json_success( array( 'message' => $message ) );
		} else {
			$message = __( 'Some Posts Trashed, Others Failed', 'author-kit' );
			wp_send_json_success( array( 'message' => $message ) );
		}
	}

	/**
	 * Handles AJAX request to restore one or more trashed posts.
	 *
	 * Validates post IDs, checks user permissions, and attempts to untrash posts.
	 * Responds with success, partial success, or failure messages.
	 *
	 * @return void Outputs JSON response with result message.
	 */
	public function handle_restore_posts() {
		// Verify nonce for security.
		check_ajax_referer( 'post-table-loader-nonce', 'nonce' );

		// Decode and sanitize JSON-encoded array of post IDs.
		$post_ids = isset( $_POST['post_ids'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['post_ids'] ) ), true ) : array();

		if ( ! is_array( $post_ids ) || empty( $post_ids ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid or Empty Post IDs.', 'author-kit' ) ) );
			return;
		}

		$restored_posts  = array();
		$errors          = array();
		$current_user_id = get_current_user_id();

		foreach ( $post_ids as $post_id ) {
			$post_id     = intval( $post_id );
			$post        = get_post( $post_id );
			$post_author = intval( $post->post_author );

			if ( ! $post ) {
				$errors[] = sprintf(
					// translators: %d is the post ID.
					__( 'Post not found (ID: %d)', 'author-kit' ),
					$post_id
				);
				continue;
			}

			// Get original status before trash (Was it published before?).
			$previous_status = get_post_meta( $post_id, '_wp_trash_meta_status', true );

			// Check capabilities: Who can restore?
			$can_restore = false;

			if ( $post_author === $current_user_id ) {
				if ( 'publish' === $previous_status && current_user_can( 'delete_published_posts' ) ) {
					$can_restore = true;
				} elseif ( current_user_can( 'delete_posts' ) ) {
					$can_restore = true;
				}
			} elseif ( current_user_can( 'delete_others_posts' ) ) {
				if ( 'publish' === $previous_status && current_user_can( 'delete_published_posts' ) ) {
					$can_restore = true;
				} elseif ( 'publish' !== $previous_status ) {
					$can_restore = true;
				}
			}

			if ( $can_restore ) {
				$result = wp_untrash_post( $post_id );

				if ( $result ) {
					$restored_posts[] = $post_id;
				} else {
					$errors[] = sprintf(
						/* translators: %d is the ID of the post that failed to restore. */
						__( 'Failed to restore post (ID: %d)', 'author-kit' ),
						$post_id
					);
				}
			} else {
				$errors[] = sprintf(
					/* translators: %d is the ID of the post the user doesn't have permission to restore. */
					__( 'You do not have permission to restore post (ID: %d)', 'author-kit' ),
					$post_id
				);
			}
		}

		// Determine the response message based on success/failure.
		if ( empty( $restored_posts ) ) {
			$message = __( 'No Posts Restored.', 'author-kit' );
			wp_send_json_error( array( 'message' => $message ) );
		} elseif ( empty( $errors ) ) {
			$message = ( count( $restored_posts ) === 1 )
				? __( 'Post Restored Successfully.', 'author-kit' )
				: __( 'All Posts Restored Successfully.', 'author-kit' );
			wp_send_json_success( array( 'message' => $message ) );
		} else {
			$message = __( 'Some Posts Restored, Others Failed', 'author-kit' );
			wp_send_json_success( array( 'message' => $message ) );
		}
	}


	/**
	 * Handles AJAX request to permanently delete one or more trashed posts.
	 *
	 * Validates post IDs, checks user permissions, and deletes posts bypassing trash.
	 * Responds with success, partial success, or failure messages.
	 *
	 * @return void Outputs JSON response with result message.
	 */
	public function handle_delete_posts_permanently() {
		// Verify nonce for security.
		check_ajax_referer( 'post-table-loader-nonce', 'nonce' );

		// Decode and sanitize JSON-encoded array of post IDs.
		$post_ids = isset( $_POST['post_ids'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['post_ids'] ) ), true ) : array();

		if ( ! is_array( $post_ids ) || empty( $post_ids ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid or Empty Post IDs.', 'author-kit' ) ) );
			return;
		}

		$deleted_posts   = array();
		$errors          = array();
		$current_user_id = get_current_user_id();

		foreach ( $post_ids as $post_id ) {
			$post_id     = intval( $post_id );
			$post        = get_post( $post_id );
			$post_author = intval( $post->post_author );

			if ( ! $post ) {
				$errors[] = sprintf(
					// translators: %d is the post ID.
					__( 'Post not found (ID: %d)', 'author-kit' ),
					$post_id
				);
				continue;
			}

			// Get original status before trash (Was it published before?).
			$previous_status = get_post_meta( $post_id, '_wp_trash_meta_status', true );

			// Check capabilities: Who can delete permanently?
			$can_delete = false;

			if ( $post_author === $current_user_id ) {
				if ( 'publish' === $previous_status && current_user_can( 'delete_published_posts' ) ) {
					$can_delete = true;
				} elseif ( current_user_can( 'delete_posts' ) ) {
					$can_delete = true;
				}
			} elseif ( current_user_can( 'delete_others_posts' ) ) {
				if ( 'publish' === $previous_status && current_user_can( 'delete_published_posts' ) ) {
					$can_delete = true;
				} elseif ( 'publish' !== $previous_status ) {
					$can_delete = true;
				}
			}

			if ( $can_delete ) {
				$result = wp_delete_post( $post_id, true ); // `true` forces permanent deletion

				if ( $result ) {
					$deleted_posts[] = $post_id;
				} else {
					$errors[] = sprintf(
						/* translators: %d is the ID of the post that failed to be deleted. */
						__( 'Failed to delete post (ID: %d)', 'author-kit' ),
						$post_id
					);
				}
			} else {
				$errors[] = sprintf(
					/* translators: %d is the ID of the post the user does not have permission to delete. */
					__( 'You do not have permission to delete post (ID: %d)', 'author-kit' ),
					$post_id
				);
			}
		}

		// Determine the response message based on success/failure.
		if ( empty( $deleted_posts ) ) {
			$message = __( 'No Posts Deleted', 'author-kit' );
			wp_send_json_error( array( 'message' => $message ) );
		} elseif ( empty( $errors ) ) {
			$message = ( count( $deleted_posts ) === 1 )
				? __( 'Post Deleted Successfully', 'author-kit' )
				: __( 'All Posts Deleted Successfully', 'author-kit' );
			wp_send_json_success( array( 'message' => $message ) );
		} else {
			$message = __( 'Some Posts Deleted, Others Failed', 'author-kit' );
			wp_send_json_success( array( 'message' => $message ) );
		}
	}
}


// Initialize the class.
new Author_Kit_Post_Table();
