<?php

namespace Storeplugin\PDFInvoice\Framework\PostType;

defined('ABSPATH') || exit;

use Storeplugin\PDFInvoice\Framework\Contracts\TaxonomyInterface;

/**
 * Taxonomy Builder
 *
 * Fluent builder pattern for registering custom taxonomies.
 */
class Taxonomy implements TaxonomyInterface
{
	/**
	 * Taxonomy slug/key
	 *
	 * @var string
	 */
	private string $taxonomy;

	/**
	 * Plural name for labels
	 *
	 * @var string
	 */
	private string $plural;

	/**
	 * Singular name for labels
	 *
	 * @var string
	 */
	private string $singular;

	/**
	 * Text domain for translations
	 *
	 * @var string
	 */
	private string $textDomain;

	/**
	 * Post types to associate with this taxonomy
	 *
	 * @var array|string
	 */
	private $objectType;

	/**
	 * Whether the taxonomy is hierarchical (like categories)
	 *
	 * @var bool
	 */
	private bool $hierarchical = false;

	/**
	 * Whether to show UI in admin
	 *
	 * @var bool
	 */
	private bool $showUi = true;

	/**
	 * Whether to show in admin menu
	 *
	 * @var bool
	 */
	private bool $showInMenu = true;

	/**
	 * Parent menu slug for submenu placement.
	 *
	 * @var string|null
	 */
	private ?string $submenuOf = null;

	/**
	 * Whether to show in quick/bulk edit panel
	 *
	 * @var bool
	 */
	private bool $showInQuickEdit = true;

	/**
	 * Whether to show admin column
	 *
	 * @var bool
	 */
	private bool $showAdminColumn = false;

	/**
	 * Whether to query var
	 *
	 * @var bool
	 */
	private bool $queryVar = true;

	/**
	 * Rewrite rules
	 *
	 * @var array|bool
	 */
	private $rewrite = true;

	/**
	 * Whether to show in REST API
	 *
	 * @var bool
	 */
	private bool $showInRest = false;

	/**
	 * REST API base
	 *
	 * @var string
	 */
	private string $restBase = '';

	/**
	 * REST controller class
	 *
	 * @var string|false
	 */
	private $restControllerClass = false;

	/**
	 * Whether to show in nav menus
	 *
	 * @var bool
	 */
	private bool $showInNavMenus = true;

	/**
	 * Whether to show tagcloud
	 *
	 * @var bool
	 */
	private bool $showTagcloud = true;

	/**
	 * Whether to show in admin
	 *
	 * @var bool
	 */
	private bool $showAdmin = true;

	/**
	 * Meta box callback
	 *
	 * @var callable|null
	 */
	private $metaBoxCb = null;

	/**
	 * Capabilities
	 *
	 * @var array|null
	 */
	private ?array $capabilities = null;

	/**
	 * Update count callback
	 *
	 * @var callable|null
	 */
	private $updateCountCallback = null;

	/**
	 * Whether to automatically sort terms
	 *
	 * @var bool
	 */
	private bool $sort = false;

	/**
	 * Arguments for query var
	 *
	 * @var string|false
	 */
	private $queryVarArg = '';

	/**
	 * Constructor.
	 *
	 * @param string $taxonomy The taxonomy slug/key.
	 * @param string $plural The plural name for labels.
	 * @param string $textDomain The text domain for translations.
	 */
	private function __construct(string $taxonomy, string $plural, string $textDomain)
	{
		$this->taxonomy = $taxonomy;
		$this->plural = $plural;
		$this->singular = rtrim($plural, 's');
		$this->textDomain = $textDomain;
	}

	/**
	 * Create a new taxonomy builder instance.
	 *
	 * @param string $taxonomy The taxonomy slug/key.
	 * @param string $plural The plural name for labels.
	 * @param string $textDomain The text domain for translations.
	 * @return TaxonomyInterface
	 */
	public static function create(string $taxonomy, string $plural, string $textDomain = ''): TaxonomyInterface
	{
		return new self($taxonomy, $plural, $textDomain);
	}

	/**
	 * Set the object type (post types) for this taxonomy.
	 *
	 * @param array|string $objectType The post type(s) to associate with.
	 * @return self
	 */
	public function objectType($objectType): self
	{
		$this->objectType = $objectType;
		return $this;
	}

	/**
	 * Set whether the taxonomy is hierarchical.
	 *
	 * @param bool $hierarchical Whether hierarchical.
	 * @return self
	 */
	public function hierarchical(bool $hierarchical = true): self
	{
		$this->hierarchical = $hierarchical;
		return $this;
	}

