<?php
/**
 * WP Multisite WaaS Product Edit/Add New Admin Page.
 *
 * @package WP_Ultimo
 * @subpackage Admin_Pages
 * @since 2.0.0
 */

namespace WP_Ultimo\Admin_Pages;

// Exit if accessed directly
defined('ABSPATH') || exit;

use \WP_Ultimo\Models\Product;
use \WP_Ultimo\Database\Products\Product_Type;

/**
 * WP Multisite WaaS Product Edit/Add New Admin Page.
 */
class Product_Edit_Admin_Page extends Edit_Admin_Page {

	/**
	 * Holds the ID for this page, this is also used as the page slug.
	 *
	 * @var string
	 */
	protected $id = 'wp-ultimo-edit-product';

	/**
	 * Is this a top-level menu or a submenu?
	 *
	 * @since 1.8.2
	 * @var string
	 */
	protected $type = 'submenu';

	/**
	 * Object ID being edited.
	 *
	 * @since 1.8.2
	 * @var string
	 */
	public $object_id = 'product';

	/**
	 * Is this a top-level menu or a submenu?
	 *
	 * @since 1.8.2
	 * @var string
	 */
	protected $parent = 'none';

	/**
	 * This page has no parent, so we need to highlight another sub-menu.
	 *
	 * @since 2.0.0
	 * @var string
	 */
	protected $highlight_menu_slug = 'wp-ultimo-products';

	/**
	 * If this number is greater than 0, a badge with the number will be displayed alongside the menu title
	 *
	 * @since 1.8.2
	 * @var integer
	 */
	protected $badge_count = 0;

	/**
	 * Holds the admin panels where this page should be displayed, as well as which capability to require.
	 *
	 * To add a page to the regular admin (wp-admin/), use: 'admin_menu' => 'capability_here'
	 * To add a page to the network admin (wp-admin/network), use: 'network_admin_menu' => 'capability_here'
	 * To add a page to the user (wp-admin/user) admin, use: 'user_admin_menu' => 'capability_here'
	 *
	 * @since 2.0.0
	 * @var array
	 */
	protected $supported_panels = array(
		'network_admin_menu' => 'wu_edit_products',
	);

	/**
	 * Register ajax forms.
	 *
	 * @since 2.0.0
	 * @return void
	 */
	public function register_forms() {
		/*
		 * Adds the hooks to handle deletion.
		 */
		add_filter('wu_form_fields_delete_product_modal', array($this, 'product_extra_delete_fields'), 10, 2);

		add_action('wu_after_delete_product_modal', array($this, 'product_after_delete_actions'));

		add_filter("wu_page_{$this->id}_load", array($this, 'add_new_product_warning_message'));

	} // end register_forms;

	/**
	 * Adds the new product warning.
	 *
	 * @since 2.0.0
	 * @return void
	 */
	public function add_new_product_warning_message() {

		if (wu_request('wu-new-model')) {

			if (!$this->get_object() || $this->get_object()->get_type() !== Product_Type::PLAN) {

				return;

			} // end if;

			\WP_Ultimo\UI\Tours::get_instance()->create_tour('new_product_warning', array(
				array(
					'id'       => 'new-product-warning',
					'title'    => __('On adding a new product...', 'wp-ultimo'),
					'text'     => array(
						__("You just successfully added a new product to your WP Multisite WaaS network and that's awesome!", 'wp-ultimo'),
						__('Keep in mind that newly created products do not appear automatically in your checkout forms.', 'wp-ultimo'),
						__('To make a product available on registration, you will need to manually add it to the pricing table field of your checkout forms.', 'wp-ultimo'),
					),
					'buttons'  => array(
						array(
							'classes' => 'button wu-text-xs sm:wu-normal-case wu-float-left',
							'text'    => __('Go to Checkout Forms', 'wp-ultimo'),
							'url'     => wu_network_admin_url('wp-ultimo-checkout-forms'),
						)
					),
					'attachTo' => array(
						'element' => '#message.updated',
						'on'      => 'top',
					),
				),
			));

		} // end if;

	} // end add_new_product_warning_message;

	/**
	 * Adds the extra delete fields to the delete form.
	 *
	 * @since 2.0.0
	 *
	 * @param array  $fields The original fields.
	 * @param object $product The product object.
	 * @return array
	 */
	public function product_extra_delete_fields($fields, $product) {

		$custom_fields = array(
			're_assignment_product_id' => array(
				'type'        => 'model',
				'title'       => __('Re-assign Memberships to', 'wp-ultimo'),
				'placeholder' => __('Select Product...', 'wp-ultimo'),
				'tooltip'     => __('The product you select here will be assigned to all the memberships attached to the product you are deleting.', 'wp-ultimo'),
				'html_attr'   => array(
					'data-model'        => 'product',
					'data-value-field'  => 'id',
					'data-label-field'  => 'name',
					'data-search-field' => 'name',
					'data-max-items'    => 1,
					'data-exclude'      => json_encode(array($product->get_id()))
				),
			),
		);

		return array_merge($custom_fields, $fields);

	} // end product_extra_delete_fields;

