<?php

namespace Storeplugin\PDFInvoice\Framework\PostType;

defined('ABSPATH') || exit;

use Storeplugin\PDFInvoice\Framework\Contracts\CPTInterface;

/**
 * Post Type Builder
 *
 * Fluent builder pattern for registering custom post types.
 */
class CPT implements CPTInterface
{
	/**
	 * Post type slug
	 *
	 * @var string
	 */
	private string $postType;

	/**
	 * 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 type description
	 *
	 * @var string
	 */
	private string $description = '';

	/**
	 * Whether the post type is public
	 *
	 * @var bool
	 */
	private bool $public = false;

	/**
	 * Whether the post type is publicly queryable
	 *
	 * @var bool
	 */
	private bool $publiclyQueryable = false;

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

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

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

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

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

	/**
	 * Capability type
	 *
	 * @var string
	 */
	private string $capabilityType = 'post';

	/**
	 * Whether the post type has an archive
	 *
	 * @var bool
	 */
	private bool $hasArchive = false;

	/**
	 * Whether the post type is hierarchical
	 *
	 * @var bool
	 */
	private bool $hierarchical = false;

	/**
	 * Menu position
	 *
	 * @var int|null
	 */
	private ?int $menuPosition = null;

	/**
	 * Menu icon
	 *
	 * @var string
	 */
	private string $menuIcon = '';

	/**
	 * Supported features
	 *
	 * @var array
	 */
	private array $supports = ['title', 'editor'];

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

	/**
	 * Taxonomies for this post type
	 *
	 * @var array
	 */
	private array $taxonomies = [];

	/**
	 * Callback for registering meta boxes
	 *
	 * @var callable|null
	 */
	private $metaBoxCallback = null;

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

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

	/**
	 * Set the post type description.
	 *
	 * @param string $description The description.
	 * @return self
	 */
	public function description(string $description = ''): self
	{
		$this->description = $description;
		return $this;
	}

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

