<?php
/**
 * Interest Subscription Manager
 *
 * Handles user subscriptions to WordPress categories and tags.
 *
 * @package Subs_Sys
 */

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

if ( ! class_exists( 'Subs_Sys_Interest_Subscription' ) ) {

	class Subs_Sys_Interest_Subscription {

		const TABLE = 'subscription_interest_subscriptions';

		/**
		 * Initialize hooks.
		 */
		public function __construct() {
			add_action( 'publish_post', array( $this, 'handle_new_post' ), 20, 2 );
			add_action( 'publish_page', array( $this, 'handle_new_post' ), 20, 2 );
		}

		/**
		 * Get the table name with prefix.
		 *
		 * @return string
		 */
		public static function table_name() {
			global $wpdb;
			return $wpdb->prefix . self::TABLE;
		}

		/**
		 * Check if the interest subscriptions table exists.
		 *
		 * @return bool
		 */
		public static function table_exists() {
			global $wpdb;
			$table = self::table_name();
			return $wpdb->get_var( "SHOW TABLES LIKE '$table'" ) === $table;
		}

		/**
		 * Free plan limits from settings.
		 *
		 * @return array{max_categories: int, max_tags: int}
		 */
		public static function get_free_limits() {
			return array(
				'max_categories' => max( 1, (int) get_option( 'subs_sys_interest_free_max_categories', 2 ) ),
				'max_tags'       => max( 1, (int) get_option( 'subs_sys_interest_free_max_tags', 3 ) ),
			);
		}

		/**
		 * Paid plan price from settings.
		 *
		 * @return float
		 */
		public static function get_paid_price() {
			return (float) get_option( 'subs_sys_interest_paid_price', 9.99 );
		}

		/**
		 * Load active subscription plans from admin for the popup modal.
		 *
		 * @return array
		 */
		public static function get_modal_plans() {
			global $wpdb;

			$table = $wpdb->prefix . 'subscription_plans';
			if ( $wpdb->get_var( "SHOW TABLES LIKE '$table'" ) !== $table ) {
				return array();
			}

			$plans = $wpdb->get_results(
				"SELECT * FROM $table WHERE status = 'active' ORDER BY price ASC, plan_name ASC"
			);

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

			$free_limits = self::get_free_limits();
			$formatted   = array();

			foreach ( $plans as $plan ) {
				$price   = (float) $plan->price;
				$is_free = $price <= 0;
				$limits  = self::get_plan_limits( $plan, $free_limits );

				$formatted[] = array(
					'id'             => (int) $plan->id,
					'name'           => $plan->plan_name,
					'price'          => $price,
					'price_label'    => self::format_plan_price_label( $plan ),
					'duration_label' => self::format_plan_duration_label( $plan ),
					'features'       => self::parse_plan_features( $plan ),
					'is_free'        => $is_free,
					'max_categories' => $limits['max_categories'],
					'max_tags'       => $limits['max_tags'],
				);
			}

			return apply_filters( 'subs_sys_interest_modal_plans', $formatted, $plans );
		}

		/**
		 * Load content plans for the modal, filtered strictly to the current post's categories/tags.
		 * Only plans that explicitly cover at least one of the post's categories or tags are returned.
		 * On non-singular pages (no context), falls back to all active free plans.
		 *
		 * @param int[] $category_ids Current post category IDs.
		 * @param int[] $tag_ids      Current post tag IDs.
		 * @return array Formatted plan list in the same shape as get_modal_plans().
		 */
		public static function get_content_modal_plans( $category_ids = array(), $tag_ids = array() ) {
			if ( ! class_exists( 'Subs_Sys_DB_Updater' ) || ! Subs_Sys_DB_Updater::content_tables_exist() ) {
				return array();
			}

			global $wpdb;
			$table = $wpdb->prefix . 'subscription_content_plans';
			$all   = $wpdb->get_results( "SELECT * FROM $table WHERE status = 'active' ORDER BY price ASC, plan_name ASC" );

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

			foreach ( $all as $plan ) {
				$plan->content_ids = json_decode( $plan->content_ids, true ) ?: array();
				// Cast stored IDs to int for reliable intersection.
				$plan->content_ids = array_map( 'intval', $plan->content_ids );
			}

			$has_context = ! empty( $category_ids ) || ! empty( $tag_ids );

			if ( $has_context ) {
				// Strict match: only plans whose content_ids overlap with the current post.
				$matched = array();
				foreach ( $all as $plan ) {
					$applies = false;
					if ( 'category' === $plan->content_type && ! empty( $category_ids ) ) {
						$applies = ! empty( array_intersect( $plan->content_ids, array_map( 'intval', $category_ids ) ) );
					} elseif ( 'tag' === $plan->content_type && ! empty( $tag_ids ) ) {
						$applies = ! empty( array_intersect( $plan->content_ids, array_map( 'intval', $tag_ids ) ) );
					}
					if ( $applies ) {
						$matched[] = $plan;
					}
				}
				// No plans cover this post — return empty so modal can show a helpful message.
				return self::format_content_plans_for_modal( $matched );
			}

			// Non-singular context: show free plans only as a general subscription option.
			$free = array_filter( $all, static function ( $p ) { return (float) $p->price <= 0; } );
			return self::format_content_plans_for_modal( array_values( $free ) ?: $all );
		}

		/**
		 * Resolve a content plan's content_ids to human-readable term names.
		 *
		 * @param object $plan Raw DB row (content_ids already decoded to int[]).
		 * @return array{type: string, names: string[]}
		 */
		private static function resolve_plan_covered_terms( $plan ) {
			$names = array();
			$type  = $plan->content_type;

			if ( in_array( $type, array( 'category', 'tag' ), true ) ) {
				$taxonomy = 'category' === $type ? 'category' : 'post_tag';
				foreach ( $plan->content_ids as $tid ) {
					$term = get_term( (int) $tid, $taxonomy );
					if ( $term && ! is_wp_error( $term ) ) {
						$names[] = $term->name;
					}
				}
			} elseif ( 'post' === $type ) {
				foreach ( $plan->content_ids as $pid ) {
					$title = get_the_title( (int) $pid );
					if ( $title ) {
						$names[] = $title;
					}
				}
			} elseif ( 'post_type' === $type ) {
				foreach ( $plan->content_ids as $pt ) {
					$obj     = get_post_type_object( $pt );
					$names[] = $obj ? $obj->label : $pt;
				}
			}

			return array( 'type' => $type, 'names' => $names );
		}

		/**
		 * Format content plan rows into the modal plan shape.
		 *
		 * @param object[] $plans Raw DB rows from subscription_content_plans.
		 * @return array
		 */
		private static function format_content_plans_for_modal( $plans ) {
			$formatted = array();
			foreach ( $plans as $plan ) {
				$price   = (float) $plan->price;
				$is_free = $price <= 0;

				// Description as a feature line (only if set and not redundant).
				$features = array();
				if ( ! empty( $plan->description ) ) {
					$stripped = wp_strip_all_tags( $plan->description );
					if ( $stripped ) {
						$features = array( $stripped );
					}
				}

				$duration = (int) ( $plan->duration ?? 30 );
				switch ( $duration ) {
					case 1:   $dur_label = __( 'day', 'subscription-system' );      break;
					case 7:   $dur_label = __( 'week', 'subscription-system' );     break;
					case 30:  $dur_label = __( 'month', 'subscription-system' );    break;
					case 90:  $dur_label = __( 'quarter', 'subscription-system' );  break;
					case 365: $dur_label = __( 'year', 'subscription-system' );     break;
					default:
						$unit      = ! empty( $plan->duration_unit ) ? $plan->duration_unit : 'days';
						$dur_label = $duration . ' ' . $unit;
				}

				$price_label = $is_free
					? __( 'Free', 'subscription-system' )
					: sprintf( '$%s / %s', number_format( $price, 2 ), $dur_label );

				// Resolve which categories/tags/posts/post-types this plan covers.
				$covered = self::resolve_plan_covered_terms( $plan );

				$formatted[] = array(
					'id'             => (int) $plan->id,
					'name'           => $plan->plan_name,
					'price'          => $price,
					'price_label'    => $price_label,
					'duration_label' => $dur_label,
					'features'       => $features,
					'is_free'        => $is_free,
					'covered_type'   => $covered['type'],
					'covered_names'  => $covered['names'],
					'max_categories' => 0,
					'max_tags'       => 0,
					'plan_source'    => 'content',
				);
			}

			return apply_filters( 'subs_sys_content_modal_plans', $formatted, $plans );
		}

		/**
		 * Get a content plan by ID from subscription_content_plans table.
		 *
		 * @param int $plan_id Content plan ID.
		 * @return object|null
		 */
		public static function get_content_plan_by_id( $plan_id ) {
			if ( ! class_exists( 'Subs_Sys_DB_Updater' ) || ! Subs_Sys_DB_Updater::content_tables_exist() ) {
				return null;
			}
			global $wpdb;
			$table = $wpdb->prefix . 'subscription_content_plans';
			return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", absint( $plan_id ) ) );
		}

		/**
		 * Get a subscription plan by ID.
		 *
		 * @param int $plan_id Plan ID.
		 * @return object|null
		 */
		public static function get_plan_by_id( $plan_id ) {
			global $wpdb;

			$plan_id = absint( $plan_id );
			if ( ! $plan_id ) {
				return null;
			}

			$table = $wpdb->prefix . 'subscription_plans';
			return $wpdb->get_row(
				$wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $plan_id )
			);
		}

		/**
		 * Parse feature list from a plan record.
		 *
		 * @param object $plan Plan row.
		 * @return array
		 */
		public static function parse_plan_features( $plan ) {
			$features = array();

			if ( ! empty( $plan->features ) ) {
				$decoded = json_decode( $plan->features, true );
				if ( is_array( $decoded ) ) {
					if ( isset( $decoded['features'] ) && is_array( $decoded['features'] ) ) {
						$features = $decoded['features'];
					} else {
						$features = $decoded;
					}
				}
			}

			if ( empty( $features ) && ! empty( $plan->description ) ) {
				$decoded = json_decode( $plan->description, true );
				if ( is_array( $decoded ) ) {
					$features = $decoded;
				} elseif ( wp_strip_all_tags( $plan->description ) ) {
					$features = array( wp_strip_all_tags( $plan->description ) );
				}
			}

			return array_values(
				array_filter(
					array_map(
						static function ( $item ) {
							return is_string( $item ) ? trim( $item ) : '';
						},
						$features
					)
				)
			);
		}

		/**
		 * Resolve category/tag limits for a plan (0 = unlimited).
		 *
		 * @param object $plan        Plan row.
		 * @param array  $free_limits Default free limits.
		 * @return array{max_categories: int, max_tags: int}
		 */
		public static function get_plan_limits( $plan, $free_limits = null ) {
			if ( null === $free_limits ) {
				$free_limits = self::get_free_limits();
			}

			$max_categories = 0;
			$max_tags       = 0;

			foreach ( array( 'features', 'description' ) as $field ) {
				if ( empty( $plan->$field ) ) {
					continue;
				}
				$decoded = json_decode( $plan->$field, true );
				if ( ! is_array( $decoded ) ) {
					continue;
				}
				if ( isset( $decoded['max_categories'] ) ) {
					$max_categories = max( 0, (int) $decoded['max_categories'] );
				}
				if ( isset( $decoded['max_tags'] ) ) {
					$max_tags = max( 0, (int) $decoded['max_tags'] );
				}
			}

			if ( (float) $plan->price <= 0 ) {
				if ( ! $max_categories ) {
					$max_categories = (int) $free_limits['max_categories'];
				}
				if ( ! $max_tags ) {
					$max_tags = (int) $free_limits['max_tags'];
				}
			}

			return array(
				'max_categories' => $max_categories,
				'max_tags'       => $max_tags,
			);
		}

		/**
		 * Format plan price for display.
		 *
		 * @param object $plan Plan row.
		 * @return string
		 */
		public static function format_plan_price_label( $plan ) {
			$price = (float) $plan->price;
			if ( $price <= 0 ) {
				return __( 'Free', 'subscription-system' );
			}

			return sprintf(
				'$%s / %s',
				number_format( $price, 2 ),
				self::format_plan_duration_label( $plan )
			);
		}

		/**
		 * Format plan billing period for display.
		 *
		 * @param object $plan Plan row.
		 * @return string
		 */
		public static function format_plan_duration_label( $plan ) {
			$duration = (int) ( $plan->duration ?? 30 );

			switch ( $duration ) {
				case 1:
					return __( 'day', 'subscription-system' );
				case 7:
					return __( 'week', 'subscription-system' );
				case 30:
					return __( 'month', 'subscription-system' );
				case 90:
					return __( 'quarter', 'subscription-system' );
				case 180:
					return __( '6 months', 'subscription-system' );
				case 365:
					return __( 'year', 'subscription-system' );
				default:
					$unit = ! empty( $plan->duration_unit ) ? $plan->duration_unit : 'days';
					return $duration . ' ' . $unit;
			}
		}

		/**
		 * Create or update an interest subscription.
		 *
		 * @param array $data Subscription data.
		 * @return int|WP_Error
		 */
		public function save_subscription( $data ) {
			global $wpdb;

			if ( ! self::table_exists() ) {
				return new WP_Error( 'no_table', __( 'Interest subscription table is not available.', 'subscription-system' ) );
			}

			$name  = sanitize_text_field( $data['name'] ?? '' );
			$email = sanitize_email( $data['email'] ?? '' );

			if ( empty( $name ) || empty( $email ) || ! is_email( $email ) ) {
				return new WP_Error( 'invalid_data', __( 'Please provide a valid name and email address.', 'subscription-system' ) );
			}

		$category_ids = array_map( 'absint', (array) ( $data['category_ids'] ?? array() ) );
		$tag_ids      = array_map( 'absint', (array) ( $data['tag_ids'] ?? array() ) );
		$plan_id      = absint( $data['plan_id'] ?? 0 );
		$plan_source  = isset( $data['plan_source'] ) && 'content' === $data['plan_source'] ? 'content' : 'interest';

		$category_ids = array_values( array_filter( $category_ids ) );
		$tag_ids      = array_values( array_filter( $tag_ids ) );

		if ( empty( $category_ids ) && empty( $tag_ids ) ) {
			return new WP_Error( 'no_interests', __( 'Please select at least one category or tag.', 'subscription-system' ) );
		}

		if ( ! $plan_id ) {
			return new WP_Error( 'invalid_plan', __( 'Please select a subscription plan.', 'subscription-system' ) );
		}

		if ( 'content' === $plan_source ) {
			$plan = self::get_content_plan_by_id( $plan_id );
		} else {
			$plan = self::get_plan_by_id( $plan_id );
		}

		if ( ! $plan || 'active' !== $plan->status ) {
			return new WP_Error( 'invalid_plan', __( 'Selected subscription plan is not available.', 'subscription-system' ) );
		}

		$is_free   = (float) $plan->price <= 0;
		$plan_type = $is_free ? 'free' : 'paid';
		// Content plans don't enforce per-category/tag limits in the interest modal.
		$limits    = 'content' === $plan_source
			? array( 'max_categories' => 0, 'max_tags' => 0 )
			: self::get_plan_limits( $plan );

			if ( $limits['max_categories'] > 0 && count( $category_ids ) > $limits['max_categories'] ) {
				return new WP_Error(
					'category_limit',
					sprintf(
						/* translators: 1: plan name, 2: maximum categories */
						__( '%1$s allows up to %2$d categories. Please upgrade your plan.', 'subscription-system' ),
						$plan->plan_name,
						$limits['max_categories']
					)
				);
			}

			if ( $limits['max_tags'] > 0 && count( $tag_ids ) > $limits['max_tags'] ) {
				return new WP_Error(
					'tag_limit',
					sprintf(
						/* translators: 1: plan name, 2: maximum tags */
						__( '%1$s allows up to %2$d tags. Please upgrade your plan.', 'subscription-system' ),
						$plan->plan_name,
						$limits['max_tags']
					)
				);
			}

			$user_id = get_current_user_id();
			if ( ! $user_id ) {
				$existing_user = get_user_by( 'email', $email );
				if ( $existing_user ) {
					$user_id = $existing_user->ID;
				} else {
					$username = sanitize_user( current( explode( '@', $email ) ), true );
					if ( username_exists( $username ) ) {
						$username = sanitize_user( $username . wp_rand( 100, 999 ), true );
					}
					$user_id = wp_insert_user(
						array(
							'user_login'   => $username,
							'user_email'   => $email,
							'display_name' => $name,
							'first_name'   => $name,
							'user_pass'    => wp_generate_password( 16, true ),
							'role'         => get_option( 'subs_sys_default_role', 'subscriber' ),
						)
					);
					if ( is_wp_error( $user_id ) ) {
						return $user_id;
					}
				}
			}

			$status = $is_free ? 'active' : 'pending';

			$table = self::table_name();
			$existing = $wpdb->get_row(
				$wpdb->prepare(
					"SELECT * FROM $table WHERE subscriber_email = %s AND status IN ('active', 'pending') ORDER BY id DESC LIMIT 1",
					$email
				)
			);

			$row = array(
				'user_id'          => $user_id,
				'plan_id'          => $plan_id,
				'subscriber_name'  => $name,
				'subscriber_email' => $email,
				'category_ids'     => wp_json_encode( $category_ids ),
				'tag_ids'          => wp_json_encode( $tag_ids ),
				'plan_type'        => $plan_type,
				'status'           => $status,
				'updated_at'       => current_time( 'mysql' ),
			);

			if ( $existing ) {
				$result = $wpdb->update( $table, $row, array( 'id' => $existing->id ) );
				if ( false === $result ) {
					return new WP_Error( 'db_error', __( 'Could not update subscription.', 'subscription-system' ) );
				}
				$subscription_id = (int) $existing->id;
			} else {
				$row['created_at'] = current_time( 'mysql' );
				$result = $wpdb->insert( $table, $row );
				if ( false === $result ) {
					return new WP_Error( 'db_error', __( 'Could not save subscription.', 'subscription-system' ) );
				}
				$subscription_id = (int) $wpdb->insert_id;
			}

			if ( 'active' === $status ) {
				do_action( 'subs_sys_interest_subscription_activated', $subscription_id, $user_id, $row );
			}

			return $subscription_id;
		}

		/**
		 * Activate a pending paid subscription.
		 *
		 * @param int    $subscription_id Subscription ID.
		 * @param string $payment_id      Payment reference.
		 * @return bool|WP_Error
		 */
		public function activate_paid_subscription( $subscription_id, $payment_id = '' ) {
			global $wpdb;

			$table = self::table_name();
			$result = $wpdb->update(
				$table,
				array(
					'status'     => 'active',
					'payment_id' => sanitize_text_field( $payment_id ),
					'updated_at' => current_time( 'mysql' ),
				),
				array( 'id' => absint( $subscription_id ) )
			);

			if ( false === $result ) {
				return new WP_Error( 'db_error', __( 'Could not activate subscription.', 'subscription-system' ) );
			}

			return true;
		}

		/**
		 * Get subscription by ID.
		 *
		 * @param int $id Subscription ID.
		 * @return object|null
		 */
		public function get_subscription( $id ) {
			global $wpdb;
			$table = self::table_name();
			$row   = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ) );
			if ( $row ) {
				$row->category_ids = json_decode( $row->category_ids, true ) ?: array();
				$row->tag_ids      = json_decode( $row->tag_ids, true ) ?: array();
			}
			return $row;
		}

		/**
		 * Get all active subscriptions.
		 *
		 * @return array
		 */
		public function get_active_subscriptions() {
			global $wpdb;
			if ( ! self::table_exists() ) {
				return array();
			}

			$table = self::table_name();
			$rows  = $wpdb->get_results( "SELECT * FROM $table WHERE status = 'active' ORDER BY created_at DESC" );

			foreach ( $rows as $row ) {
				$row->category_ids = json_decode( $row->category_ids, true ) ?: array();
				$row->tag_ids      = json_decode( $row->tag_ids, true ) ?: array();
			}

			return $rows ?: array();
		}

		/**
		 * Check whether a post matches a subscription's interests.
		 *
		 * @param int    $post_id      Post ID.
		 * @param object $subscription Subscription row.
		 * @return bool
		 */
		public function post_matches_subscription( $post_id, $subscription ) {
			$post_categories = wp_get_post_categories( $post_id );
			$post_tags       = wp_get_post_tags( $post_id, array( 'fields' => 'ids' ) );

			$category_match = ! empty( $subscription->category_ids )
				&& ! empty( array_intersect( $subscription->category_ids, $post_categories ) );

			$tag_match = ! empty( $subscription->tag_ids )
				&& ! empty( array_intersect( $subscription->tag_ids, $post_tags ) );

			return $category_match || $tag_match;
		}

		/**
		 * Notify matching subscribers when new content is published.
		 *
		 * @param int     $post_id Post ID.
		 * @param WP_Post $post    Post object.
		 */
		public function handle_new_post( $post_id, $post ) {
			if ( wp_is_post_revision( $post_id ) || 'publish' !== $post->post_status ) {
				return;
			}

			if ( ! self::table_exists() ) {
				return;
			}

			$subscriptions = $this->get_active_subscriptions();
			if ( empty( $subscriptions ) ) {
				return;
			}

			foreach ( $subscriptions as $subscription ) {
				if ( ! $this->post_matches_subscription( $post_id, $subscription ) ) {
					continue;
				}
				$this->send_notification_email( $subscription, $post_id, $post );
			}
		}

		/**
		 * Send a notification email to a subscriber.
		 *
		 * @param object  $subscription Subscription row.
		 * @param int     $post_id      Post ID.
		 * @param WP_Post $post         Post object.
		 */
		private function send_notification_email( $subscription, $post_id, $post ) {
			if ( ! get_option( 'enable_email_notifications', 1 ) ) {
				return;
			}

			$subject = sprintf(
				/* translators: 1: site name, 2: post title */
				__( '[%1$s] New update: %2$s', 'subscription-system' ),
				get_bloginfo( 'name' ),
				$post->post_title
			);

			$excerpt = $post->post_excerpt;
			if ( empty( $excerpt ) ) {
				$excerpt = wp_trim_words( wp_strip_all_tags( $post->post_content ), 30 );
			}

			$message = sprintf(
				"<p>%s</p><p>%s</p><p><a href='%s'>%s</a></p>",
				sprintf(
					/* translators: %s: subscriber name */
					esc_html__( 'Hi %s,', 'subscription-system' ),
					esc_html( $subscription->subscriber_name )
				),
				esc_html( $excerpt ),
				esc_url( get_permalink( $post_id ) ),
				esc_html__( 'Read more', 'subscription-system' )
			);

			$from_email = get_option( 'subscription_from_email', get_option( 'admin_email' ) );
			$from_name  = get_option( 'subscription_from_name', get_bloginfo( 'name' ) );

			$headers = array(
				'Content-Type: text/html; charset=UTF-8',
				'From: ' . $from_name . ' <' . $from_email . '>',
			);

			wp_mail( $subscription->subscriber_email, $subject, $message, $headers );
		}

		/**
		 * Get WordPress categories for the subscribe form.
		 *
		 * @return array
		 */
		public static function get_available_categories() {
			$categories = get_categories(
				array(
					'hide_empty' => false,
					'orderby'    => 'name',
					'order'      => 'ASC',
				)
			);

			$options = array();
			foreach ( $categories as $cat ) {
				$options[] = array(
					'id'   => (int) $cat->term_id,
					'name' => $cat->name,
					'slug' => $cat->slug,
				);
			}
			return $options;
		}

		/**
		 * Get a specific set of categories by term ID, in the same format as get_available_categories().
		 *
		 * @param int[] $ids Term IDs to fetch.
		 * @return array
		 */
		public static function get_categories_by_ids( array $ids ) {
			if ( empty( $ids ) ) {
				return array();
			}
			$ids        = array_map( 'intval', $ids );
			$categories = get_categories(
				array(
					'include'    => $ids,
					'hide_empty' => false,
					'orderby'    => 'name',
					'order'      => 'ASC',
				)
			);
			$options = array();
			foreach ( $categories as $cat ) {
				$options[] = array(
					'id'   => (int) $cat->term_id,
					'name' => $cat->name,
					'slug' => $cat->slug,
				);
			}
			return $options;
		}

		/**
		 * Get WordPress tags for the subscribe form.
		 *
		 * @return array
		 */
		public static function get_available_tags() {
			$tags = get_tags(
				array(
					'hide_empty' => false,
					'orderby'    => 'name',
					'order'      => 'ASC',
				)
			);

			$options = array();
			foreach ( $tags as $tag ) {
				$options[] = array(
					'id'   => (int) $tag->term_id,
					'name' => $tag->name,
					'slug' => $tag->slug,
				);
			}
			return $options;
		}

		/**
		 * Get a specific set of tags by term ID, in the same format as get_available_tags().
		 *
		 * @param int[] $ids Term IDs to fetch.
		 * @return array
		 */
		public static function get_tags_by_ids( array $ids ) {
			if ( empty( $ids ) ) {
				return array();
			}
			$ids  = array_map( 'intval', $ids );
			$tags = get_tags(
				array(
					'include'    => $ids,
					'hide_empty' => false,
					'orderby'    => 'name',
					'order'      => 'ASC',
				)
			);
			$options = array();
			foreach ( $tags as $tag ) {
				$options[] = array(
					'id'   => (int) $tag->term_id,
					'name' => $tag->name,
					'slug' => $tag->slug,
				);
			}
			return $options;
		}
	}
}