	/**
	 * Adds the primary domain handling to the product deletion.
	 *
	 * @since 2.0.0
	 *
	 * @param object $product The product object.
	 * @return void
	 */
	public function product_after_delete_actions($product) {

		global $wpdb;

		$new_product_id = wu_request('re_assignment_product_id');

		$re_assignment_product = wu_get_product($new_product_id);

		if ($re_assignment_product) {

			$query = $wpdb->prepare(
				"UPDATE {$wpdb->base_prefix}wu_memberships
				 SET product_id = %d
				 WHERE product_id = %d",
				$re_assignment_product->get_id(),
				$product->get_id()
			);

			$wpdb->query($query); // phpcs:ignore

		} // end if;

	} // end product_after_delete_actions;

	/**
	 * Registers the necessary scripts and styles for this admin page.
	 *
	 * @since 2.0.0
	 * @return void
	 */
	public function register_scripts() {

		parent::register_scripts();

		wp_enqueue_media();

	} // end register_scripts;

	/**
	 * Allow child classes to register widgets, if they need them.
	 *
	 * @since 1.8.2
	 * @return void
	 */
	public function register_widgets() {

		parent::register_widgets();

		$this->add_fields_widget('description', array(
			'title'    => __('Description', 'wp-ultimo'),
			'position' => 'normal',
			'fields'   => array(
				'description' => array(
					'type'        => 'textarea',
					'title'       => __('Product Description', 'wp-ultimo'),
					'placeholder' => __('Tell your customers what this product is about.', 'wp-ultimo'),
					'tooltip'     => __('This description is made available for layouts and can be shown to end customers.', 'wp-ultimo'),
					'value'       => $this->get_object()->get_description(),
					'html_attr'   => array(
						'rows' => 3,
					),
				),
			),
		));

		$this->add_tabs_widget('product_options', array(
			'title'    => __('Product Options', 'wp-ultimo'),
			'position' => 'normal',
			'sections' => $this->get_product_option_sections(),
		));

		/*
		 * Handle legacy options for back-compat.
		 */
		$this->handle_legacy_options();

		$this->add_list_table_widget('events', array(
			'title'        => __('Events', 'wp-ultimo'),
			'table'        => new \WP_Ultimo\List_Tables\Inside_Events_List_Table(),
			'query_filter' => array($this, 'query_filter'),
		));

		$save_widget_args = apply_filters('wu_product_edit_save_widget', array(
			'html_attr' => array(
				'data-wu-app' => 'product_pricing',
				'data-state'  => json_encode(array(
					'is_recurring'  => $this->get_object()->is_recurring(),
					'pricing_type'  => $this->get_object()->get_pricing_type(),
					'has_trial'     => $this->get_object()->get_trial_duration() > 0,
					'has_setup_fee' => $this->get_object()->has_setup_fee(),
					'setup_fee'     => $this->get_object()->get_setup_fee(),
					'amount'        => $this->get_object()->get_formatted_amount(),
					'duration'      => $this->get_object()->get_duration(),
					'duration_unit' => $this->get_object()->get_duration_unit(),
				)),
			),
			'fields'    => array(
				// Fields for price
				'pricing_type'     => array(
					'type'              => 'select',
					'title'             => __('Pricing Type', 'wp-ultimo'),
					'placeholder'       => __('Select Pricing Type', 'wp-ultimo'),
					'desc'              => __('Products can be free, paid, or require further contact for pricing.', 'wp-ultimo'),
					'value'             => $this->get_object()->get_pricing_type(),
					'tooltip'           => '',
					'options'           => array(
						'paid'       => __('Paid', 'wp-ultimo'),
						'free'       => __('Free', 'wp-ultimo'),
						'contact_us' => __('Contact Us', 'wp-ultimo'),
					),
					'wrapper_html_attr' => array(
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-model' => 'pricing_type',
					),
				),
				'contact_us_label' => array(
					'type'              => 'text',
					'title'             => __('Button Label', 'wp-ultimo'),
					'placeholder'       => __('E.g. Contact us', 'wp-ultimo'),
					'desc'              => __('This will be used on the pricing table CTA button, as the label.', 'wp-ultimo'),
					'value'             => $this->get_object()->get_contact_us_label(),
					'wrapper_html_attr' => array(
						'v-show'  => "pricing_type == 'contact_us'",
						'v-cloak' => '1',
					),
				),
				'contact_us_link'  => array(
					'type'              => 'url',
					'title'             => __('Button Link', 'wp-ultimo'),
					'placeholder'       => __('E.g. https://contactus.page.com', 'wp-ultimo'),
					'desc'              => __('This will be used on the pricing table CTA button.', 'wp-ultimo'),
					'value'             => $this->get_object()->get_contact_us_link(),
					'wrapper_html_attr' => array(
						'v-show'  => "pricing_type == 'contact_us'",
						'v-cloak' => '1',
					),
				),
				'recurring'        => array(
					'type'              => 'toggle',
					'title'             => __('Is Recurring?', 'wp-ultimo'),
					'desc'              => __('Check this if this product has a recurring charge.', 'wp-ultimo'),
					'value'             => $this->get_object()->is_recurring(),
					'wrapper_html_attr' => array(
						'v-show'  => "pricing_type == 'paid'",
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-model' => 'is_recurring',
					),
				),
				'amount'           => array(
					'type'      => 'hidden',
					'html_attr' => array(
						'v-model' => 'amount',
					),
				),
				'_amount'          => array(
					'type'              => 'text',
					'title'             => __('Price', 'wp-ultimo'),
					'placeholder'       => __('Price', 'wp-ultimo'),
					'value'             => $this->get_object()->get_formatted_amount(),
					'tooltip'           => '',
					'money'             => true,
					'wrapper_html_attr' => array(
						'v-show'  => "pricing_type == 'paid' && !is_recurring ",
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-bind:name' => '""',
						'v-model'     => 'amount',
					),
				),
				'amount_group'     => array(
					'type'              => 'group',
					'title'             => __('Price', 'wp-ultimo'),
					// translators: placeholder %1$s is the amount, %2$s is the duration (such as 1, 2, 3), and %3$s is the unit (such as month, year, week)
					'desc'              => sprintf(__('The customer will be charged %1$s every %2$s %3$s(s).', 'wp-ultimo'), '{{ wu_format_money(amount) }}', '{{ duration }}', '{{ duration_unit }}'),
					'tooltip'           => '',
					'wrapper_html_attr' => array(
						'v-show'  => "is_recurring && pricing_type == 'paid'",
						'v-cloak' => '1',
					),
					'fields'            => array(
						'_amount'       => array(
							'type'            => 'text',
							'value'           => $this->get_object()->get_formatted_amount(),
							'placeholder'     => wu_format_currency('99'),
							'wrapper_classes' => '',
							'money'           => true,
							'html_attr'       => array(
								'v-bind:name' => '""',
								'v-model'     => 'amount',
							),
						),
						'duration'      => array(
							'type'            => 'number',
							'value'           => $this->get_object()->get_duration(),
							'placeholder'     => '',
							'wrapper_classes' => 'wu-mx-2 wu-w-1/3',
							'min'             => 0,
							'html_attr'       => array(
								'v-model' => 'duration',
								'steps'   => 1,
							),
						),
						'duration_unit' => array(
							'type'            => 'select',
							'value'           => $this->get_object()->get_duration_unit(),
							'placeholder'     => '',
							'wrapper_classes' => 'wu-w-2/3',
							'html_attr'       => array(
								'v-model' => 'duration_unit',
							),
							'options'         => array(
								'day'   => __('Days', 'wp-ultimo'),
								'week'  => __('Weeks', 'wp-ultimo'),
								'month' => __('Months', 'wp-ultimo'),
								'year'  => __('Years', 'wp-ultimo'),
							),
						),
					),
				),
				'billing_cycles'   => array(
					'type'              => 'number',
					'title'             => __('Billing Cycles', 'wp-ultimo'),
					'placeholder'       => __('E.g. 1', 'wp-ultimo'),
					'desc'              => __('How many times should we bill this customer. Leave 0 to charge until cancelled.', 'wp-ultimo'),
					'value'             => $this->get_object()->get_billing_cycles(),
					'tooltip'           => '',
					'wrapper_html_attr' => array(
						'v-show'  => "is_recurring && pricing_type == 'paid'",
						'v-cloak' => '1',
					),
				),
				'has_trial'        => array(
					'type'              => 'toggle',
					'title'             => __('Offer Trial', 'wp-ultimo'),
					'desc'              => __('Check if you want to add a trial period to this product.', 'wp-ultimo'),
					'value'             => $this->get_object()->has_trial(),
					'wrapper_html_attr' => array(
						'v-show'  => "pricing_type == 'paid'",
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-model' => 'has_trial',
					),
				),
				'trial_group'      => array(
					'type'              => 'group',
					'title'             => __('Trial', 'wp-ultimo'),
					'tooltip'           => '',
					'wrapper_html_attr' => array(
						'v-show'  => "has_trial && pricing_type == 'paid'",
						'v-cloak' => '1',
					),
					'fields'            => array(
						'trial_duration'      => array(
							'type'            => 'number',
							'value'           => $this->get_object()->get_trial_duration(),
							'placeholder'     => '',
							'wrapper_classes' => 'wu-mr-2 wu-w-1/3',
						),
						'trial_duration_unit' => array(
							'type'            => 'select',
							'value'           => $this->get_object()->get_trial_duration_unit(),
							'placeholder'     => '',
							'wrapper_classes' => 'wu-w-2/3',
							'options'         => array(
								'day'   => __('Days', 'wp-ultimo'),
								'week'  => __('Weeks', 'wp-ultimo'),
								'month' => __('Months', 'wp-ultimo'),
								'year'  => __('Years', 'wp-ultimo'),
							),
						),
					),
				),
				'has_setup_fee'    => array(
					'type'              => 'toggle',
					'title'             => __('Add Setup Fee?', 'wp-ultimo'),
					'desc'              => __('Check if you want to add a setup fee.', 'wp-ultimo'),
					'value'             => $this->get_object()->has_setup_fee(),
					'wrapper_html_attr' => array(
						'v-show'  => "pricing_type == 'paid'",
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-model' => 'has_setup_fee',
					),
				),
				'setup_fee'        => array(
					'type'      => 'hidden',
					'html_attr' => array(
						'v-model' => 'setup_fee',
					),
				),
				'_setup_fee'       => array(
					'type'              => 'text',
					'money'             => true,
					'title'             => __('Setup Fee', 'wp-ultimo'),
					'desc'              => __('The setup fee will be added to the first charge, in addition to the regular price of the product.', 'wp-ultimo'),
					// translators: %s is a price placeholder value.
					'placeholder'       => sprintf(__('E.g. %s', 'wp-ultimo'), wu_format_currency(199)),
					'value'             => $this->get_object()->get_formatted_amount('setup_fee'),
					'wrapper_html_attr' => array(
						'v-show'  => "has_setup_fee && pricing_type == 'paid'",
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-model' => 'setup_fee',
					),
				),
			),
		), $this->get_object());

		$this->add_save_widget('save', $save_widget_args);

		$this->add_fields_widget('active', array(
			'title'  => __('Active', 'wp-ultimo'),
			'fields' => array(
				'active' => array(
					'type'  => 'toggle',
					'title' => __('Active', 'wp-ultimo'),
					'desc'  => __('Use this option to manually enable or disable this product for new sign-ups.', 'wp-ultimo'),
					'value' => $this->get_object()->is_active(),
				),
			),
		));

		$this->add_fields_widget('image', array(
			'title'  => __('Product Image', 'wp-ultimo'),
			'fields' => array(
				'featured_image_id' => array(
					'type'    => 'image',
					'stacked' => true,
					'title'   => __('Product Image', 'wp-ultimo'),
					'desc'    => __('This image is used on product list tables and other places.', 'wp-ultimo'),
					'value'   => $this->get_object()->get_featured_image_id(),
					'img'     => $this->get_object()->get_featured_image(),
				),
			),
		));

	} // end register_widgets;

