<?php
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * Main WC_Appointments_Admin class.
 */
class WC_Appointments_Admin {

	private static self $_this;

	/**
	 * Constructor
	 */
	public function __construct() {
		self::$_this = $this;

		// Add to <head> in admin.
		add_action( 'admin_head', [ $this, 'appointments_admin_head' ], 11, 0 );

		// Load correct list table classes for current screen.
		add_action( 'current_screen', [ $this, 'setup_screen' ] );
		add_action( 'current_screen', [ $this, 'add_whats_new_help_tab' ], 20 );
		add_action( 'check_ajax_referer', [ $this, 'setup_screen' ], 10, 2 );

		add_action( 'woocommerce_product_duplicate', [ $this, 'woocommerce_duplicate_product' ], 10, 2 );
		add_filter( 'post_updated_messages', [ $this, 'post_updated_messages' ] );
		add_action( 'admin_init', [ $this, 'init_tabs' ], 10, 0 );
		add_action( 'admin_init', [ $this, 'include_meta_box_handlers' ], 10, 0 );
		add_action( 'admin_init', [ $this, 'redirect_new_add_appointment_url' ], 10, 0 );
		add_action( 'woocommerce_product_options_inventory_product_data', [ $this, 'appointment_inventory' ], 10, 0 );
		add_filter( 'product_type_options', [ $this, 'product_type_options' ] );
		add_filter( 'product_type_selector', [ $this, 'product_type_selector' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'styles_and_scripts' ] );
		add_action( 'woocommerce_product_options_general_product_data', [ $this, 'appointment_general' ], 10, 0 );
		add_action( 'load-options-general.php', [ $this, 'reset_ics_exporter_timezone_cache' ], 10, 0 );
		add_action( 'woocommerce_before_order_itemmeta', [ $this, 'appointment_display' ], 10, 3 );
		add_filter( 'woocommerce_get_settings_pages', [ $this, 'add_settings_page' ], 10, 1 );
		add_filter( 'woocommerce_template_overrides_scan_paths', [ $this, 'template_scan_path' ] );
		add_filter( 'woocommerce_product_type_query', [ $this, 'maybe_override_product_type' ], 10, 2 );

		// Saving data.
		add_action( 'woocommerce_process_product_meta', [ $this, 'save_product_data' ], 20 );
		add_action( 'woocommerce_admin_process_product_object', [ $this, 'set_props' ], 20 );
		add_action( 'before_delete_post', [ $this, 'before_delete_product' ], 11 );

		include __DIR__ . '/class-wc-appointments-admin-menus.php';
		include __DIR__ . '/class-wc-appointments-admin-staff-profile.php';
		include __DIR__ . '/export/class-wc-appointments-export-manager.php';
		include __DIR__ . '/class-wc-appointments-admin-exporters.php';
	}

	/**
	 * Add meta tags and scripts to <head> in admin.
	 *
	 * @since 4.10.7
	 */
	public function appointments_admin_head(): void {
		$client_id = sanitize_text_field( $_POST['wc_appointments_gcal_client_id'] ?? '' ) ?: get_option( 'wc_appointments_gcal_client_id' );

		if ( $client_id ) {
			echo '<meta name="google-signin-client_id" content="' . $client_id . '">';
		}

		// Include appointment modal template on the appointments list screen
		$this->include_appointment_modal_template();
	}

	/**
	 * Include appointment modal template
	 *
	 * @return void
	 */
	public function include_appointment_modal_template(): void {
		// Include the modal template on all admin pages for users who can edit appointments.
		if ( ! current_user_can( 'edit_appointments' ) ) {
			return;
		}
		include_once __DIR__ . '/views/html-appointment-modal.php';
	}

	/**
	 * Looks at the current screen and loads the correct list table handler.
	 *
	 * @since 3.3.0
	 */
	public function setup_screen(): void {
		global $wc_list_table;

		$screen_id = false;

		if ( function_exists( 'get_current_screen' ) ) {
			$screen    = get_current_screen();
			$screen_id = $screen->id ?? '';
		}

		if ( ! empty( $_REQUEST['screen'] ) ) { // WPCS: input var ok.
			$screen_id = wc_clean( wp_unslash( $_REQUEST['screen'] ) ); // WPCS: input var ok, sanitization ok.
		}

		if ('edit-wc_appointment' === $screen_id) {
            include_once __DIR__ . '/class-wc-appointments-admin-list-table.php';
            $wc_list_table = new WC_Appointments_Admin_List_Table();
        }

		// Ensure the table handler is only loaded once. Prevents multiple loads if a plugin calls check_ajax_referer many times.
		remove_action( 'current_screen', [ $this, 'setup_screen' ] );
		remove_action( 'check_ajax_referer', [ $this, 'setup_screen' ] );
	}

