<?php
/**
 * KooKoa Folders — Smart Folder (dynamic rules) AJAX handlers.
 *
 * Create a smart folder, update its rules, and read them back for the edit
 * modal. Registered and dispatched by KookoaWPF_Ajax; this collaborator holds
 * the handler bodies plus the rule sanitiser and taxonomy guard they share.
 *
 * @package KookoaWPF
 * @license GPL-2.0-or-later
 */

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

/**
 * AJAX handlers for smart (rule-based) folders.
 */
class KookoaWPF_Ajax_Dynamic 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).
	}

	/**
	 * AJAX: create a smart folder with rules.
	 */
	public function create_dynamic_folder(): void {
		check_ajax_referer( 'kookoawpf_nonce', 'nonce' );
		$this->verify_request();

		$name      = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
		$post_type = self::require_managed_post_type(
			isset( $_POST['post_type'] ) ? sanitize_key( wp_unslash( $_POST['post_type'] ) ) : ''
		);
		$rules_in  = isset( $_POST['rules'] ) ? map_deep( wp_unslash( $_POST['rules'] ), 'sanitize_text_field' ) : array();

		if ( '' === $name || '' === $post_type ) {
			wp_send_json_error( array( 'message' => __( 'Missing required fields.', 'kookoa-folders' ) ) );
		}

		$rules = self::sanitize_rules( $rules_in );
		if ( empty( $rules ) ) {
			wp_send_json_error( array( 'message' => __( 'Add at least one rule.', 'kookoa-folders' ) ) );
		}

		$result = KookoaWPF_Dynamic::create( $name, $post_type, $rules );
		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		}

		// Pre-compute the count so the optimistic node renders the right number.
		if ( ! empty( $result['term_id'] ) ) {
			$result['count'] = KookoaWPF_Dynamic::count_items( (int) $result['term_id'], $post_type );
		}

		wp_send_json_success( $result );
	}

	/**
	 * AJAX: update rules on an existing smart folder.
	 */
	public function update_dynamic_rules(): void {
		check_ajax_referer( 'kookoawpf_nonce', 'nonce' );
		$this->verify_request();

		$folder_id = isset( $_POST['folder_id'] ) ? absint( wp_unslash( $_POST['folder_id'] ) ) : 0;
		$rules_in  = isset( $_POST['rules'] ) ? map_deep( wp_unslash( $_POST['rules'] ), 'sanitize_text_field' ) : array();

		if ( ! $folder_id ) {
			wp_send_json_error( array( 'message' => __( 'Missing folder ID.', 'kookoa-folders' ) ) );
		}
		if ( ! self::folder_belongs_to_kookoawpf( $folder_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Folder not found.', 'kookoa-folders' ) ) );
		}
		if ( ! KookoaWPF_Dynamic::is_dynamic( $folder_id ) ) {
			wp_send_json_error( array( 'message' => __( 'This is not a smart folder.', 'kookoa-folders' ) ) );
		}
		if ( KookoaWPF_Folders::get_meta( $folder_id, 'folder_locked' ) ) {
			wp_send_json_error( array( 'message' => __( 'This folder is locked.', 'kookoa-folders' ) ) );
		}

		KookoaWPF_Dynamic::update_rules( $folder_id, self::sanitize_rules( $rules_in ) );
		wp_send_json_success( array( 'message' => __( 'Smart folder updated.', 'kookoa-folders' ) ) );
	}

	/**
	 * AJAX: read the rules attached to a smart folder (Edit modal).
	 */
	public function get_dynamic_rules(): void {
		check_ajax_referer( 'kookoawpf_nonce', 'nonce' );
		$this->verify_request();

		$folder_id = isset( $_POST['folder_id'] ) ? absint( wp_unslash( $_POST['folder_id'] ) ) : 0;
		if ( ! $folder_id ) {
			wp_send_json_error( array( 'message' => __( 'Missing folder ID.', 'kookoa-folders' ) ) );
		}
		if ( ! self::folder_belongs_to_kookoawpf( $folder_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Folder not found.', 'kookoa-folders' ) ) );
		}

		wp_send_json_success( array( 'rules' => KookoaWPF_Dynamic::get_rules( $folder_id ) ) );
	}

	/**
	 * Confirm a term ID lives in any kookoawpf_folder_* taxonomy. Used by the
	 * smart-folder AJAX handlers, which only carry the folder ID (not a post
	 * type) and need to validate against the full set of folder taxonomies.
	 *
	 * @param int $folder_id Term ID.
	 * @return bool
	 */
	private static function folder_belongs_to_kookoawpf( $folder_id ) {
		$folder_id = absint( $folder_id );
		if ( ! $folder_id ) {
			return false;
		}
		$term = get_term( $folder_id );
		if ( ! $term || is_wp_error( $term ) ) {
			return false;
		}
		return 0 === strpos( $term->taxonomy, 'kookoawpf_folder_' );
	}

	/**
	 * Sanitize a rules array (max 6 {field, value} pairs).
	 *
	 * @param mixed $rules Raw rules input.
	 * @return array<int, array<string, string>>
	 */
	private static function sanitize_rules( $rules ) {
		if ( ! is_array( $rules ) ) {
			return array();
		}
		$clean = array();
		foreach ( $rules as $rule ) {
			if ( ! is_array( $rule ) ) {
				continue;
			}
			$field = isset( $rule['field'] ) ? sanitize_key( $rule['field'] ) : '';
			$value = isset( $rule['value'] ) ? sanitize_text_field( $rule['value'] ) : '';
			if ( '' === $field || '' === $value ) {
				continue;
			}
			$clean[] = array(
				'field' => $field,
				'value' => $value,
			);
		}
		return array_slice( $clean, 0, 6 );
	}
}
