<?php

namespace CustomTaxonomyOrder\Includes;

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

/**
 * Taxonomy discovery, exclusions, and capability checks.
 */
class TaxonomyHelper {

	public const DEFAULT_ORDER_META_KEY = '_wpps_taxonomy_order';

	/** @var list<string> */
	public const EXCLUDED_TAXONOMIES = [
		'nav_menu',
		'link_category',
		'post_format',
		'wp_theme',
		'wp_template_part_area',
		'wp_template',
		'wp_pattern_category',
	];

	/** @var list<string> */
	public const ALLOWED_CAPABILITY_KEYS = [
		'manage_categories',
		'manage_terms',
		'edit_posts',
		'manage_options',
	];

	public static function getOrderMetaKey(): string {
		/**
		 * Filter the term meta key used to store custom taxonomy order.
		 *
		 * @param string $meta_key Default `_wpps_taxonomy_order`.
		 */
		return (string) apply_filters(
			'wp_powersuite_custom_taxonomy_order_meta_key',
			self::DEFAULT_ORDER_META_KEY
		);
	}

	/**
	 * @return list<string>
	 */
	public static function getExcludedTaxonomies(): array {
		$excluded = self::EXCLUDED_TAXONOMIES;

		/**
		 * Filter taxonomies excluded from custom ordering.
		 *
		 * @param list<string> $excluded Taxonomy slugs.
		 */
		return apply_filters( 'wp_powersuite_custom_taxonomy_order_excluded_taxonomies', $excluded );
	}

	/**
	 * @return array<string, \WP_Taxonomy>
	 */
	public static function getAvailableTaxonomyObjects(): array {
		$objects  = get_taxonomies( [ 'public' => true ], 'objects' );
		$excluded = array_flip( self::getExcludedTaxonomies() );
		$out      = [];

		foreach ( $objects as $name => $object ) {
			if ( ! $object instanceof \WP_Taxonomy ) {
				continue;
			}
			if ( isset( $excluded[ $name ] ) ) {
				continue;
			}
			if ( ! self::currentUserCanManageTaxonomy( $name, $object ) ) {
				continue;
			}
			$out[ $name ] = $object;
		}

		if ( class_exists( 'WooCommerce' ) ) {
			foreach ( [ 'product_cat', 'product_tag' ] as $wc_tax ) {
				$wc_object = get_taxonomy( $wc_tax );
				if ( $wc_object instanceof \WP_Taxonomy && ! isset( $out[ $wc_tax ] ) && self::currentUserCanManageTaxonomy( $wc_tax, $wc_object ) ) {
					$out[ $wc_tax ] = $wc_object;
				}
			}
		}

		/**
		 * Filter available taxonomies for custom ordering.
		 *
		 * @param array<string, \WP_Taxonomy> $out Taxonomy objects keyed by slug.
		 */
		return apply_filters( 'wp_powersuite_custom_taxonomy_order_taxonomies', $out );
	}

	/**
	 * @return list<string>
	 */
	public static function getAvailableTaxonomySlugs(): array {
		return array_keys( self::getAvailableTaxonomyObjects() );
	}

	public static function isSupportedTaxonomy( string $taxonomy ): bool {
		$taxonomy = sanitize_key( $taxonomy );
		if ( $taxonomy === '' || ! taxonomy_exists( $taxonomy ) ) {
			return false;
		}
		if ( in_array( $taxonomy, self::getExcludedTaxonomies(), true ) ) {
			return false;
		}

		return in_array( $taxonomy, self::getAvailableTaxonomySlugs(), true );
	}

	/**
	 * @param list<string> $selected
	 * @return list<string>
	 */
	public static function sanitizeSelectedTaxonomies( array $selected ): array {
		$allowed = array_flip( self::getAvailableTaxonomySlugs() );
		$out     = [];

		foreach ( $selected as $tax ) {
			$key = sanitize_key( (string) $tax );
			if ( $key !== '' && isset( $allowed[ $key ] ) && ! in_array( $key, $out, true ) ) {
				$out[] = $key;
			}
		}

		return $out;
	}

	public static function currentUserCanManageTaxonomy( string $taxonomy, ?\WP_Taxonomy $object = null ): bool {
		$object = $object ?? get_taxonomy( $taxonomy );
		if ( ! $object instanceof \WP_Taxonomy ) {
			return false;
		}

		$cap = $object->cap->manage_terms ?? 'manage_categories';
		return current_user_can( $cap );
	}

	public static function userCanReorderTaxonomy( string $taxonomy, string $reorder_capability ): bool {
		if ( ! self::isSupportedTaxonomy( $taxonomy ) ) {
			return false;
		}

		$reorder_capability = self::sanitizeCapabilityKey( $reorder_capability );
		if ( ! current_user_can( $reorder_capability ) ) {
			return false;
		}

		return self::currentUserCanManageTaxonomy( $taxonomy );
	}

	public static function userCanEditTerm( int $term_id, string $taxonomy ): bool {
		$term = get_term( $term_id, $taxonomy );
		if ( ! $term instanceof \WP_Term || is_wp_error( $term ) ) {
			return false;
		}

		$object = get_taxonomy( $taxonomy );
		if ( ! $object instanceof \WP_Taxonomy ) {
			return false;
		}

		$cap = $object->cap->edit_terms ?? 'manage_categories';
		return current_user_can( $cap, $term_id );
	}

	public static function sanitizeCapabilityKey( string $capability ): string {
		$capability = sanitize_key( $capability );
		if ( in_array( $capability, self::ALLOWED_CAPABILITY_KEYS, true ) ) {
			return $capability;
		}

		return 'manage_categories';
	}

	public static function isHierarchical( string $taxonomy ): bool {
		$object = get_taxonomy( $taxonomy );
		return $object instanceof \WP_Taxonomy && $object->hierarchical;
	}
}