	/**
	 * Add a Help tab linking to the Appointments "What's New" page on all Appointments admin screens.
	 *
	 * @return void
	 */
	public function add_whats_new_help_tab(): void {
		if ( ! function_exists( 'get_current_screen' ) ) {
			return;
		}

		$screen = get_current_screen();

		if ( ! $screen || empty( $screen->id ) ) {
			return;
		}

		// Only add the tab on Appointments-related admin screens.
		if ( false === strpos( $screen->id, 'wc_appointment' ) ) {
			return;
		}

		// Respect capabilities required to view the landing page.
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			return;
		}

		// Avoid adding the tab more than once.
		foreach ( (array) $screen->get_help_tabs() as $tab ) {
			if ( isset( $tab['id'] ) && 'wc_appointments_whats_new' === $tab['id'] ) {
				return;
			}
		}

		$whats_new_url = admin_url( 'edit.php?post_type=wc_appointment&page=' . WC_Appointments_Admin_Whats_New::PAGE_SLUG );

		$screen->add_help_tab(
		    [
				'id'      => 'wc_appointments_whats_new',
				'title'   => __( "What's New", 'woocommerce-appointments' ),
				'content' => wp_kses_post(
				    sprintf(
				        '<p>%1$s</p><p><a href="%2$s">%3$s</a></p>',
				        __( 'Read about the latest updates in WooCommerce Appointments.', 'woocommerce-appointments' ),
				        esc_url( $whats_new_url ),
				        __( 'Open the What\'s New page', 'woocommerce-appointments' ),
				    ),
				),
			],
		);
	}

	/**
	 * Save Appointment data for the product in 2.6.x.
	 *
	 * @param int $post_id
	 * @return void
	 */
	public function save_product_data( $post_id ): void {
		if ( version_compare( WC_VERSION, '3.0', '>=' ) || 'appointment' !== sanitize_title( stripslashes( $_POST['product-type'] ) ) ) {
			return;
		}
		$product = get_wc_product_appointment( $post_id );
		$this->set_props( $product );
		$product->save();
	}

	/**
	 * Get posted pricing fields and format.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	private function get_posted_pricing(): array {
		$pricing  = [];
		$row_size = count( $_POST['wc_appointment_pricing_type'] ?? [] );
		for ( $i = 0; $i < $row_size; $i ++ ) {
			$pricing[ $i ]['type']          = wc_clean( $_POST['wc_appointment_pricing_type'][ $i ] );
			$pricing[ $i ]['cost']          = wc_clean( $_POST['wc_appointment_pricing_cost'][ $i ] );
			$pricing[ $i ]['modifier']      = wc_clean( $_POST['wc_appointment_pricing_cost_modifier'][ $i ] );
			$pricing[ $i ]['base_cost']     = wc_clean( $_POST['wc_appointment_pricing_base_cost'][ $i ] );
			$pricing[ $i ]['base_modifier'] = wc_clean( $_POST['wc_appointment_pricing_base_cost_modifier'][ $i ] );

			switch ( $pricing[ $i ]['type'] ) {
				case 'custom':
					$pricing[ $i ]['from'] = wc_clean( $_POST['wc_appointment_pricing_from_date'][ $i ] );
					$pricing[ $i ]['to']   = wc_clean( $_POST['wc_appointment_pricing_to_date'][ $i ] );
					break;
				case 'months':
					$pricing[ $i ]['from'] = wc_clean( $_POST['wc_appointment_pricing_from_month'][ $i ] );
					$pricing[ $i ]['to']   = wc_clean( $_POST['wc_appointment_pricing_to_month'][ $i ] );
					break;
				case 'weeks':
					$pricing[ $i ]['from'] = wc_clean( $_POST['wc_appointment_pricing_from_week'][ $i ] );
					$pricing[ $i ]['to']   = wc_clean( $_POST['wc_appointment_pricing_to_week'][ $i ] );
					break;
				case 'days':
					$pricing[ $i ]['from'] = wc_clean( $_POST['wc_appointment_pricing_from_day_of_week'][ $i ] );
					$pricing[ $i ]['to']   = wc_clean( $_POST['wc_appointment_pricing_to_day_of_week'][ $i ] );
					break;
				case 'time':
				case 'time:1':
				case 'time:2':
				case 'time:3':
				case 'time:4':
				case 'time:5':
				case 'time:6':
				case 'time:7':
					$pricing[ $i ]['from'] = wc_appointment_sanitize_time( $_POST['wc_appointment_pricing_from_time'][ $i ] );
					$pricing[ $i ]['to']   = wc_appointment_sanitize_time( $_POST['wc_appointment_pricing_to_time'][ $i ] );
					break;
				case 'time:range':
					$pricing[ $i ]['from'] = wc_appointment_sanitize_time( $_POST['wc_appointment_pricing_from_time'][ $i ] );
					$pricing[ $i ]['to']   = wc_appointment_sanitize_time( $_POST['wc_appointment_pricing_to_time'][ $i ] );

					$pricing[ $i ]['from_date'] = wc_clean( $_POST['wc_appointment_pricing_from_date'][ $i ] );
					$pricing[ $i ]['to_date']   = wc_clean( $_POST['wc_appointment_pricing_to_date'][ $i ] );
					break;
				default:
					$pricing[ $i ]['from'] = wc_clean( $_POST['wc_appointment_pricing_from'][ $i ] );
					$pricing[ $i ]['to']   = wc_clean( $_POST['wc_appointment_pricing_to'][ $i ] );
					break;
			}
		}
		return $pricing;
	}

	/**
	 * Get posted staff.
	 *
	 * Staffs are global, but appointment products store information about the relationship.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	private function get_posted_staff(): array {
        $staff = [];
        if ( isset( $_POST['staff_id'] ) ) {
			$staff_ids        = $_POST['staff_id'];
			$staff_menu_order = $_POST['staff_menu_order'];
			$staff_base_cost  = $_POST['staff_cost'];
			$staff_qty        = $_POST['staff_qty'];
			$max_loop         = max( array_keys( $_POST['staff_id'] ) );
			$staff_base_costs = [];
			$staff_qtys       = [];

			foreach ( $staff_menu_order as $key => $value ) {
				$staff[ absint( $staff_ids[ $key ] ) ] = [
					'base_cost' => wc_clean( $staff_base_cost[ $key ] ),
					'qty'       => wc_clean( $staff_qty[ $key ] ),
				];
			}
		}
        return $staff;
    }

	/**
	 * Get posted appointment product properties from POST data.
	 *
	 * Sanitizes and extracts all appointment product properties from $_POST.
	 * Returns an array ready to be passed to set_props().
	 *
	 * @since 5.0.2
	 * @return array Array of sanitized appointment product properties.
	 */
	private function get_posted_appointment_props(): array {
		$prefix = '_wc_appointment_';

		// Helper to get POST value with optional default.
		$get_posted = function( string $key, $default = '' ) use ( $prefix ) {
			$full_key = $prefix . $key;
			return wc_clean( $_POST[ $full_key ] ?? '' ) ?: $default;
		};

		// Helper to check if POST key exists (for boolean fields).
		$has_posted = fn( string $key ): bool => isset( $_POST[ $prefix . $key ] );

		// Get qty_basis with validation.
		$qty_basis = $get_posted( 'qty_basis', WC_Product_Appointment::QTY_BASIS_SLOT );
		$qty_basis = in_array( $qty_basis, [ WC_Product_Appointment::QTY_BASIS_DAY, WC_Product_Appointment::QTY_BASIS_SLOT ], true )
			? $qty_basis
			: WC_Product_Appointment::QTY_BASIS_SLOT;

		$staff = $this->get_posted_staff();

		return [
			'has_price_label'         => $has_posted( 'has_price_label' ),
			'price_label'             => $get_posted( 'price_label' ),
			'has_pricing'             => $has_posted( 'has_pricing' ),
			'pricing'                 => $this->get_posted_pricing(),
			'qty'                     => $get_posted( 'qty' ),
			'qty_basis'               => $qty_basis,
			'qty_min'                 => $get_posted( 'qty_min' ),
			'qty_max'                 => $get_posted( 'qty_max' ),
			'duration_unit'           => $get_posted( 'duration_unit' ),
			'duration'                => $get_posted( 'duration' ),
			'interval_unit'           => $get_posted( 'interval_unit' ),
			'interval'                => $get_posted( 'interval' ),
			'padding_duration_unit'   => $get_posted( 'padding_duration_unit' ),
			'padding_duration'        => $get_posted( 'padding_duration' ),
			'min_date_unit'           => $get_posted( 'min_date_unit' ),
			'min_date'                => $get_posted( 'min_date' ),
			'max_date_unit'           => $get_posted( 'max_date_unit' ),
			'max_date'                => $get_posted( 'max_date' ),
			'user_can_cancel'         => $has_posted( 'user_can_cancel' ),
			'cancel_limit_unit'       => $get_posted( 'cancel_limit_unit' ),
			'cancel_limit'            => $get_posted( 'cancel_limit' ),
			'user_can_reschedule'     => $has_posted( 'user_can_reschedule' ),
			'reschedule_limit_unit'   => $get_posted( 'reschedule_limit_unit' ),
			'reschedule_limit'        => $get_posted( 'reschedule_limit' ),
			'requires_confirmation'   => $has_posted( 'requires_confirmation' ),
			'customer_timezones'      => $has_posted( 'customer_timezones' ),
			'cal_color'               => $get_posted( 'cal_color' ),
			'availability_span'       => $get_posted( 'availability_span' ),
			'availability_autoselect' => $has_posted( 'availability_autoselect' ),
			'has_restricted_days'     => $has_posted( 'has_restricted_days' ),
			'restricted_days'         => wc_clean( $_POST[ $prefix . 'restricted_days' ] ?? '' ),
			'staff_label'             => $get_posted( 'staff_label' ),
			'staff_ids'               => array_keys( $staff ),
			'staff_base_costs'        => wp_list_pluck( $staff, 'base_cost' ),
			'staff_qtys'              => wp_list_pluck( $staff, 'qty' ),
			'staff_assignment'        => $get_posted( 'staff_assignment' ),
			'staff_nopref'            => $has_posted( 'staff_nopref' ),
			'appointments_version'    => WC_APPOINTMENTS_VERSION,
			'appointments_db_version' => WC_APPOINTMENTS_DB_VERSION,
		];
	}

	/**
	 * Get posted availability fields and format.
	 *
	 * @param WC_Product $product
	 * @return void
	 */
	private function save_product_availability( $product ): void {
		// Delete.
		if ( ! empty( $_POST['wc_appointment_availability_deleted'] ) ) {
			$deleted_ids = array_filter( explode( ',', wc_clean( wp_unslash( $_POST['wc_appointment_availability_deleted'] ) ) ) );

			foreach ( $deleted_ids as $delete_id ) {
				$availability_object = get_wc_appointments_availability( $delete_id );
				if ( $availability_object ) {
					$availability_object->delete();
				}
			}
		}
        // Decode rules from request (JSON preferred, arrays fallback) and save.
		$data_store = WC_Data_Store::load( 'appointments-availability' );
		$rules      = $data_store->decode_rules_from_post();
		$rules      = array_values( array_filter( $rules, [ $data_store, 'is_rule_valid' ] ) );
		foreach ( $rules as $i => $rule ) {
				$current_id = empty( $rule['id'] ) ? 0 : intval( $rule['id'] );
				$range_type = empty( $rule['type'] ) ? '' : wc_clean( $rule['type'] );

            $availability = get_wc_appointments_availability( $current_id );
            $availability->set_ordering( intval( $i ) );
            $availability->set_range_type( $range_type );
            $availability->set_kind( WC_Appointments_Availability::KIND_PRODUCT );
            $availability->set_kind_id( $product->get_id() );

            // Apply normalized rule properties.
            $data_store->apply_json_rule_to_availability( $availability, $rule );

            $availability->save();
        }
	}

	/**
	 * Set data in 3.0.x
	 *
	 * @version  3.3.0
	 * @param    WC_Product $product
	 * @return   void
	 */
	public function set_props( $product ): void {
		// Only set props if the product is a appointable product.
		if ( ! is_wc_appointment_product( $product ) ) {
			return;
		}

		$this->save_product_availability( $product );
		$props = $this->get_posted_appointment_props();
		$product->set_props( $props );
	}

	/**
	 * Init product edit tabs.
	 *
	 * @return void
	 */
	public function init_tabs(): void {
		add_filter( 'woocommerce_product_data_tabs', [ $this, 'register_tab' ], 5 );
		add_action( 'woocommerce_product_data_panels', [ $this, 'appointment_panels' ], 10, 0 );
	}

	/**
	 * Add tabs to WC 2.6+
	 *
	 * @param array $tabs
	 * @return array
	 */
	public function register_tab( array $tabs ): array {
		$tabs['appointments_staff']        = [
			'label'    => __( 'Staff', 'woocommerce-appointments' ),
			'target'   => 'appointments_staff',
			'class'    => [
				'show_if_appointment',
			],
			'priority' => 25,
		];
		$tabs['appointments_availability'] = [
			'label'    => __( 'Availability', 'woocommerce-appointments' ),
			'target'   => 'appointments_availability',
			'class'    => [
				'show_if_appointment',
			],
			'priority' => 25,
		];

		// Inventory
		$tabs['inventory']['class'][] = 'show_if_appointment';

		return $tabs;
	}

	/**
	 * Public access to instance object
	 *
	 * @return object
	 */
	public static function get_instance(): \WC_Appointments_Admin {
		return self::$_this;
	}

	/**
	 * Duplicate a post.
	 *
	 * @param  WC_Product $new_product Duplicated product.
	 * @param  WC_Product $product     Original product.
	 * @return void
	 */
	public function woocommerce_duplicate_product( $new_product, $product ): void {
		if ( is_wc_appointment_product( $product ) ) {
			// Get all product availability rules.
			$product_rules = WC_Data_Store::load( 'appointments-availability' )->get_all(
			    [
					[
						'key'     => 'kind',
						'compare' => '=',
						'value'   => 'availability#product',
					],
					[
						'key'     => 'kind_id',
						'compare' => '=',
						'value'   => $product->get_id(),
					],
				],
			);

			// Stop here when no rules.
			if ( ! $product_rules ) {
				return;
			}

			// Clone and re-save availabilities.
			foreach ( $product_rules as $availability ) {
				$dupe_availability = clone $availability;
				$dupe_availability->set_id( 0 );
				$dupe_availability->set_kind_id( $new_product->get_id() );
				$dupe_availability->save();
			}
		}
	}

	/**
	 * Change messages when a post type is updated.
	 *
	 * @param array $messages
	 * @return array
	 */
	public function post_updated_messages( array $messages ): array {
		$messages['wc_appointment'] = [
			0  => '', // Unused. Messages start at index 1.
			1  => __( 'Appointment updated.', 'woocommerce-appointments' ),
			2  => __( 'Custom field updated.', 'woocommerce-appointments' ),
			3  => __( 'Custom field deleted.', 'woocommerce-appointments' ),
			4  => __( 'Appointment updated.', 'woocommerce-appointments' ),
			5  => '',
			6  => __( 'Appointment updated.', 'woocommerce-appointments' ),
			7  => __( 'Appointment saved.', 'woocommerce-appointments' ),
			8  => __( 'Appointment submitted.', 'woocommerce-appointments' ),
			9  => '',
			10 => '',
		];

		return $messages;
	}

	/**
	 * Show appointment data if a line item is linked to an appointment ID.
	 */
	public function appointment_display( $item_id, $item, $product ): void {
		if ( ! is_wc_appointment_product( $product ) ) {
			return;
		}

		$appointment_ids = WC_Appointment_Data_Store::get_appointment_ids_from_order_and_item_id( $item->get_order_id(), $item_id );

		wc_get_template(
		    'order/admin/appointment-display.php',
		    [
				'appointment_ids' => $appointment_ids,
				'is_rtl'          => is_rtl() ? 'right' : 'left',
			],
		    '',
		    WC_APPOINTMENTS_TEMPLATE_PATH,
		);
	}

	/**
	 * Include meta box handlers
	 *
	 * @return void
	 */
	public function include_meta_box_handlers(): void {
		include __DIR__ . '/class-wc-appointments-admin-meta-boxes.php';
	}

	/**
	 * Redirect the default add appointment url to the custom one.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function redirect_new_add_appointment_url() {
	}

	/**
	 * Get appointment products.
	 *
	 * @since 1.0.0
	 *
	 * @param bool $show_all Whether to show all products or just publish/private.
	 *
	 * @return array
	 */
	public static function get_appointment_products( $show_all = false ): array {
		$ids                  = WC_Data_Store::load( 'product-appointment' )->get_appointable_product_ids( $show_all );
		$appointable_products = [];

		if ( ! $ids ) {
			return $appointable_products;
		}

		foreach ( $ids as $id ) {
			$appointable_products[] = get_wc_product_appointment( $id );
		}

		return $appointable_products;
	}

	/**
	 * Get appointment staff.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public static function get_appointment_staff(): array {
		// Cache query.
		$cache_group = 'wc-appointments-admin-staff';
		$cache_key   = WC_Cache_Helper::get_cache_prefix( $cache_group ) . 'get_appointment_staff';

		$data = wp_cache_get( $cache_key, $cache_group );

		if ( false === $data ) {
			$data = get_users(
			    apply_filters(
			        'get_appointment_staff_args',
			        [
						'role'    => 'shop_staff',
						'orderby' => 'nicename',
						'order'   => 'asc',
					],
			    ),
			);

			wp_cache_set( $cache_key, $data, $cache_group );
		}

		return $data;
	}

	/**
	 * On before delete product hook remove the product from all appointments
	 *
	 * @param int $product_id The id of the product that we are deleting
	 * @return void
	 */
	public function before_delete_product( $product_id ): void {
		if ( ! current_user_can( 'delete_posts' ) ) {
			return;
		}

		if ( 0 < $product_id && 'product' === get_post_type( $product_id ) ) {
			$product_appointments = WC_Appointment_Data_Store::get_appointments_for_product( $product_id, get_wc_appointment_statuses( 'validate' ) );
			// Loop appointable products is added to remove the product from it
			foreach ( $product_appointments as $product_appointment ) {
				delete_post_meta( $product_appointment->get_id(), '_appointment_product_id' );
			}
		}
	}

	/**
	 * Show the appointment inventory view
	 *
	 * @return void
	 */
	public function appointment_inventory(): void {
		global $appointable_product;

		if ( empty( $appointable_product ) || $appointable_product->get_id() !== get_the_ID() ) {
			$appointable_product = get_wc_product_appointment( get_the_ID() );
		}

		include __DIR__ . '/views/html-appointment-inventory.php';
	}

	/**
	 * Tweak product type options
	 *
	 * @param array $options
	 * @return array
	 */
	public function product_type_options( array $options ): array {
		$options['virtual']['wrapper_class']      .= ' show_if_appointment';
		$options['downloadable']['wrapper_class'] .= ' show_if_appointment';

		return $options;
	}

	/**
	 * Add the appointment product type
	 *
	 * @param array $types
	 * @return array
	 */
	public function product_type_selector( array $types ): array {
		$types['appointment'] = __( 'Appointable product', 'woocommerce-appointments' );

		return $types;
	}

	/**
	 * Show the appointment general view
	 *
	 * @return void
	 */
	public function appointment_general(): void {
		global $appointable_product;

		if ( empty( $appointable_product ) || $appointable_product->get_id() !== get_the_ID() ) {
			$appointable_product = get_wc_product_appointment( get_the_ID() );
		}

		include __DIR__ . '/views/html-appointment-general.php';
	}

	/**
	 * Show the appointment panels views
	 *
	 * @return void
	 */
	public function appointment_panels(): void {
		global $appointable_product;

		if ( empty( $appointable_product ) || $appointable_product->get_id() !== get_the_ID() ) {
			$appointable_product = get_wc_product_appointment( get_the_ID() );
		}

		$restricted_meta = $appointable_product->get_restricted_days();

		for ( $i = 0; 7 > $i; $i++ ) {

			$restricted_days[ $i ] = $restricted_meta && in_array( $i, $restricted_meta ) ? $i : false;
		}

		wp_enqueue_script( 'wc_appointments_writepanel_js' );

		include __DIR__ . '/views/html-appointment-staff.php';
		include __DIR__ . '/views/html-appointment-availability.php';
	}

	/**
	 * Add admin styles
	 *
	 * @return void
	 */
	public function styles_and_scripts(): void {
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';

		wp_enqueue_style( 'wc_appointments_admin_styles', WC_APPOINTMENTS_PLUGIN_URL . '/assets/css/admin.css', true, WC_APPOINTMENTS_VERSION );
		wp_register_style( 'wc_appointments_admin_modal_css', WC_APPOINTMENTS_PLUGIN_URL . '/assets/css/appointment-admin-modal.css', [ 'woocommerce_admin_styles', 'wc_appointments_admin_styles' ], WC_APPOINTMENTS_VERSION );
		wp_enqueue_style( 'wc_appointments_admin_modal_css' );
		if ( ! wp_style_is( 'select2', 'enqueued' ) ) {
			wp_enqueue_style( 'select2' );
		}
		wp_enqueue_script( 'wp-color-picker' );
		// Force unminified writepanel.js to ensure our customizations are loaded.
		wp_register_script( 'wc_appointments_writepanel_js', WC_APPOINTMENTS_PLUGIN_URL . '/assets/js/writepanel.js', [ 'jquery', 'jquery-ui-datepicker', 'jquery-ui-sortable' ], WC_APPOINTMENTS_VERSION, true );
		wp_register_script( 'wc_appointments_exporter_js', WC_APPOINTMENTS_PLUGIN_URL . '/assets/js/appointment-export' . $suffix . '.js', [ 'jquery', 'jquery-ui-datepicker', 'wc-enhanced-select' ], WC_APPOINTMENTS_VERSION, true );

        wp_register_script( 'wc_appointments_admin_modal_js', WC_APPOINTMENTS_PLUGIN_URL . '/assets/js/appointment-admin-modal.js', [ 'jquery', 'wc_appointments_writepanel_js', 'wc-enhanced-select', 'wp-util' ], WC_APPOINTMENTS_VERSION, true );
        wp_enqueue_script( 'wc_appointments_admin_modal_js' );

        // Remove ACF plugin's timepicker scripts.
        if ( 'product' === get_post_type() ) {
            wp_deregister_script( 'acf-timepicker' );
            wp_dequeue_style( 'acf-timepicker' );
        }

        // Get default location.
        $default_location = wc_get_customer_default_location();

        // Determine site timezone string for consistent admin time display.
        $site_tz = function_exists( 'wp_timezone_string' ) ? wp_timezone_string() : get_option( 'timezone_string' );
        if ( empty( $site_tz ) ) {
            $site_tz = 'UTC';
        }

        $params = [
            'nonce_delete_staff'        => wp_create_nonce( 'delete-appointable-staff' ),
            'nonce_add_staff'           => wp_create_nonce( 'add-appointable-staff' ),
            'nonce_add_product'         => wp_create_nonce( 'add-staff-product' ),
            'nonce_staff_html'          => wp_create_nonce( 'appointable-staff-html' ),
            'nonce_manual_sync'         => wp_create_nonce( 'add-manual-sync' ),
            'nonce_oauth_redirect'      => wp_create_nonce( 'add-oauth-redirect' ),
            'nonce_export_appointemnts' => wp_create_nonce( 'wc-appointment-export' ),
            'nonce_appointment_order'   => wp_create_nonce( 'search-appointment-order' ),
            'nonce_indexing'            => wp_create_nonce( 'wc_appointments_admin_nonce' ),

            'nonce_get_products'        => wp_create_nonce( 'get-appointable-products' ),
            'nonce_search_products'     => wp_create_nonce( 'search-products' ),
            'nonce_get_product_details' => wp_create_nonce( 'get-product-details' ),
            'nonce_get_times'           => wp_create_nonce( 'get-available-times' ),
            'nonce_create_appointment'  => wp_create_nonce( 'create-appointment' ),
            'nonce_search_customers'    => wp_create_nonce( 'search-customers' ),
            'nonce_get_customer_details'=> wp_create_nonce( 'get-customer-details' ),
            'nonce_search_orders'       => wp_create_nonce( 'search-orders' ),
            // New availability check nonce
            'nonce_check_availability'  => wp_create_nonce( 'wc_appointments_check_availability' ),
            // New pretty duration nonce
            'nonce_pretty_duration'     => wp_create_nonce( 'wc_appointments_pretty_duration' ),

            'i18n_confirmation'         => esc_js( __( 'Are you sure?', 'woocommerce-appointments' ) ),
            'i18n_minutes'              => esc_js( __( 'minutes', 'woocommerce-appointments' ) ),
            'i18n_hours'                => esc_js( __( 'hours', 'woocommerce-appointments' ) ),
            'i18n_minutes_short'        => esc_js( _x( 'm', 'minutes short label', 'woocommerce-appointments' ) ),
            'i18n_hours_short'          => esc_js( _x( 'h', 'hours short label', 'woocommerce-appointments' ) ),
            'i18n_days'                 => esc_js( __( 'days', 'woocommerce-appointments' ) ),
            'i18n_addons_required'      => esc_js( __( 'Please select required add-ons.', 'woocommerce-appointments' ) ),
            // Availability i18n
            'i18n_checking_availability'=> esc_js( __( 'Checking availability…', 'woocommerce-appointments' ) ),
            'i18n_available'            => esc_js( __( 'Available', 'woocommerce-appointments' ) ),
            'i18n_unavailable'          => esc_js( __( 'Not available', 'woocommerce-appointments' ) ),
            'i18n_next_available'       => esc_js( __( 'Next available', 'woocommerce-appointments' ) ),
            'i18n_error_checking'       => esc_js( __( 'Error checking availability', 'woocommerce-appointments' ) ),

            'post'                      => get_the_ID(),
            'plugin_url'                => WC()->plugin_url(),
            'ajax_url'                  => admin_url( 'admin-ajax.php' ),
            'site_timezone'             => $site_tz,
            'firstday'                  => absint( get_option( 'start_of_week', 1 ) ),
            'calendar_image'            => WC()->plugin_url() . '/assets/images/calendar.png',
            'appointments_list_url'    => esc_url_raw( admin_url( 'edit.php?post_type=wc_appointment' ) ),

	        'exporter'                  => [
				'string'     => esc_js( __( 'Export', 'woocommerce-appointments' ) ),
				'url'        => esc_url_raw( admin_url( 'edit.php?post_type=wc_appointment&page=appointment_exporter' ) ),
				'permission' => (bool) current_user_can( 'export' ),
			],

			'billing'                   => [
				'countries'       => wp_json_encode( array_merge( WC()->countries->get_allowed_country_states(), WC()->countries->get_shipping_country_states() ) ),
				'default_country' => $default_location['country'] ?? '',
				'default_state'   => $default_location['state'] ?? '',
			],
            'i18n' => [
                    'select_product' => __( 'Select a product...', 'woocommerce-appointments' ),
                    'search_products' => __( 'Search for an appointable product...', 'woocommerce-appointments' ),
                    'select_time' => __( 'Select time...', 'woocommerce-appointments' ),
                    'select_duration' => __( 'Select duration...', 'woocommerce-appointments' ),
                    'any_staff' => __( 'Any staff member', 'woocommerce-appointments' ),
					'all_staff_assigned' => __( 'All staff assigned', 'woocommerce-appointments' ),
					'no_preference' => __( 'No preference', 'woocommerce-appointments' ),
                    'creating' => __( 'Creating...', 'woocommerce-appointments' ),
                    'create_appointment' => __( 'Create Appointment', 'woocommerce-appointments' ),
                    'error_loading_products' => __( 'Error loading products.', 'woocommerce-appointments' ),
                    'error_loading_product' => __( 'Error loading product details.', 'woocommerce-appointments' ),
                    'error_creating' => __( 'Error creating appointment.', 'woocommerce-appointments' ),
                    'appointment_created' => __( 'Appointment created successfully.', 'woocommerce-appointments' ),
					'edit_appointment' => __( 'Edit Appointment', 'woocommerce-appointments' ),
					'edit_order' => __( 'Edit Order', 'woocommerce-appointments' ),
                    'create_another' => __( 'Add New Appointment', 'woocommerce-appointments' ),
                    'close' => __( 'Close', 'woocommerce-appointments' ),
            ],
		];

		wp_localize_script( 'wc_appointments_writepanel_js', 'wc_appointments_writepanel_js_params', $params );
		wp_localize_script( 'wc_appointments_exporter_js', 'wc_appointments_exporter_js_params', $params );
        wp_localize_script( 'wc_appointments_admin_modal_js', 'wc_appointment_modal_js_params', $params );
	}

	/**
     * Reset the ics exporter timezone string cache.
     */
    public function reset_ics_exporter_timezone_cache(): void {
		if ( isset( $_GET['settings-updated'] ) && 'true' == $_GET['settings-updated'] ) {
			wp_cache_delete( 'wc_appointments_timezone_string' );
		}
	}

	/**
	 * Add memberships settings page
	 *
	 * @since 1.0
	 * @param array $settings
	 * @return array
	 */
	public function add_settings_page( $settings ): array {
		$settings[] = include __DIR__ . '/class-wc-appointments-admin-settings.php';

		return $settings;
	}

	/**
	 * Support scanning for template overrides in extension.
	 *
	 * @param array $paths
	 * @return array
	 */
	public function template_scan_path( array $paths ): array {
		$paths['WooCommerce Appointments'] = WC_APPOINTMENTS_TEMPLATE_PATH;

		return $paths;
	}

	/**
	 * Show a notice highlighting bad template files.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public static function template_file_check_notice(): void {
		$core_templates = WC_Admin_Status::scan_template_files( WC_APPOINTMENTS_TEMPLATE_PATH );
		$outdated       = false;

		foreach ( $core_templates as $file ) {

			$theme_file = false;
			if ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
				$theme_file = get_stylesheet_directory() . '/' . $file;
			} elseif ( file_exists( get_stylesheet_directory() . '/woocommerce/' . $file ) ) {
				$theme_file = get_stylesheet_directory() . '/woocommerce/' . $file;
			} elseif ( file_exists( get_template_directory() . '/' . $file ) ) {
				$theme_file = get_template_directory() . '/' . $file;
			} elseif ( file_exists( get_template_directory() . '/woocommerce/' . $file ) ) {
				$theme_file = get_template_directory() . '/woocommerce/' . $file;
			}

			if ( false !== $theme_file ) {
				$core_version  = WC_Admin_Status::get_file_version( WC_APPOINTMENTS_TEMPLATE_PATH . $file );
				$theme_version = WC_Admin_Status::get_file_version( $theme_file );

				if ( $core_version && $theme_version && version_compare( $theme_version, $core_version, '<' ) ) {
					$outdated = true;
					break;
				}
			}
		}

		if ( $outdated ) {
			$theme = wp_get_theme();

			WC_Admin_Notices::add_custom_notice(
			    'wc_appointments_template_files',
			    sprintf(
					/* translators: %1$s: Theme name, %2$d: Admin URL, %3$s: Link to template strucure docs */
					__( '<p><strong>Your theme (%1$s) contains outdated copies of some WooCommerce Appointments template files.</strong> These files may need updating to ensure they are compatible with the current version of WooCommerce Appointments. You can see which files are affected from the <a href="%2$s">system status page</a>. If in doubt, check with the author of the theme.<p><p class="submit"><a class="button-primary" href="%3$s" target="_blank">Learn More About Templates</a></p>', 'woocommerce-appointments' ),
			        esc_html( $theme['Name'] ),
			        esc_url( admin_url( 'admin.php?page=wc-status' ) ),
			        esc_url( 'https://docs.woocommerce.com/document/template-structure/' ),
			    ),
			);
		} else {
			WC_Admin_Notices::remove_notice( 'wc_appointments_template_files' );
		}
	}

	/**
	 * Override product type for New Product screen, if a request parameter is set.
	 *
	 * @since 1.0.0
	 *
	 * @param string $override Product Type
	 * @param int    $product_id
	 *
	 * @return string
	 */
	public function maybe_override_product_type( $override, $product_id ): string {
		if ( ! empty( $_REQUEST['appointable_product'] ) ) {
			return 'appointment';
		}

		return $override;
	}
}

$GLOBALS['wc_appointments_admin'] = new WC_Appointments_Admin();
