<?php
/**
 * Manages user-defined custom taxonomies for vehicles.
 *
 * Stores taxonomy definitions in the `trx_ad_user_taxonomies` option
 * and dynamically registers them on `init`.
 *
 * @package ThemeREX\AutoDealer
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\Taxonomies;

defined( 'ABSPATH' ) || exit;
use ThemeREX\AutoDealer\PostTypes\VehiclePostType;

class UserTaxonomyManager {

	/**
	 * Option key for user-defined taxonomy definitions.
	 */
	public const OPTION_KEY = 'trx_ad_user_taxonomies';

	/**
	 * Prefix for user taxonomy slugs.
	 */
	public const SLUG_PREFIX = 'trx_ad_u_';

	/**
	 * Core taxonomy slugs that cannot be overridden or deleted.
	 *
	 * @var string[]
	 */
	public const CORE_TAXONOMIES = array(
		'trx_ad_make',
		'trx_ad_model',
		'trx_ad_body_style',
		'trx_ad_fuel_type',
		'trx_ad_transmission',
		'trx_ad_drivetrain',
		'trx_ad_color',
		'trx_ad_feature',
		'trx_ad_vehicle_type',
		'trx_ad_condition',
	);

	/**
	 * Option key for disabled core taxonomy slugs.
	 */
	public const DISABLED_OPTION_KEY = 'trx_ad_disabled_taxonomies';

	/**
	 * Core taxonomies that cannot be disabled (essential for plugin operation).
	 *
	 * @var string[]
	 */
	public const NON_DISABLEABLE = array(
		'trx_ad_make',
		'trx_ad_model',
		'trx_ad_vehicle_type',
		'trx_ad_condition',
	);

	/**
	 * Maximum number of user taxonomies allowed.
	 */
	private const MAX_TAXONOMIES = 20;

	/**
	 * Register hooks for dynamic taxonomy registration.
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'init', array( $this, 'registerUserTaxonomies' ), 9 );
	}

	/**
	 * Register all user-defined taxonomies.
	 *
	 * Hooked to `init` at priority 9 (same as core taxonomies,
	 * before CPTs register at 10).
	 *
	 * @return void
	 */
	public function registerUserTaxonomies(): void {
		$taxonomies = self::getAll();

		foreach ( $taxonomies as $taxonomy ) {
			$slug = $taxonomy['slug'] ?? '';
			if ( $slug === '' || taxonomy_exists( $slug ) ) {
				continue;
			}

			$this->registerSingle( $taxonomy );
		}
	}

	/**
	 * Register a single user-defined taxonomy.
	 *
	 * @param array{slug: string, label: string, singular: string, hierarchical: bool, show_in_filters: bool, show_in_specs: bool, rewrite_slug: string} $taxonomy Taxonomy definition.
	 * @return void
	 */
	private function registerSingle( array $taxonomy ): void {
		$slug         = $taxonomy['slug'];
		$label        = $taxonomy['label'] ?? $slug;
		$singular     = $taxonomy['singular'] ?? $label;
		$hierarchical = (bool) ( $taxonomy['hierarchical'] ?? false );
		$rewriteSlug  = $taxonomy['rewrite_slug'] ?? sanitize_title( $label );

		$labels = array(
			'name'              => $label,
			'singular_name'     => $singular,
			'search_items'      => sprintf(
				/* translators: %s: taxonomy plural label */
				__( 'Search %s', 'trx-auto-dealer' ),
				$label
			),
			'all_items'         => sprintf(
				/* translators: %s: taxonomy plural label */
				__( 'All %s', 'trx-auto-dealer' ),
				$label
			),
			'parent_item'       => $hierarchical ? sprintf(
				/* translators: %s: taxonomy singular label */
				__( 'Parent %s', 'trx-auto-dealer' ),
				$singular
			) : null,
			'parent_item_colon' => $hierarchical ? sprintf(
				/* translators: %s: taxonomy singular label */
				__( 'Parent %s:', 'trx-auto-dealer' ),
				$singular
			) : null,
			'edit_item'         => sprintf(
				/* translators: %s: taxonomy singular label */
				__( 'Edit %s', 'trx-auto-dealer' ),
				$singular
			),
			'update_item'       => sprintf(
				/* translators: %s: taxonomy singular label */
				__( 'Update %s', 'trx-auto-dealer' ),
				$singular
			),
			'add_new_item'      => sprintf(
				/* translators: %s: taxonomy singular label */
				__( 'Add New %s', 'trx-auto-dealer' ),
				$singular
			),
			'new_item_name'     => sprintf(
				/* translators: %s: taxonomy singular label */
				__( 'New %s Name', 'trx-auto-dealer' ),
				$singular
			),
			'menu_name'         => $label,
		);

		$args = array(
			'labels'            => $labels,
			'hierarchical'      => $hierarchical,
			'public'            => true,
			'show_ui'           => true,
			'show_admin_column' => true,
			'show_in_nav_menus' => true,
			'show_in_rest'      => false,
			'show_tagcloud'     => ! $hierarchical,
			'rewrite'           => array(
				'slug'         => $rewriteSlug,
				'with_front'   => false,
				'hierarchical' => $hierarchical,
			),
		);

		register_taxonomy( $slug, VehiclePostType::POST_TYPE, $args );
	}

	/**
	 * Get all user-defined taxonomy definitions.
	 *
	 * @return array<int, array{id: string, slug: string, label: string, singular: string, hierarchical: bool, show_in_filters: bool, show_in_specs: bool, rewrite_slug: string}>
	 */
	public static function getAll(): array {
		$taxonomies = get_option( self::OPTION_KEY, array() );
		return is_array( $taxonomies ) ? $taxonomies : array();
	}

	/**
	 * Save user-defined taxonomy definitions.
	 *
	 * @param array<int, array<string, mixed>> $taxonomies Raw taxonomy definitions.
	 * @return true|\WP_Error True on success, WP_Error on failure.
	 */
	public static function save( array $taxonomies ): bool|\WP_Error {
		$clean = array();

		foreach ( $taxonomies as $taxonomy ) {
			if ( ! is_array( $taxonomy ) ) {
				continue;
			}

			$label = sanitize_text_field( $taxonomy['label'] ?? '' );
			if ( $label === '' ) {
				continue;
			}

			$slug = sanitize_key( $taxonomy['slug'] ?? '' );
			if ( $slug === '' ) {
				$slug = self::SLUG_PREFIX . sanitize_key( str_replace( ' ', '_', strtolower( $label ) ) );
			}

			// Ensure prefix.
			if ( ! str_starts_with( $slug, self::SLUG_PREFIX ) ) {
				$slug = self::SLUG_PREFIX . $slug;
			}

			// WordPress taxonomy slug max length is 32 characters.
			if ( strlen( $slug ) > 32 ) {
				$slug = substr( $slug, 0, 32 );
			}

			// Block core taxonomy slugs.
			if ( in_array( $slug, self::CORE_TAXONOMIES, true ) ) {
				continue;
			}

			// Check for duplicate slugs within user taxonomies.
			$existingSlugs = array_column( $clean, 'slug' );
			if ( in_array( $slug, $existingSlugs, true ) ) {
				continue;
			}

			$clean[] = array(
				'id'              => sanitize_key( $taxonomy['id'] ?? wp_generate_uuid4() ),
				'slug'            => $slug,
				'label'           => $label,
				'singular'        => sanitize_text_field( $taxonomy['singular'] ?? $label ),
				'hierarchical'    => ! empty( $taxonomy['hierarchical'] ),
				'show_in_filters' => ! empty( $taxonomy['show_in_filters'] ),
				'show_in_specs'   => ! empty( $taxonomy['show_in_specs'] ),
				'rewrite_slug'    => sanitize_title( $taxonomy['rewrite_slug'] ?? $label ),
			);
		}

		if ( count( $clean ) > self::MAX_TAXONOMIES ) {
			return new \WP_Error(
				'trx_ad_too_many_taxonomies',
				sprintf(
					/* translators: %d: maximum number of user taxonomies */
					__( 'Maximum %d custom taxonomies allowed.', 'trx-auto-dealer' ),
					self::MAX_TAXONOMIES
				)
			);
		}

		update_option( self::OPTION_KEY, $clean );

		// Flush rewrite rules so new taxonomy archives work.
		flush_rewrite_rules();

		return true;
	}

	/**
	 * Get all user taxonomy slugs.
	 *
	 * @return string[]
	 */
	public static function getSlugs(): array {
		return array_column( self::getAll(), 'slug' );
	}

	/**
	 * Get user taxonomies marked as filterable.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function getFilterable(): array {
		return array_filter(
			self::getAll(),
			static fn( array $t ): bool => ! empty( $t['show_in_filters'] )
		);
	}

	/**
	 * Get user taxonomies marked for specs table display.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function getSpecsVisible(): array {
		return array_filter(
			self::getAll(),
			static fn( array $t ): bool => ! empty( $t['show_in_specs'] )
		);
	}

	/**
	 * Check if a given taxonomy slug is a user-defined taxonomy.
	 *
	 * @param string $slug Taxonomy slug.
	 * @return bool
	 */
	public static function isUserTaxonomy( string $slug ): bool {
		return in_array( $slug, self::getSlugs(), true );
	}

	// ------------------------------------------------------------------
	// Disabled core taxonomies (Pro)
	// ------------------------------------------------------------------

	/**
	 * Get the list of disabled core taxonomy slugs.
	 *
	 * @return string[]
	 */
	public static function getDisabled(): array {
		$disabled = get_option( self::DISABLED_OPTION_KEY, array() );
		return is_array( $disabled ) ? $disabled : array();
	}

	/**
	 * Check if a core taxonomy is disabled.
	 *
	 * @param string $slug Taxonomy slug.
	 * @return bool
	 */
	public static function isDisabled( string $slug ): bool {
		return in_array( $slug, self::getDisabled(), true );
	}

	/**
	 * Check if a core taxonomy is active (not disabled).
	 *
	 * @param string $slug Taxonomy slug.
	 * @return bool
	 */
	public static function isActive( string $slug ): bool {
		return ! self::isDisabled( $slug );
	}

	/**
	 * Save the list of disabled core taxonomy slugs.
	 *
	 * Filters out non-disableable taxonomies (make, model, vehicle_type, condition).
	 *
	 * @param string[] $slugs Taxonomy slugs to disable.
	 * @return void
	 */
	public static function saveDisabled( array $slugs ): void {
		$clean = array();

		foreach ( $slugs as $slug ) {
			$slug = sanitize_key( $slug );

			// Only core taxonomies can be disabled via this method.
			if ( ! in_array( $slug, self::CORE_TAXONOMIES, true ) ) {
				continue;
			}

			// Some core taxonomies cannot be disabled.
			if ( in_array( $slug, self::NON_DISABLEABLE, true ) ) {
				continue;
			}

			$clean[] = $slug;
		}

		update_option( self::DISABLED_OPTION_KEY, array_unique( $clean ) );
	}

	/**
	 * Get core taxonomies that are currently active (not disabled).
	 *
	 * @return string[]
	 */
	public static function getActiveCoreSlug(): array {
		$disabled = self::getDisabled();
		return array_values(
			array_filter(
				self::CORE_TAXONOMIES,
				static fn( string $slug ): bool => ! in_array( $slug, $disabled, true )
			)
		);
	}
}