	/**
	 * Handles legacy advanced options for plans.
	 *
	 * @since 2.0.0
	 * @return void
	 */
	public function handle_legacy_options() {

		global $wp_filter;

		$tabs = apply_filters_deprecated('wu_plans_advanced_options_tabs', array(
			array(),
		), '2.0.0', 'wu_product_options_sections');

		if (!isset($wp_filter['wu_plans_advanced_options_after_panels'])) {

			return;

		} // end if;

		wp_enqueue_style('wu-legacy-admin-tabs', wu_get_asset('legacy-admin-tabs.css', 'css'), false, wu_get_version());

		$priorities = $wp_filter['wu_plans_advanced_options_after_panels']->callbacks;

		$fields = array(
			'heading' => array(
				'type'  => 'header',
				'title' => __('Legacy Options', 'wp-ultimo'),
				// translators: %s is the name of legacy add-ons.
				'desc'  => sprintf(__('Options for %s, and others.', 'wp-ultimo'), implode(', ', $tabs)),
			),
		);

		foreach ($priorities as $priority => $callbacks) {

			foreach ($callbacks as $id => $callable) {

				$fields[$id] = array(
					'type'    => 'html',
					'classes' => 'wu--mt-2',
					'content' => function() use ($callable) {

						call_user_func($callable['function'], new \WU_Plan($this->get_object()));

					},
				);

			} // end foreach;

		} // end foreach;

		$this->add_fields_widget('legacy-options', array(
			'title'                 => __('Legacy Options', 'wp-ultimo'),
			'position'              => 'normal',
			'fields'                => $fields,
			'classes'               => 'wu-legacy-options-panel',
			'field_wrapper_classes' => 'wu-w-full wu-box-border wu-items-center wu-flex wu-justify-between wu-p-4 wu-m-0 wu-border-t wu-border-l-0 wu-border-r-0 wu-border-b-0 wu-border-gray-300 wu-border-solid',
			'html_attr'             => array(
				'style' => 'margin-top: -5px;',
			),
		));

	} // end handle_legacy_options;

