<?php
/**
 * WPFAQBlock Data
 *
 * Responsible for all database read operations:
 * - Fetching FAQ posts (wpfaqblock CPT)
 * - Fetching FAQ Categories (faq_group taxonomy terms)
 * - Grouping FAQs by their assigned term
 *
 * @package WPFAQBlock
 * @since   2.0.0
 */

// Prevent direct file access.
if (!defined('ABSPATH')) {
	exit;
}

/**
 * Class WPFAQBlock_Data
 *
 * Pure data-access class. No output, no hooks — only returns structured data.
 * Consumed by admin templates and (later) the shortcode renderer.
 */
class WPFAQBlock_Data
{

	// ──────────────────────────────────────────────
	// Groups (faq_group taxonomy)
	// ──────────────────────────────────────────────

	/**
	 * Return all faq_group terms ordered by term_order meta (drag-and-drop)
	 * then alphabetically as a fallback.
	 *
	 * @since 2.0.0
	 *
	 * @return WP_Term[]  Array of WP_Term objects. Empty array if none exist.
	 */
	public function get_groups(): array
	{
		$terms = get_terms(
			array(
				'taxonomy' => WPFAQBlock_Taxonomy::SLUG,
				'hide_empty' => false,
				'orderby' => 'meta_value_num',
				'order' => 'ASC',
				'meta_query' => array(  // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
					'relation' => 'OR',
					array(
						'key' => '_wpfaqblock_term_order',
						'compare' => 'EXISTS',
					),
					array(
						'key' => '_wpfaqblock_term_order',
						'compare' => 'NOT EXISTS',
					),
				),
			)
		);

		if (is_wp_error($terms)) {
			return array();
		}

		// Sort: terms with order meta first (ASC), ungrouped appended at end.
		usort(
			$terms,
			function (WP_Term $a, WP_Term $b): int {
				$order_a = (int) get_term_meta($a->term_id, '_wpfaqblock_term_order', true);
				$order_b = (int) get_term_meta($b->term_id, '_wpfaqblock_term_order', true);

				if ($order_a === $order_b) {
					return strnatcasecmp($a->name, $b->name);
				}
				return $order_a <=> $order_b;
			}
		);

		return $terms;
	}

	// ──────────────────────────────────────────────
	// FAQs (wpfaqblock CPT)
	// ──────────────────────────────────────────────

	/**
	 * Fetch all FAQ posts for a given group term, ordered by menu_order.
	 *
	 * @since 2.0.0
	 *
	 * @param int $term_id  faq_group term ID. Pass 0 to fetch un-grouped FAQs.
	 * @return WP_Post[]    Ordered array of WP_Post objects.
	 */
	public function get_faqs_by_group(int $term_id): array
	{
		$args = array(
			'post_type' => WPFAQBlock_CPT::SLUG,
			'post_status' => array('publish', 'draft'),
			'posts_per_page' => -1,
			'orderby' => 'menu_order',
			'order' => 'ASC',
			'no_found_rows' => true,   // Performance: skip pagination count.
		);

		if ($term_id > 0) {
			$args['tax_query'] = array(  // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
				array(
					'taxonomy' => WPFAQBlock_Taxonomy::SLUG,
					'field' => 'term_id',
					'terms' => $term_id,
				),
			);
		} else {
			// Un-grouped: FAQs not assigned to any faq_group term.
			$args['tax_query'] = array(  // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
				array(
					'taxonomy' => WPFAQBlock_Taxonomy::SLUG,
					'operator' => 'NOT EXISTS',
				),
			);
		}

		$query = new WP_Query($args);

		return $query->posts;
	}

	/**
	 * Fetch published FAQ posts by exact IDs, preserving the selected order.
	 *
	 * @since 2.0.0
	 *
	 * @param int[] $post_ids FAQ post IDs.
	 * @return WP_Post[] Ordered array of WP_Post objects.
	 */
	public function get_faqs_by_ids(array $post_ids): array
	{
		$post_ids = array_values(array_unique(array_filter(array_map('absint', $post_ids))));

		if (empty($post_ids)) {
			return array();
		}

		$query = new WP_Query(
			array(
				'post_type' => WPFAQBlock_CPT::SLUG,
				'post_status' => 'publish',
				'post__in' => $post_ids,
				'orderby' => 'post__in',
				'posts_per_page' => count($post_ids),
				'no_found_rows' => true,
			)
		);

		return $query->posts;
	}

	/**
	 * Fetch every FAQ post grouped by faq_group term.
	 *
	 * Returns an array of group objects with their child FAQ posts:
	 *
	 * [
	 *   [
	 *     'term' => WP_Term|null,   // null for ungrouped
	 *     'faqs' => WP_Post[],
	 *   ],
	 *   …
	 * ]
	 *
	 * Groups with zero FAQs are still included (so empty groups show in the builder).
	 *
	 * @since 2.0.0
	 *
	 * @return array<int, array{term: WP_Term|null, faqs: WP_Post[]}>
	 */
	public function get_grouped_faqs(): array
	{
		$groups = $this->get_groups();
		$result = array();

		foreach ($groups as $term) {
			$result[] = array(
				'term' => $term,
				'faqs' => $this->get_faqs_by_group($term->term_id),
			);
		}

		// Also collect un-grouped FAQs.
		$ungrouped = $this->get_faqs_by_group(0);
		if (!empty($ungrouped)) {
			$result[] = array(
				'term' => null,
				'faqs' => $ungrouped,
			);
		}

		return $result;
	}

	// ──────────────────────────────────────────────
	// Single FAQ helpers
	// ──────────────────────────────────────────────

	/**
	 * Get a single FAQ post by ID.
	 *
	 * @since 2.0.0
	 *
	 * @param int $post_id  Post ID.
	 * @return WP_Post|null WP_Post if found and correct type, null otherwise.
	 */
	public function get_faq(int $post_id): ?WP_Post
	{
		$post = get_post($post_id);

		if (!$post instanceof WP_Post) {
			return null;
		}

		if (WPFAQBlock_CPT::SLUG !== $post->post_type) {
			return null;
		}

		return $post;
	}

	/**
	 * Get the faq_group term assigned to a FAQ post.
	 *
	 * @since 2.0.0
	 *
	 * @param int $post_id  Post ID.
	 * @return WP_Term|null First assigned term or null.
	 */
	public function get_faq_group(int $post_id): ?WP_Term
	{
		$terms = get_the_terms($post_id, WPFAQBlock_Taxonomy::SLUG);

		if (empty($terms) || is_wp_error($terms)) {
			return null;
		}

		return $terms[0];
	}

	/**
	 * Check whether a FAQ is "active" (published).
	 *
	 * @since 2.0.0
	 *
	 * @param WP_Post $post  FAQ post object.
	 * @return bool
	 */
	public function is_faq_active(WP_Post $post): bool
	{
		return 'publish' === $post->post_status;
	}

	// ──────────────────────────────────────────────
	// Groups dropdown helper (for edit panel select)
	// ──────────────────────────────────────────────

	/**
	 * Return a flat array of group data suitable for a <select> dropdown.
	 *
	 * @since 2.0.0
	 *
	 * @return array<int, array{id: int, name: string}>
	 */
	public function get_groups_for_select(): array
	{
		$terms = $this->get_groups();
		$result = array();

		foreach ($terms as $term) {
			$result[] = array(
				'id' => $term->term_id,
				'name' => $term->name,
			);
		}

		return $result;
	}
}