	/**
	 * Set whether the post type is publicly queryable.
	 *
	 * @param bool $queryable Whether publicly queryable.
	 * @return self
	 */
	public function publiclyQueryable(bool $queryable = true): self
	{
		$this->publiclyQueryable = $queryable;
		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 post type 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 use query var.
	 *
	 * @param bool $query Whether to use query var.
	 * @return self
	 */
	public function queryVar(bool $query = true): self
	{
		$this->queryVar = $query;
		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 the capability type.
	 *
	 * @param string $type The capability type.
	 * @return self
	 */
	public function capabilityType(string $type = 'post'): self
	{
		$this->capabilityType = $type;
		return $this;
	}

	/**
	 * Set whether the post type has an archive.
	 *
	 * @param bool $hasArchive Whether to have archive.
	 * @return self
	 */
	public function hasArchive(bool $hasArchive = true): self
	{
		$this->hasArchive = $hasArchive;
		return $this;
	}

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

	/**
	 * Set the menu position.
	 *
	 * @param int|null $position The menu position.
	 * @return self
	 */
	public function menuPosition(?int $position = null): self
	{
		$this->menuPosition = $position;
		return $this;
	}

	/**
	 * Set the menu icon.
	 *
	 * @param string $icon The dashicon class or URL.
	 * @return self
	 */
	public function menuIcon(string $icon = ''): self
	{
		$this->menuIcon = $icon;
		return $this;
	}

	/**
	 * Set the supported features.
	 *
	 * @param array $supports Array of supported features.
	 * @return self
	 */
	public function supports(array $supports): self
	{
		$this->supports = $supports;
		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 taxonomies for this post type.
	 *
	 * @param array $taxonomies Array of taxonomy names.
	 * @return self
	 */
	public function taxonomies(array $taxonomies): self
	{
		$this->taxonomies = $taxonomies;
		return $this;
	}

	/**
	 * Set callback for registering meta boxes.
	 *
	 * @param callable $callback Callback function that receives WP_Post object.
	 * @return self
	 */
	public function withMetaBoxCallback(callable $callback): self
	{
		$this->metaBoxCallback = $callback;
		return $this;
	}

    /**
	 * Register the post type with WordPress.
	 *
	 * @internal WordPress callback method - do not call directly
	 * @return void
	 */
	public function registerPostType(): void
	{
		$args = $this->buildArgs();
		register_post_type($this->postType, $args);
	}

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

    /**
	 * Build the arguments array for register_post_type.
	 *
	 * @return array
	 */
	private function buildArgs(): array
	{
		$args = [
			'labels'             => $this->buildLabels(),
			'description'        => $this->description ?: ucfirst($this->singular) . ' custom post type.',
			'public'             => $this->public,
			'publicly_queryable' => $this->publiclyQueryable,
			'show_ui'            => $this->showUi,
			'show_in_menu'       => $this->submenuOf ? false : $this->showInMenu,
			'query_var'          => $this->queryVar,
			'rewrite'            => $this->rewrite,
			'capability_type'    => $this->capabilityType,
			'has_archive'        => $this->hasArchive,
			'hierarchical'       => $this->hierarchical,
			'menu_position'      => $this->menuPosition,
			'menu_icon'          => $this->menuIcon,
			'supports'           => $this->supports,
			'show_in_rest'       => $this->showInRest,
		];

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

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

		return $args;
	}

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

		$postTypeObject = get_post_type_object($this->postType);

		if (!$postTypeObject) {
			return;
		}

		$menuTitle = $postTypeObject->labels->menu_name ?? $this->plural;
		$pageTitle = $postTypeObject->labels->all_items ?? $menuTitle;
		$capability = $postTypeObject->cap->edit_posts ?? 'edit_posts';

		add_submenu_page(
			$this->submenuOf,
			$pageTitle,
			$menuTitle,
			$capability,
			'edit.php?post_type=' . $this->postType
		);
	}

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

		return [
			'name'                  => $plural,
			'singular_name'         => $singular,
			'menu_name'             => $plural,
			'name_admin_bar'        => $singular,
			'add_new'               => __('Add New', 'storeinvoice-print-pdf-invoices-and-packing-slips'),
			/* translators: %s: Singular post type label. */
			'add_new_item'          => sprintf(__('Add New %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular post type label. */
			'new_item'              => sprintf(__('New %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular post type label. */
			'edit_item'             => sprintf(__('Edit %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular post type label. */
			'view_item'             => sprintf(__('View %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Plural post type label. */
			'view_items'            => sprintf(__('View %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $plural),
			/* translators: %s: Plural post type label. */
			'all_items'             => sprintf(__('All %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $plural),
			/* translators: %s: Plural post type label. */
			'search_items'          => sprintf(__('Search %s', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $plural),
			/* translators: %s: Plural post type label. */
			'parent_item_colon'     => sprintf(__('Parent %s:', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $plural),
			/* translators: %s: Lowercase plural post type label. */
			'not_found'             => sprintf(__('No %s found.', 'storeinvoice-print-pdf-invoices-and-packing-slips'), strtolower($plural)),
			/* translators: %s: Lowercase plural post type label. */
			'not_found_in_trash'    => sprintf(__('No %s found in Trash.', 'storeinvoice-print-pdf-invoices-and-packing-slips'), strtolower($plural)),
			/* translators: %s: Singular post type label. */
			'featured_image'        => sprintf(_x('%s Cover Image', 'Overrides the "Featured Image" phrase', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			'set_featured_image'    => _x('Set cover image', 'Overrides the "Set featured image" phrase', 'storeinvoice-print-pdf-invoices-and-packing-slips'),
			'remove_featured_image' => _x('Remove cover image', 'Overrides the "Remove featured image" phrase', 'storeinvoice-print-pdf-invoices-and-packing-slips'),
			'use_featured_image'    => _x('Use as cover image', 'Overrides the "Use as featured image" phrase', 'storeinvoice-print-pdf-invoices-and-packing-slips'),
			/* translators: %s: Singular post type label. */
			'archives'              => sprintf(_x('%s archives', 'The post type archive label', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Lowercase singular post type label. */
			'insert_into_item'      => sprintf(_x('Insert into %s', 'Overrides the "Insert into post" phrase', 'storeinvoice-print-pdf-invoices-and-packing-slips'), strtolower($singular)),
			/* translators: %s: Lowercase singular post type label. */
			'uploaded_to_this_item' => sprintf(_x('Uploaded to this %s', 'Overrides the "Uploaded to this post" phrase', 'storeinvoice-print-pdf-invoices-and-packing-slips'), strtolower($singular)),
			/* translators: %s: Lowercase plural post type label. */
			'filter_items_list'     => sprintf(_x('Filter %s list', 'Screen reader text for the filter links', 'storeinvoice-print-pdf-invoices-and-packing-slips'), strtolower($plural)),
			/* translators: %s: Singular post type label. */
			'items_list_navigation' => sprintf(_x('%s list navigation', 'Screen reader text for the pagination', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
			/* translators: %s: Singular post type label. */
			'items_list'            => sprintf(_x('%s list', 'Screen reader text for the items list', 'storeinvoice-print-pdf-invoices-and-packing-slips'), $singular),
		];
	}
}