	/**
	 * Returns the list of sections and its fields for the product page.
	 *
	 * Can be filtered via 'wu_product_options_sections'.
	 *
	 * @see inc/managers/class-limitation-manager.php
	 *
	 * @since 2.0.0
	 * @return array
	 */
	protected function get_product_option_sections() {

		$sections = array(
			'general' => array(
				'title'  => __('General', 'wp-ultimo'),
				'desc'   => __('General product options such as product slug, type, etc.', 'wp-ultimo'),
				'icon'   => 'dashicons-wu-globe',
				'state'  => array(
					'slug'         => $this->get_object()->get_slug(),
					'product_type' => $this->get_object()->get_type(),
				),
				'fields' => array(
					'slug'                               => array(
						'type'        => 'text',
						'title'       => __('Product Slug', 'wp-ultimo'),
						'placeholder' => __('e.g. premium', 'wp-ultimo'),
						'desc'        => __('This serves as a id to the product in a number of different contexts.', 'wp-ultimo'),
						'value'       => $this->get_object()->get_slug(),
						'tooltip'     => __('Lowercase alpha-numeric characters with dashes or underlines. No spaces allowed.', 'wp-ultimo'),
						'html_attr'   => array(
							'v-on:input'   => 'slug = $event.target.value.toLowerCase().replace(/[^a-z0-9-_]+/g, "")',
							'v-bind:value' => 'slug',
						),
					),
					// Fields for price
					'type'                               => array(
						'type'        => 'select',
						'title'       => __('Product Type', 'wp-ultimo'),
						'placeholder' => __('Product Type', 'wp-ultimo'),
						'desc'        => __('Different product types have different options.', 'wp-ultimo'),
						'value'       => $this->get_object()->get_type(),
						'tooltip'     => '',
						'options'     => Product_Type::to_array(),
						'html_attr'   => array(
							'v-model' => 'product_type',
						),
					),
					'modules[customer_user_role][limit]' => array(
						'title'             => __('Customer Role', 'wp-ultimo'),
						'desc'              => __('Select the role WP Multisite WaaS should use when adding the user to their newly created site.', 'wp-ultimo'),
						'type'              => 'select',
						'value'             => $this->get_object()->get_customer_role(),
						'default'           => 'administrator',
						'options'           => fn() => wu_get_roles_as_options(true),
						'wrapper_html_attr' => array(
							'v-show'  => 'product_type === "plan"',
							'v-cloak' => 1,
						),
					),
				),
			),
		);

		$sections['ups-and-downs'] = array(
			'title'  => __('Up & Downgrades', 'wp-ultimo'),
			'desc'   => __('Settings related to upgrade and downgrade flows.', 'wp-ultimo'),
			'icon'   => 'dashicons-wu-shop',
			'v-show' => 'product_type === "plan"',
			'state'  => array(),
			'fields' => array(
				'group'            => array(
					'title'       => __('Plan Group', 'wp-ultimo'),
					'desc'        => __('Add related plans to the same group to have them show up as upgrade/downgrade paths.', 'wp-ultimo'),
					'placeholder' => __('Type and press enter to search and/or add.', 'wp-ultimo'),
					'type'        => 'select',
					'value'       => $this->get_object()->get_group(),
					'options'     => array_merge(array('' => __('Select Group', 'wp-ultimo')), wu_get_product_groups()),
					'html_attr'   => array(
						'data-selectize-categories' => 999,
						'data-max-items'            => 1,
					),
				),
				'list_order'       => array(
					'title'       => __('Product Order', 'wp-ultimo'),
					'desc'        => __('Plans are shown in the order determined by this parameter, from the lowest to the highest.', 'wp-ultimo'),
					'placeholder' => __('Type and press enter to search and/or add.', 'wp-ultimo'),
					'type'        => 'number',
					'value'       => $this->get_object()->get_list_order(),
				),
				'available_addons' => array(
					'type'        => 'model',
					'title'       => __('Offer Add-ons', 'wp-ultimo'),
					'placeholder' => __('Search for a package or service', 'wp-ultimo'),
					'desc'        => __('This products will be offered inside upgrade/downgrade forms as order bumps.', 'wp-ultimo'),
					'html_attr'   => array(
						'data-exclude'      => implode(',', array_keys(wu_get_plans_as_options())),
						'data-model'        => 'product',
						'data-value-field'  => 'id',
						'data-label-field'  => 'name',
						'data-search-field' => 'name',
						'data-max-items'    => 99,
						'data-selected'     => json_encode(wu_get_products(array(
							'id__in'     => $this->get_object()->get_available_addons(),
							'id__not_in' => array_keys(wu_get_plans_as_options()),
						))),
					),
				),
			),
		);

		$sections['price-variations'] = array(
			'title'  => __('Price Variations', 'wp-ultimo'),
			'desc'   => __('Discounts for longer membership commitments.', 'wp-ultimo'),
			'icon'   => 'dashicons-wu-price-tag',
			'state'  => array(
				'enable_price_variations' => !empty($this->get_object()->get_price_variations()),
				'price_variations'        => $this->get_object()->get_price_variations(),
			),
			'fields' => array(
				'enable_price_variations' => array(
					'type'      => 'toggle',
					'title'     => __('Enable Price Variations', 'wp-ultimo'),
					'desc'      => __('Price Variations are an easy way to offer discounted prices for longer subscription commitments.', 'wp-ultimo'),
					'value'     => false,
					'html_attr' => array(
						'v-model' => 'enable_price_variations',
					),
				),
				'price_variations'        => array(
					'type'              => 'group',
					// translators: 1 is the price, 2 is the duration and 3 the duration unit
					'desc'              => sprintf(__('A discounted price of %1$s will be used when memberships are created with the recurrence of %2$s %3$s(s) instead of the regular period.', 'wp-ultimo'), '{{ wu_format_money(price_variation.amount) }}', '{{ price_variation.duration }}', '{{ price_variation.duration_unit }}'),
					'tooltip'           => '',
					'wrapper_classes'   => 'wu-relative',
					'wrapper_html_attr' => array(
						'v-for'   => '(price_variation, index) in price_variations',
						'v-show'  => 'enable_price_variations',
						'v-cloak' => '1',
					),
					'fields'            => array(
						'price_variations_remove'        => array(
							'type'            => 'note',
							'desc'            => sprintf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => price_variations.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', __('Remove', 'wp-ultimo')),
							'wrapper_classes' => 'wu-absolute wu-top-0 wu-right-0',
						),
						'price_variations_duration'      => array(
							'type'            => 'number',
							'title'           => __('Duration', 'wp-ultimo'),
							'placeholder'     => '',
							'wrapper_classes' => 'wu-w-1/3',
							'min'             => 1,
							'html_attr'       => array(
								'v-model'     => 'price_variation.duration',
								'steps'       => 1,
								'v-bind:name' => '"price_variations[" + index + "][duration]"',
							),
						),
						'price_variations_duration_unit' => array(
							'type'            => 'select',
							'title'           => __('Period', 'wp-ultimo'),
							'placeholder'     => '',
							'wrapper_classes' => 'wu-w-1/3 wu-mx-2',
							'html_attr'       => array(
								'v-model'     => 'price_variation.duration_unit',
								'v-bind:name' => '"price_variations[" + index + "][duration_unit]"',
							),
							'options'         => array(
								'day'   => __('Days', 'wp-ultimo'),
								'week'  => __('Weeks', 'wp-ultimo'),
								'month' => __('Months', 'wp-ultimo'),
								'year'  => __('Years', 'wp-ultimo'),
							),
						),
						// Bind the amount of the price variation to another field so we don't send the formatted value to the server.
						'price_variations_amount'        => array(
							'type'      => 'hidden',
							'html_attr' => array(
								'v-bind:value' => 'price_variation.amount',
								'v-bind:name'  => '"price_variations[" + index + "][amount]"'
							),
						),
						'_price_variations_amount'       => array(
							'type'            => 'text',
							'title'           => __('New Price', 'wp-ultimo'),
							'placeholder'     => wu_format_currency('99'),
							'wrapper_classes' => 'wu-w-1/3',
							'money'           => true,
							'html_attr'       => array(
								'v-model'     => 'price_variation.amount',
								'v-bind:name' => '""',
							),
						),
					),
				),
				'repeat'                  => array(
					'type'              => 'submit',
					'title'             => __('Add new Price Variation', 'wp-ultimo'),
					'classes'           => 'button wu-self-end',
					'wrapper_classes'   => 'wu-bg-whiten wu-items-end',
					'wrapper_html_attr' => array(
						'v-show'  => 'enable_price_variations',
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-on:click.prevent' => '() => price_variations.push({
							duration: 1,
							duration_unit: "month",
							amount: get_value("wu_product_pricing").amount,
						})',
					),
				),
			),
		);

		$sections['taxes'] = array(
			'title'  => __('Taxes', 'wp-ultimo'),
			'desc'   => __('Tax settings for your products.', 'wp-ultimo'),
			'icon'   => 'dashicons-wu-credit',
			'state'  => array(
				'taxable' => $this->get_object()->is_taxable(),
			),
			'fields' => array(
				'taxable'      => array(
					'type'      => 'toggle',
					'title'     => __('Is Taxable?', 'wp-ultimo'),
					'desc'      => __('Enable this if you plan to collect taxes for this product.', 'wp-ultimo'),
					'value'     => $this->get_object()->is_taxable(),
					'html_attr' => array(
						'v-model' => 'taxable',
					),
				),
				'tax_category' => array(
					'type'              => 'select',
					'title'             => __('Tax Category', 'wp-ultimo'),
					'desc'              => __('Select the product tax category.', 'wp-ultimo'),
					'value'             => $this->get_object()->get_tax_category(),
					'options'           => 'wu_get_tax_categories_as_options',
					'wrapper_html_attr' => array(
						'v-cloak' => '1',
						'v-show'  => 'require("taxable", true)',
					),
				),
			),
		);

		$sections['allowed_templates'] = array(
			'title'  => __('Site Templates', 'wp-ultimo'),
			'desc'   => __('Limit which site templates are available for this particular template.', 'wp-ultimo'),
			'icon'   => 'dashicons-wu-grid1 wu-align-text-bottom',
			'v-show' => "get_state_value('product_type', 'none') !== 'service'",
			'state'  => array(
				'allow_site_templates'         => $this->get_object()->get_limitations()->site_templates->is_enabled(),
				'site_template_selection_mode' => $this->get_object()->get_limitations()->site_templates->get_mode(),
				'pre_selected_template'        => $this->get_object()->get_limitations()->site_templates->get_pre_selected_site_template(),
			),
			'fields' => array(
				'modules[site_templates][enabled]' => array(
					'type'              => 'toggle',
					'title'             => __('Allow Site Templates', 'wp-ultimo'),
					'desc'              => __('Toggle this option on to allow this plan to use Site Templates. If this option is disabled, sign-ups on this plan will get a default WordPress site.', 'wp-ultimo'),
					'wrapper_html_attr' => array(
						'v-cloak' => '1',
					),
					'html_attr'         => array(
						'v-model' => 'allow_site_templates',
					),
				),
				'modules[site_templates][mode]'    => array(
					'type'              => 'select',
					'title'             => __('Site Template Selection Mode', 'wp-ultimo'),
					'placeholder'       => __('Site Template Selection Mode', 'wp-ultimo'),
					'desc'              => __('Select the type of limitation you want to apply.', 'wp-ultimo'),
					'tooltip'           => __('"Default" will follow the settings of the checkout form: if you have a template selection field in there, all the templates selected will show up. If no field is present, then a default WordPress site will be created. <br><br>"Assign Site Template" forces new accounts with this plan to use a particular template site (this option removes the template selection field from the signup, if one exists). <br><br>Finally, "Choose Available Site Templates", overrides the templates selected on the checkout form with the templates selected here, while also giving you the chance of pre-select a template to be used as default.', 'wp-ultimo'),
					'value'             => 'default',
					'options'           => array(
						'default'                    => __('Default', 'wp-ultimo'),
						'assign_template'            => __('Assign Site Template', 'wp-ultimo'),
						'choose_available_templates' => __('Choose Available Site Templates', 'wp-ultimo')
					),
					'html_attr'         => array(
						'v-model' => 'site_template_selection_mode',
					),
					'wrapper_html_attr' => array(
						'v-cloak' => '1',
						'v-show'  => 'allow_site_templates',
					),
				),
				'templates'                        => array(
					'type'              => 'html',
					'title'             => __('Site Templates', 'wp-ultimo'),
					'desc'              => esc_attr(sprintf('{{ site_template_selection_mode === "assign_template" ? "%s" : "%s" }}', __('Select the Site Template to assign.', 'wp-ultimo'), __('Customize the access level of each Site Template below.', 'wp-ultimo'))),
					'wrapper_html_attr' => array(
						'v-cloak' => '1',
						'v-show'  => "allow_site_templates && site_template_selection_mode !== 'default'",
					),
					'content'           => fn() => $this->get_site_template_selection_list($this->get_object()),
				),
			),
		);

		return apply_filters('wu_product_options_sections', $sections, $this->get_object());

	} // end get_product_option_sections;