	/**
	 * Set whether to show UI in admin.
	 *
	 * @param bool $show Whether to show UI.
	 * @return self
	 */
	public function showUi(bool $show = true): self
	{
		$this->showUi = $show;
		return $this;
	}

	/**
	 * Set whether to show in admin menu.
	 *
	 * @param bool $show Whether to show in menu.
	 * @return self
	 */
	public function showInMenu(bool $show = true): self
	{
		$this->showInMenu = $show;
		return $this;
	}

	/**
	 * Place the taxonomy under a parent admin menu slug.
	 *
	 * @param string $parentSlug Parent menu slug.
	 * @return self
	 */
	public function submenuOf(string $parentSlug): self
	{
		$this->submenuOf = $parentSlug;
		return $this;
	}

	/**
	 * Set whether to show in quick/bulk edit panel.
	 *
	 * @param bool $show Whether to show in quick edit.
	 * @return self
	 */
	public function showInQuickEdit(bool $show = true): self
	{
		$this->showInQuickEdit = $show;
		return $this;
	}

	/**
	 * Set whether to show admin column.
	 *
	 * @param bool $show Whether to show admin column.
	 * @return self
	 */
	public function showAdminColumn(bool $show = true): self
	{
		$this->showAdminColumn = $show;
		return $this;
	}

	/**
	 * Set whether to use query var.
	 *
	 * @param bool|string $query Whether to use query var or the query var string.
	 * @return self
	 */
	public function queryVar($query = true): self
	{
		if (is_string($query)) {
			$this->queryVar = true;
			$this->queryVarArg = $query;
		} else {
			$this->queryVar = $query;
			$this->queryVarArg = '';
		}
		return $this;
	}

	/**
	 * Set rewrite rules.
	 *
	 * @param array|bool $rewrite Rewrite rules or false to disable.
	 * @return self
	 */
	public function rewrite($rewrite = true): self
	{
		$this->rewrite = $rewrite;
		return $this;
	}

	/**
	 * Set whether to show in REST API.
	 *
	 * @param bool $show Whether to show in REST.
	 * @return self
	 */
	public function showInRest(bool $show = true): self
	{
		$this->showInRest = $show;
		return $this;
	}

	/**
	 * Set the REST API base.
	 *
	 * @param string $base The REST base.
	 * @return self
	 */
	public function restBase(string $base): self
	{
		$this->restBase = $base;
		return $this;
	}

	/**
	 * Set the REST controller class.
	 *
	 * @param string|false $controller The controller class name.
	 * @return self
	 */
	public function restControllerClass($controller): self
	{
		$this->restControllerClass = $controller;
		return $this;
	}

	/**
	 * Set whether to show in nav menus.
	 *
	 * @param bool $show Whether to show in nav menus.
	 * @return self
	 */
	public function showInNavMenus(bool $show = true): self
	{
		$this->showInNavMenus = $show;
		return $this;
	}

	/**
	 * Set whether to show tagcloud.
	 *
	 * @param bool $show Whether to show tagcloud.
	 * @return self
	 */
	public function showTagcloud(bool $show = true): self
	{
		$this->showTagcloud = $show;
		return $this;
	}

	/**
	 * Set whether to show in admin.
	 *
	 * @param bool $show Whether to show in admin.
	 * @return self
	 */
	public function showAdmin(bool $show = true): self
	{
		$this->showAdmin = $show;
		return $this;
	}

	/**
	 * Set the meta box callback.
	 *
	 * @param callable|null $callback The callback function.
	 * @return self
	 */
	public function metaBoxCb(?callable $callback): self
	{
		$this->metaBoxCb = $callback;
		return $this;
	}

	/**
	 * Set capabilities.
	 *
	 * @param array|null $capabilities Array of capabilities.
	 * @return self
	 */
	public function capabilities(?array $capabilities): self
	{
		$this->capabilities = $capabilities;
		return $this;
	}

	/**
	 * Set the update count callback.
	 *
	 * @param callable|null $callback The callback function.
	 * @return self
	 */
	public function updateCountCallback(?callable $callback): self
	{
		$this->updateCountCallback = $callback;
		return $this;
	}

	/**
	 * Set whether to automatically sort terms.
	 *
	 * @param bool $sort Whether to auto sort.
	 * @return self
	 */
	public function sort(bool $sort = true): self
	{
		$this->sort = $sort;
		return $this;
	}

