<?php
/**
 * KooKoa Folders — Items AJAX handlers.
 *
 * Move, assign and unassign items (attachments or posts) to/from folders.
 * Registered and dispatched by KookoaWPF_Ajax; this collaborator holds the
 * handler bodies and the per-post capability filter they share.
 *
 * @package KookoaWPF
 * @license GPL-2.0-or-later
 */

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

/**
 * AJAX handlers for assigning items to and from folders.
 */
class KookoaWPF_Ajax_Items extends KookoaWPF_Ajax_Handler {

	/**
	 * Singleton instance.
	 *
	 * @var self|null
	 */
	private static $instance = null;

	/**
	 * Return the singleton instance.
	 *
	 * @return self
	 */
	public static function instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor. The action map lives on the KookoaWPF_Ajax dispatcher, so
	 * this collaborator registers nothing of its own.
	 */
	private function __construct() {
		// Registration is owned by KookoaWPF_Ajax (the dispatch surface).
	}

	/**
	 * Move items to a folder
	 */
	public function move_items(): void {
		check_ajax_referer( 'kookoawpf_nonce', 'nonce' );
		$this->verify_request();

		$post_ids  = isset( $_POST['post_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['post_ids'] ) ) : array();
		$from_id   = isset( $_POST['from_folder_id'] ) ? absint( wp_unslash( $_POST['from_folder_id'] ) ) : 0;
		$to_id     = isset( $_POST['to_folder_id'] ) ? absint( wp_unslash( $_POST['to_folder_id'] ) ) : 0;
		$post_type = self::require_managed_post_type(
			isset( $_POST['post_type'] ) ? sanitize_key( wp_unslash( $_POST['post_type'] ) ) : ''
		);

		if ( empty( $post_ids ) || empty( $post_type ) ) {
			wp_send_json_error( array( 'message' => __( 'Missing data.', 'kookoa-folders' ) ) );
		}

		// Per-post capability filter: silently drop ids the caller cannot edit.
		$post_ids = self::filter_editable_post_ids( $post_ids );
		if ( empty( $post_ids ) ) {
			wp_send_json_error( array( 'message' => __( 'You do not have permission to edit the selected items.', 'kookoa-folders' ) ) );
		}

		// Scope sibling folder ids to the caller's taxonomy.
		if ( $from_id > 0 && ! self::folder_in_taxonomy( $from_id, $post_type ) ) {
			wp_send_json_error( array( 'message' => __( 'Source folder not found.', 'kookoa-folders' ) ) );
		}
		if ( $to_id > 0 && ! self::folder_in_taxonomy( $to_id, $post_type ) ) {
			wp_send_json_error( array( 'message' => __( 'Destination folder not found.', 'kookoa-folders' ) ) );
		}

		KookoaWPF_Folders::move_items( $post_ids, $from_id, $to_id, $post_type );

		set_transient( 'kookoawpf_tree_dirty_' . $post_type, 1, 30 );
		wp_send_json_success( array( 'message' => __( 'Items moved.', 'kookoa-folders' ) ) );
	}

	/**
	 * Assign items to folder
	 */
	public function assign_items(): void {
		check_ajax_referer( 'kookoawpf_nonce', 'nonce' );
		$this->verify_request();

		$post_ids  = isset( $_POST['post_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['post_ids'] ) ) : array();
		$folder_id = isset( $_POST['folder_id'] ) ? absint( wp_unslash( $_POST['folder_id'] ) ) : 0;
		$post_type = self::require_managed_post_type(
			isset( $_POST['post_type'] ) ? sanitize_key( wp_unslash( $_POST['post_type'] ) ) : ''
		);

		if ( empty( $post_ids ) || ! $folder_id || empty( $post_type ) ) {
			wp_send_json_error( array( 'message' => __( 'Missing data.', 'kookoa-folders' ) ) );
		}

		// Per-post capability filter.
		$post_ids = self::filter_editable_post_ids( $post_ids );
		if ( empty( $post_ids ) ) {
			wp_send_json_error( array( 'message' => __( 'You do not have permission to edit the selected items.', 'kookoa-folders' ) ) );
		}

		// Verify the target folder is in the caller's taxonomy.
		if ( ! self::folder_in_taxonomy( $folder_id, $post_type ) ) {
			wp_send_json_error( array( 'message' => __( 'Folder not found.', 'kookoa-folders' ) ) );
		}

		KookoaWPF_Folders::assign_items( $post_ids, $folder_id, $post_type );

		set_transient( 'kookoawpf_tree_dirty_' . $post_type, 1, 30 );
		wp_send_json_success( array( 'message' => __( 'Items assigned.', 'kookoa-folders' ) ) );
	}

	/**
	 * Unassign items from folder
	 */
	public function unassign_items(): void {
		check_ajax_referer( 'kookoawpf_nonce', 'nonce' );
		$this->verify_request();

		$post_ids  = isset( $_POST['post_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['post_ids'] ) ) : array();
		$folder_id = isset( $_POST['folder_id'] ) ? absint( wp_unslash( $_POST['folder_id'] ) ) : 0;
		$post_type = self::require_managed_post_type(
			isset( $_POST['post_type'] ) ? sanitize_key( wp_unslash( $_POST['post_type'] ) ) : ''
		);

		if ( empty( $post_ids ) || empty( $post_type ) ) {
			wp_send_json_error( array( 'message' => __( 'Missing data.', 'kookoa-folders' ) ) );
		}

		// Per-post capability filter.
		$post_ids = self::filter_editable_post_ids( $post_ids );
		if ( empty( $post_ids ) ) {
			wp_send_json_error( array( 'message' => __( 'You do not have permission to edit the selected items.', 'kookoa-folders' ) ) );
		}

		// folder_id=0 is a valid sentinel ("unassign from all folders in this
		// post type's taxonomy") used when dropping on Uncategorized from the
		// All view. For any non-zero value, the folder must live in the caller's taxonomy.
		if ( $folder_id && ! self::folder_in_taxonomy( $folder_id, $post_type ) ) {
			wp_send_json_error( array( 'message' => __( 'Folder not found.', 'kookoa-folders' ) ) );
		}

		KookoaWPF_Folders::unassign_items( $post_ids, $folder_id, $post_type );

		set_transient( 'kookoawpf_tree_dirty_' . $post_type, 1, 30 );
		wp_send_json_success( array( 'message' => __( 'Items removed from folder.', 'kookoa-folders' ) ) );
	}

	/**
	 * Filter an array of post IDs, keeping only those the current user can edit.
	 *
	 * @param int[] $post_ids Array of post IDs.
	 * @return int[] Re-indexed array of editable post IDs.
	 */
	private static function filter_editable_post_ids( $post_ids ) {
		return array_values(
			array_filter(
				$post_ids,
				function ( $pid ) {
					$pid = (int) $pid;
					return $pid && current_user_can( 'edit_post', $pid );
				}
			)
		);
	}
}