	/**
	 * Returns the HTML markup for the plugin selector list.
	 *
	 * @since 2.0.0
	 *
	 * @param \WP_Ultimo\Models\Product $product The product being edited.
	 * @return string
	 */
	public function get_site_template_selection_list($product) {

		$all_templates = wu_get_site_templates();

		return wu_get_template_contents('limitations/site-template-selector', array(
			'templates' => $all_templates,
			'product'   => $product,
		));

	} // end get_site_template_selection_list;

	/**
	 * Returns the title of the page.
	 *
	 * @since 2.0.0
	 * @return string Title of the page.
	 */
	public function get_title() {

		return $this->edit ? __('Edit Product', 'wp-ultimo') : __('Add new Product', 'wp-ultimo');

	} // end get_title;

	/**
	 * Returns the title of menu for this page.
	 *
	 * @since 2.0.0
	 * @return string Menu label of the page.
	 */
	public function get_menu_title() {

		return __('Edit Product', 'wp-ultimo');

	} // end get_menu_title;

	/**
	 * Returns the action links for that page.
	 *
	 * @since 1.8.2
	 * @return array
	 */
	public function action_links() {

		$actions = array();

		if ($this->get_object()->get_type() === 'plan' && $this->edit) {

			$shareable_link = $this->get_object()->get_shareable_link();

			$actions[] = array(
				'url'     => '#',
				'label'   => __('Click to copy Shareable Link', 'wp-ultimo'),
				'icon'    => 'wu-attachment',
				'classes' => 'wu-copy',
				'attrs'   => 'data-clipboard-text="' . esc_attr($shareable_link) . '"',
			);

		} // end if;

		return $actions;

	} // end action_links;