	/**
	 * Build and register the taxonomy.
	 *
	 * @return void
	 */
	public function render(): void
	{
		add_action('init', [$this, 'registerTaxonomy']);
		if ($this->submenuOf) {
			add_action('admin_menu', [$this, 'registerSubmenu']);
		}
	}

    /**
	 * Register the taxonomy with WordPress.
	 *
	 * @internal WordPress callback method - do not call directly
	 * @return void
	 */
	public function registerTaxonomy(): void
	{
		$args = $this->buildArgs();
		register_taxonomy($this->taxonomy, $this->objectType, $args);
	}

    /**
	 * Register a submenu item for the taxonomy.
	 *
	 * @internal WordPress callback method - do not call directly
	 * @return void
	 */
	public function registerSubmenu(): void
	{
		if (!$this->submenuOf) {
			return;
		}

		$taxonomy = get_taxonomy($this->taxonomy);
		if (!$taxonomy) {
			return;
		}

		$menuTitle = $taxonomy->labels->menu_name ?? $this->plural;
		$pageTitle = $taxonomy->labels->all_items ?? $menuTitle;
		$capability = $taxonomy->cap->manage_terms ?? 'manage_categories';
		$postType = $this->resolveObjectTypeForMenu();
		$slug = 'edit-tags.php?taxonomy=' . $this->taxonomy . '&post_type=' . $postType;

		add_submenu_page(
			$this->submenuOf,
			$pageTitle,
			$menuTitle,
			$capability,
			$slug
		);
	}

    /**
	 * Build the arguments array for register_taxonomy.
	 *
	 * @return array
	 */
	private function buildArgs(): array
	{
		$args = [
			'labels'            => $this->buildLabels(),
			'hierarchical'      => $this->hierarchical,
			'public'            => $this->showUi,
			'show_ui'           => $this->showUi,
			'show_in_menu'      => $this->submenuOf ? false : $this->showInMenu,
			'show_in_quick_edit' => $this->showInQuickEdit,
			'show_admin_column' => $this->showAdminColumn,
			'query_var'         => $this->queryVarArg ?: $this->queryVar,
			'rewrite'           => $this->rewrite,
			'show_in_nav_menus' => $this->showInNavMenus,
			'show_tagcloud'     => $this->showTagcloud,
			'show_in_rest'      => $this->showInRest,
		];

		if (!empty($this->restBase)) {
			$args['rest_base'] = $this->restBase;
		}

		if ($this->restControllerClass !== false) {
			$args['rest_controller_class'] = $this->restControllerClass;
		}

		if ($this->metaBoxCb !== null) {
			$args['meta_box_cb'] = $this->metaBoxCb;
		}

		if ($this->capabilities !== null) {
			$args['capabilities'] = $this->capabilities;
		}

		if ($this->updateCountCallback !== null) {
			$args['update_count_callback'] = $this->updateCountCallback;
		}

		if ($this->sort) {
			$args['sort'] = $this->sort;
		}

		return $args;
	}

    /**
	 * Resolve a post type for the taxonomy menu URL.
	 *
	 * @return string
	 */
	private function resolveObjectTypeForMenu(): string
	{
		$objectType = $this->objectType;
		if (is_array($objectType)) {
			$objectType = reset($objectType);
		}

		if (!is_string($objectType) || $objectType === '') {
			return 'post';
		}

		return $objectType;
	}

    /**
	 * Build the labels array.
	 *
	 * @return array
	 */
	private function buildLabels(): array
	{
		$singular = $this->singular;
		$plural = $this->plural;

		return [
			'name'              => $plural,
			'singular_name'     => $singular,
			/* translators: %s: Plural taxonomy label. */
			'search_items'      => sprintf(__('Search %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $plural),
			/* translators: %s: Plural taxonomy label. */
			'all_items'         => sprintf(__('All %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $plural),
			/* translators: %s: Singular taxonomy label. */
			'parent_item'       => $this->hierarchical ? sprintf(__('Parent %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular) : null,
			/* translators: %s: Singular taxonomy label. */
			'parent_item_colon' => $this->hierarchical ? sprintf(__('Parent %s:', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular) : null,
			/* translators: %s: Singular taxonomy label. */
			'edit_item'         => sprintf(__('Edit %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular taxonomy label. */
			'view_item'         => sprintf(__('View %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular taxonomy label. */
			'update_item'       => sprintf(__('Update %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular taxonomy label. */
			'add_new_item'      => sprintf(__('Add New %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular taxonomy label. */
			'new_item_name'     => sprintf(__('New %s Name', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			'menu_name'         => $plural,
		];
	}
}