	/**
	 * Returns the labels to be used on the admin page.
	 *
	 * @since 2.0.0
	 * @return array
	 */
	public function get_labels() {

		return array(
			'edit_label'          => __('Edit Product', 'wp-ultimo'),
			'add_new_label'       => __('Add new Product', 'wp-ultimo'),
			'updated_message'     => __('Product updated with success!', 'wp-ultimo'),
			'title_placeholder'   => __('Enter Product Name', 'wp-ultimo'),
			'title_description'   => __('This name will be used on pricing tables, invoices, and more.', 'wp-ultimo'),
			'save_button_label'   => __('Save Product', 'wp-ultimo'),
			'save_description'    => '',
			'delete_button_label' => __('Delete Product', 'wp-ultimo'),
			'delete_description'  => __('Be careful. This action is irreversible.', 'wp-ultimo'),
		);

	} // end get_labels;

	/**
	 * Filters the list table to return only relevant events.
	 *
	 * @since 2.0.0
	 *
	 * @param array $args Query args passed to the list table.
	 * @return array Modified query args.
	 */
	public function query_filter($args) {

		$extra_args = array(
			'object_type' => 'product',
			'object_id'   => absint($this->get_object()->get_id()),
		);

		return array_merge($args, $extra_args);

	} // end query_filter;

	/**
	 * Returns the object being edit at the moment.
	 *
	 * @since 2.0.0
	 * @return \WP_Ultimo\Models\Product
	 */
	public function get_object() {

		if ($this->object !== null) {

			return $this->object;

		} // end if;

		if (isset($_GET['id'])) {

			$query = new \WP_Ultimo\Database\Products\Product_Query;

			$item = $query->get_item_by('id', $_GET['id']);

			if (!$item) {

				wp_redirect(wu_network_admin_url('wp-ultimo-products'));

				exit;

			} // end if;

			$this->object = $item;

			return $this->object;

		} // end if;

		$this->object = new Product;

		return $this->object;

	} // end get_object;
	/**
	 * Products have titles.
	 *
	 * @since 2.0.0
	 */
	public function has_title(): bool {

		return true;

	} // end has_title;

	/**
	 * Should implement the processes necessary to save the changes made to the object.
	 *
	 * @since 2.0.0
	 * @return void
	 */
	public function handle_save() {
		/*
		 * Set the recurring value to zero if the toggle is disabled.
		 */
		if (!wu_request('recurring')) {

			$_POST['recurring'] = false;

		} // end if;

		if (!wu_request('legacy_options')) {

			$_POST['legacy_options'] = false;

		} // end if;

		if (!wu_request('featured_plan')) {

			$_POST['featured_plan'] = false;

		} // end if;

		/*
		 * Set the setup fee value to zero if the toggle is disabled.
		 */
		if (!wu_request('has_setup_fee')) {

			$_POST['setup_fee'] = 0;

		} // end if;

		/*
		 * Disabled Trial
		 */
		if (!wu_request('has_trial')) {

			$_POST['trial_duration'] = 0;

		} // end if;

		/*
		 * Set the setup fee value to zero if the toggle is disabled.
		 */
		if (!wu_request('price_variations')) {

			$_POST['price_variations'] = array();

		} // end if;

		/*
		 * Set the taxable value to zero if the toggle is disabled.
		 */
		if (!wu_request('taxable')) {

			$_POST['taxable'] = 0;

		} // end if;

		parent::handle_save();

	} // end handle_save;

} // end class Product_Edit_Admin_Page;