<?php
/**
 * RadiusBooking API Route Registrar
 *
 * This class is responsible for registering API routes dynamically with the WordPress REST API.
 * It supports CRUD operations and allows for middleware to be applied globally or per route.
 *
 * @package RadiusTheme\RadiusBooking\Core\Api\Routes
 */

namespace RadiusTheme\RadiusBooking\Assets;

use RadiusTheme\RadiusBooking\Common\Keys;
use RadiusTheme\RadiusBooking\Controllers\ServiceController;
use RadiusTheme\RadiusBooking\Controllers\UserController;
use RadiusTheme\RadiusBooking\Helpers\SettingsHelper;
use RadiusTheme\RadiusBooking\Helpers\WPHelpers;
use RadiusTheme\RadiusBooking\Models\EmployeeService;
use RadiusTheme\RadiusBooking\Models\Service;
use RadiusTheme\RadiusBooking\Models\UserProfile;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/**
 * Load assets class
 *
 * Responsible for managing all of the assets (CSS, JS, Images, Locales).
 */
class LoadAssets {

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
		add_action( 'wp_print_scripts', array( $this, 'localize_printed_scripts' ), 5 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_site_assets' ), 999 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_service_list_assets' ), 999 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_employee_list_assets' ), 999 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_my_booking_assets' ), 999 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_chatbot_assets' ), 999 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_meeting_booking_assets' ), 999 );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_event_booking_assets' ), 999 );

		// Load assets inside Elementor editor preview iframe.
		add_action( 'elementor/preview/enqueue_scripts', array( $this, 'enqueue_service_list_assets_elementor' ) );
		add_action( 'elementor/preview/enqueue_scripts', array( $this, 'enqueue_employee_list_assets_elementor' ) );
		add_action( 'elementor/preview/enqueue_scripts', array( $this, 'enqueue_site_assets_elementor' ) );

		// Load booking form assets in the Gutenberg block editor.
		add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_site_assets_elementor' ) );

		// Resolve script translations by merging every
		// `radius-booking-{locale}-*.json` in the languages folder, so the
		// per-script src-hash in the filename stops mattering. Lets Loco /
		// `wp i18n make-json` produce files with any hash convention and they
		// all still load.
		add_filter( 'pre_load_script_translations', array( $this, 'load_merged_script_translations' ), 10, 4 );
	}

	/**
	 * Merge all JSON translation files for the current locale into one
	 * locale_data block and return it as the script translations payload.
	 *
	 * Without this, WordPress only loads the single file whose name hashes to
	 * the enqueued bundle URL — which Loco / make-json can't reliably produce
	 * for Vite-built bundles because they shard JSON by source file.
	 *
	 * @param string|null $translations Translations JSON (null until provided).
	 * @param string|false $file        The path WP was going to load.
	 * @param string      $handle       Script handle.
	 * @param string      $domain       Text domain.
	 *
	 * @return string|null JSON string to use as locale data, or null to defer.
	 */
	public function load_merged_script_translations( $translations, $file, $handle, $domain ) {
		if ( 'radius-booking' !== $domain ) {
			return $translations;
		}

		static $cache = array();
		// determine_locale() returns the admin user's profile language on
		// admin requests. If the user hasn't picked a language in their
		// profile, it falls through to the site locale. We still also try
		// get_locale() as a fallback so a site-language change works without
		// needing to also change the user profile.
		$locales = array_unique( array_filter( array( determine_locale(), get_locale() ) ) );
		$cache_key = implode( '|', $locales );
		if ( isset( $cache[ $cache_key ] ) ) {
			return $cache[ $cache_key ];
		}

		$files = $this->find_translation_json_files( $locales );
		if ( empty( $files ) ) {
			$cache[ $cache_key ] = $translations;
			return $translations;
		}

		$messages = array(
			'' => array(
				'domain' => 'messages',
				'lang'   => reset( $locales ),
			),
		);

		foreach ( $files as $f ) {
			$raw = @file_get_contents( $f ); // phpcs:ignore WordPress.PHP.NoSilencedErrors,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
			if ( false === $raw ) {
				continue;
			}
			$data = json_decode( $raw, true );
			if ( empty( $data['locale_data'] ) || ! is_array( $data['locale_data'] ) ) {
				continue;
			}
			// Loco keys locale_data by 'messages'; some tools use the domain
			// name. Take whichever inner block exists, ignoring the empty
			// header entry.
			foreach ( $data['locale_data'] as $inner ) {
				if ( ! is_array( $inner ) ) {
					continue;
				}
				if ( isset( $inner[''] ) && isset( $inner['']['plural-forms'] ) ) {
					$messages['']['plural-forms'] = $inner['']['plural-forms'];
				}
				foreach ( $inner as $msgid => $msgstr ) {
					if ( '' === $msgid ) {
						continue;
					}
					$messages[ $msgid ] = $msgstr;
				}
				break;
			}
		}

		$merged = array(
			'domain'      => 'messages',
			'locale_data' => array(
				'messages' => $messages,
			),
		);

		$cache[ $cache_key ] = wp_json_encode( $merged );
		return $cache[ $cache_key ];
	}

	/**
	 * Scan all known WordPress translation directories for radius-booking JSON
	 * files matching any of the given locales.
	 *
	 * Loco Translate stores its output in one of three places depending on the
	 * "File system access" setting: the plugin's own languages folder (Author),
	 * wp-content/languages/loco/plugins (Custom — Loco default), or
	 * wp-content/languages/plugins (System). We check all three so editors are
	 * free to pick whichever is appropriate for their server.
	 *
	 * @param string[] $locales Locale codes to try, in priority order.
	 *
	 * @return string[] List of absolute file paths.
	 */
	private function find_translation_json_files( array $locales ) {
		$dirs = array(
			RADIUS_BOOKING_DIR . '/languages',
			WP_LANG_DIR . '/loco/plugins',
			WP_LANG_DIR . '/plugins',
		);

		$found = array();
		foreach ( $dirs as $dir ) {
			foreach ( $locales as $locale ) {
				// Match Loco's hashed pattern and WordPress.org's bare pattern
				// so community translations from translate.wordpress.org are
				// picked up too.
				$patterns = array(
					$dir . '/radius-booking-' . $locale . '-*.json',
					$dir . '/radius-booking-' . $locale . '.json',
				);
				foreach ( $patterns as $pattern ) {
					$matches = glob( $pattern );
					if ( ! empty( $matches ) ) {
						$found = array_merge( $found, $matches );
					}
				}
			}
		}

		// Deduplicate in case the same path is reached more than once.
		$found = array_values( array_unique( $found ) );

		// Sort by mtime ascending so the most recently saved file is read
		// LAST in the merge loop — its messages overwrite anything older.
		// Without this, an older Loco JSON could win over a newer one
		// purely because glob() returned it later. mtime ordering matches
		// what a human would expect: latest save wins.
		usort( $found, static function ( $a, $b ) {
			return filemtime( $a ) <=> filemtime( $b );
		} );

		return $found;
	}

	/**
	 * Enqueue admin styles and scripts.
	 *
	 * @return void
	 * @since 0.3.0 Loads the JS and CSS only on the Dynamic Discount admin page.
	 *
	 * @since 1.0.0
	 */
	public function enqueue_admin_assets() {
		if ( ! is_admin() || ! isset( $_GET['page'] ) || sanitize_text_field( wp_unslash( $_GET['page'] ) ) !== 'radius-booking' ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return;
		}

		wp_enqueue_media();
		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;
		$assets     = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$entry = 'src/main.jsx';

		$assets->enqueue(
			$entry,
			array(
				'handle'              => 'radius-booking-app',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array( 'wp-components' ),
			)
		);

		$this->register_script_translations( 'radius-booking-app' );
	}

	/**
	 * Register JS translations for an enqueued script handle.
	 *
	 * Centralised so every Vite entry uses the same domain + languages path
	 * and we never accidentally enqueue a script without translations wired
	 * up. WordPress will look for
	 * `languages/radius-booking-{locale}-{md5(script-src)}.json` and inline
	 * the translations via `wp.i18n.setLocaleData` before the bundle runs.
	 *
	 * @param string $handle Registered script handle.
	 *
	 * @return void
	 */
	private function register_script_translations( $handle ) {
		wp_set_script_translations(
			$handle,
			'radius-booking',
			RADIUS_BOOKING_DIR . '/languages'
		);
	}

	/**
	 * Localize scripts for the Radius Booking app.
	 *
	 * This function localizes the script with necessary parameters such as AJAX URL, nonce, and plugin URL.
	 *
	 * @since 1.0.0
	 */
	public function localize_printed_scripts() {
		if ( ! is_admin() || ! isset( $_GET['page'] ) || sanitize_text_field( wp_unslash( $_GET['page'] ) ) !== 'radius-booking' ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return;
		}
		wp_localize_script(
			'radius-booking-app',
			'radius_booking_param',
			array(
				'ajax_url'          => admin_url( 'admin-ajax.php' ),
				'nonce'             => wp_create_nonce( 'wp_rest' ),
				'rest_root'         => esc_url_raw( rest_url() ),
				'plugin_url'        => untrailingslashit( content_url( 'plugins/radius-booking' ) ),
				'admin_email'       => get_option( 'admin_email' ),
				'current_user_info' => WPHelpers::get_current_user_data(),
				'currency_symbol'   => array_map( 'html_entity_decode', get_rtrb_currency_symbols() ),
				'currency'          => get_rtrb_currency(),
				'current_location'  => get_rtrb_current_location(),
				'country'           => WPHelpers::get_formated_country_name(),
				'is_pro'            => rtrb_pro_active(),
				'active_modules'    => array(
					'service' => true,
					'meeting' => rtrb_is_module_active( 'meeting' ),
					'event'   => rtrb_is_module_active( 'event' ),
				),
				'root'              => site_url(),
				'timeZone'          => get_option( 'timezone_string' ),
				'phone_settings'    => SettingsHelper::get_setting( 'phone' ),
				'setup_completed'   => (bool) get_option( Keys::RADIUS_BOOKING_SETUP_COMPLETED, false ),
				'wp_pages'          => WPHelpers::get_wp_pages_for_select(),
				'tax_settings'      => SettingsHelper::get_setting( 'tax' ),
				'date_time_settings' => SettingsHelper::get_setting( 'dateTime' ),
				'custom_fields'     => apply_filters( 'rtrb_custom_fields_data', array() ),
				'upgrade_url'       => 'https://radiusbooking.co/pricing/',
				'is_cl_active'      => function_exists( 'rtcl' ),
				'payout_enabled'    => function_exists( 'rtcl' ) && ! empty( SettingsHelper::get_setting( 'payout' )['enabled'] ),
			)
		);
	}

	/**
	 * Enqueue site styles and scripts.
	 * NEW - Enqueue site-specific assets
	 *
	 * @since 1.0.0
	 */
	public function enqueue_site_assets() {
		// Only load on frontend
		if ( is_admin() ) {
			return;
		}

		global $post;
		$should_load = false;

		// Check if shortcode is present
		if ( $post && has_shortcode( $post->post_content, 'radius_booking_form' ) ) {
			$should_load = true;
		}

		// Check if Elementor renders the appointment form widget.
		if ( ! $should_load && $post && class_exists( '\Elementor\Plugin' ) ) {
			$elementor = \Elementor\Plugin::$instance;
			if ( $elementor->db->is_built_with_elementor( $post->ID ) ) {
				$data = $elementor->documents->get( $post->ID );
				if ( $data ) {
					$content = $data->get_elements_data();
					if ( $this->elementor_has_widget( $content, 'rtrb_appointment_form' ) ) {
						$should_load = true;
					}
				}
			}
		}

		// Check if Gutenberg block is present.
		if ( ! $should_load && $post && has_block( 'radius-booking/appointment-form', $post ) ) {
			$should_load = true;
		}

		// Check if this is a single Classified Listing with booking enabled.
		if ( ! $should_load && is_singular( 'rtcl_listing' ) ) {
			$listing_id = get_the_ID();
			if ( $listing_id && \RadiusTheme\RadiusBooking\Integrations\ClassifiedListing\ListingServiceSync::normalizeBookingEnabled( get_post_meta( $listing_id, '_rtrb_booking_enabled', true ) ) ) {
				$should_load = true;
			}
		}

		// Note: Classified Listing archive / custom-loop pages are intentionally
		// NOT force-loaded here. rtrb_cl_booking_button() lazily enqueues the
		// bundle itself (via enqueue_site_bundle()) only when a real bookable
		// button is actually rendered, so archives without a button stay clean.
		$should_load = apply_filters( 'rtrb_should_load_frontend_assets', $should_load );

		if ( ! $should_load ) {
			return;
		}

		$this->enqueue_site_bundle();
	}

	/**
	 * Enqueue the React booking bundle (and the Classified Listing modal
	 * controller). Idempotent: safe to call multiple times and from any point
	 * before wp_footer, so the rtrb_cl_booking_button() template function can
	 * lazily pull the assets in on custom pages/loops that the automatic
	 * detection in enqueue_site_assets() does not cover.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function enqueue_site_bundle() {
		static $done = false;

		if ( $done || is_admin() ) {
			return;
		}
		$done = true;

		// Path to site assets
		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		// Initialize Vite with the build manifest
		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$entry = 'src/site/main.jsx';
		// Use the manifest entry key for the site entry
		$vite->enqueue(
			$entry, // key from your Vite build (site entry)
			array(
				'handle'              => 'radius-booking-site',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(), // Add frontend CSS dependencies if needed
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-site' );

		// Localize site-specific data
		$this->localize_site_scripts();

		// Classified Listing: lightweight handler that opens the shared booking
		// modal and mounts the React form for the clicked listing. Works for the
		// single listing page and anywhere rtrb_cl_booking_button() is used.
		if ( function_exists( 'rtcl' ) ) {
			wp_enqueue_script(
				'rtrb-cl-booking',
				RADIUS_BOOKING_URL . '/assets/js/cl-booking.js',
				array(),
				RADIUS_BOOKING_VERSION,
				true
			);
		}
	}

	/**
	 * Enqueue meeting booking page assets.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function enqueue_meeting_booking_assets() {
		if ( is_admin() ) {
			return;
		}

		global $post;
		$should_load = false;

		if ( $post && has_shortcode( $post->post_content, 'rtrb_meeting_booking' ) ) {
			$should_load = true;
		}

		if ( ! $should_load ) {
			return;
		}

		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$vite->enqueue(
			'src/meetingBooking/main.jsx',
			array(
				'handle'              => 'radius-booking-meeting',
				'script-dependencies' => array( 'wp-element', 'wp-i18n', 'wp-api-fetch' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-meeting' );

		wp_localize_script(
			'radius-booking-meeting',
			'radius_booking_meeting_param',
			array(
				'rest_root'          => esc_url_raw( rest_url() ),
				'nonce'              => wp_create_nonce( 'wp_rest' ),
				'date_time_settings' => SettingsHelper::get_setting( 'dateTime' ),
				'language'           => get_locale(),
				'currency_symbol'    => function_exists( 'get_rtrb_currency_symbol' ) ? get_rtrb_currency_symbol() : '$',
				'currency'           => function_exists( 'get_rtrb_currency' ) ? get_rtrb_currency() : 'USD',
				// All active custom fields; the public meeting form filters
				// by `applies_to` and `meeting_types` client-side.
				'custom_fields'      => apply_filters( 'rtrb_custom_fields_data', array() ),
			)
		);
	}

	/**
	 * Enqueue event booking page assets.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function enqueue_event_booking_assets() {
		if ( is_admin() ) {
			return;
		}

		global $post;
		$should_load = false;

		if ( $post && ( has_shortcode( $post->post_content, 'rtrb_event_list' ) || has_shortcode( $post->post_content, 'rtrb_event_registration' ) ) ) {
			$should_load = true;
		}

		if ( ! $should_load ) {
			return;
		}

		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$vite->enqueue(
			'src/eventBooking/main.jsx',
			array(
				'handle'              => 'radius-booking-event',
				'script-dependencies' => array( 'wp-element', 'wp-i18n', 'wp-api-fetch' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-event' );

		$pages = \RadiusTheme\RadiusBooking\Helpers\SettingsHelper::get_setting( 'pages' );
		wp_localize_script(
			'radius-booking-event',
			'radius_booking_event_param',
			array(
				'rest_root'              => esc_url_raw( rest_url() ),
				'nonce'                  => wp_create_nonce( 'wp_rest' ),
				'event_registration_url' => ! empty( $pages['eventRegistrationPage'] ) ? get_permalink( $pages['eventRegistrationPage'] ) : '',
				'currency_symbol'        => get_rtrb_currency_symbol(),
				'currency'               => get_rtrb_currency(),
				'is_pro'                 => function_exists( 'rtrb_pro_active' ) && rtrb_pro_active(),
				// All active custom fields; the public event form filters
				// by `applies_to` and `events` client-side.
				'custom_fields'          => apply_filters( 'rtrb_custom_fields_data', array() ),
			)
		);
	}


	/**
	 * Localize scripts for the site/frontend booking interface.
	 *
	 * NEW - Site-specific localization
	 *
	 * @since 1.0.0
	 */
	public function localize_site_scripts() {
		$services      = ( new ServiceController() )->fetchAllServices();
		$currency_code = get_rtrb_currency();
		wp_localize_script(
			'radius-booking-site',
			'radius_booking_site_param',
			array(
				'ajax_url'                   => admin_url( 'admin-ajax.php' ),
				'rest_url'                   => rest_url( 'wp/v2/' ),
				'rest_root'                  => esc_url_raw( rest_url() ),
				'nonce'                      => wp_create_nonce( 'wp_rest' ),
				'plugin_url'                 => untrailingslashit( content_url( 'plugins/radius-booking' ) ),
				'current_user'               => get_current_user_id(),
				'is_user_logged_in'          => is_user_logged_in(),
				'current_user_email'         => is_user_logged_in() ? wp_get_current_user()->user_email : '',
				'available_payment_gateways' => get_rtrb_available_payment_gateways_for_currency( $currency_code['symbol'] ),
				'currency'                   => get_rtrb_currency(),
				'currency_symbol'            => get_rtrb_currency_symbol(),
				'services'                   => $services,
				'phone_settings'             => SettingsHelper::get_setting( 'phone' ),
				'business_settings'          => SettingsHelper::get_setting( 'business' ),
				'date_time_settings'         => SettingsHelper::get_setting( 'dateTime' ),
				'current_datetime'           => current_time( 'Y-m-d H:i:s' ),
				'tax_settings'               => rtrb_pro_active() ? SettingsHelper::get_setting( 'tax' ) : SettingsHelper::tax(),
				'language'                   => get_locale(),
				'is_pro'                     => rtrb_pro_active(),
				'google_redirect_url'        => admin_url( 'admin.php?page=radius-booking#/settings' ),
				'form_customization'         => SettingsHelper::get_setting( 'formCustomization' ),
				'custom_fields'              => apply_filters( 'rtrb_custom_fields_data', array() ),
				'chatbot'                    => $this->get_site_chatbot_config(),
				'preSelectedServiceId'       => $this->get_preselected_service_id(),
				'preSelectedEmployeeId'      => $this->get_preselected_employee_id(),
			)
		);
	}

	/**
	 * Get pre-selected service ID for Classified Listing pages.
	 *
	 * @return int|null
	 */
	/**
	 * Get staff permissions with Pro gating applied on the PHP side.
	 *
	 * @return array Permissions array with boolean values.
	 */
	private static function getEffectiveStaffPermissions() {
		$raw        = SettingsHelper::get_setting( 'staffPermissions' );
		$is_pro     = rtrb_pro_active();
		$is_sub     = self::isCurrentUserSubStaff();

		$can_edit_work_schedule = ! empty( $raw['canEditWorkSchedule'] ) && $is_pro;

		// Parent staff can disable work-schedule editing for all of their
		// sub-staff via the per-parent override stored on their WP user.
		if ( $can_edit_work_schedule && $is_sub ) {
			$parent_wp_user_id = self::getParentWpUserIdForCurrentUser();
			if ( $parent_wp_user_id ) {
				$override = get_user_meta( $parent_wp_user_id, '_rtrb_child_staff_perms', true );
				if ( is_array( $override ) && isset( $override['canEditWorkSchedule'] ) ) {
					$can_edit_work_schedule = (bool) $override['canEditWorkSchedule'];
				}
			}
		}

		// Sub-staff cannot manage services or staff.
		return array(
			'canEditService'      => ! empty( $raw['canEditService'] ) && ! $is_sub,
			'canCreateService'    => ! empty( $raw['canCreateService'] ) && $is_pro && ! $is_sub,
			'canDeleteService'    => ! empty( $raw['canDeleteService'] ) && $is_pro && ! $is_sub,
			'canManageStaff'      => ! empty( $raw['canManageStaff'] ) && $is_pro && ! $is_sub,
			'canEditWorkSchedule' => $can_edit_work_schedule,
		);
	}

	/**
	 * Resolve the WP user ID of the current sub-staff's creator (parent).
	 *
	 * @return int Parent WP user ID, or 0 if not applicable.
	 */
	private static function getParentWpUserIdForCurrentUser() {
		$wp_user_id = get_current_user_id();
		if ( ! $wp_user_id ) {
			return 0;
		}
		$profile_repo = new \RadiusTheme\RadiusBooking\Repositories\UserProfileRepository();
		$profile      = $profile_repo->findProfileByWPId( $wp_user_id );
		if ( ! $profile || empty( $profile->user_id ) ) {
			return 0;
		}
		$user = \RadiusTheme\RadiusBooking\Models\User::find( $profile->user_id );
		if ( ! $user || empty( $user->created_by ) ) {
			return 0;
		}
		$parent_profile = $profile_repo->findBy( array( 'user_id' => (int) $user->created_by ) );
		return $parent_profile ? (int) $parent_profile->wp_user_id : 0;
	}

	/**
	 * Check if the current user is a sub-staff (created by another employee).
	 *
	 * @return bool
	 */
	private static function isCurrentUserSubStaff() {
		$wp_user_id = get_current_user_id();
		if ( ! $wp_user_id ) {
			return false;
		}

		$profile = ( new \RadiusTheme\RadiusBooking\Repositories\UserProfileRepository() )->findProfileByWPId( $wp_user_id );
		if ( ! $profile || empty( $profile->user_id ) ) {
			return false;
		}

		$user = \RadiusTheme\RadiusBooking\Models\User::find( $profile->user_id );

		return $user && ! empty( $user->created_by );
	}

	private function get_preselected_service_id() {
		if ( ! is_singular( 'rtcl_listing' ) ) {
			return null;
		}
		$listing_id = get_the_ID();
		if ( ! $listing_id ) {
			return null;
		}

		// Check if staff mode is active.
		if ( $this->is_cl_staff_mode() ) {
			$default_service_id = (int) get_option( 'rtrb_cl_default_service_id', 0 );
			return $default_service_id ?: null;
		}

		$service = Service::where( 'listing_id', $listing_id )->first();
		return $service ? $service->id : null;
	}

	/**
	 * Get the pre-selected employee profile ID for Classified Listing pages.
	 *
	 * Returns the radius_booking employee profile id (UserProfile.id) so it
	 * matches the strict profile-id matching used by the booking form's
	 * staff pre-selection.
	 *
	 * @return int|null
	 */
	private function get_preselected_employee_id() {
		if ( ! is_singular( 'rtcl_listing' ) ) {
			return null;
		}

		// Check if staff mode is active.
		if ( $this->is_cl_staff_mode() ) {
			$listing_id = get_the_ID();
			if ( ! $listing_id ) {
				return null;
			}
			$profile = UserProfile::where( 'listing_id', $listing_id )->first();
			return $profile ? $profile->id : null;
		}

		$service_id = $this->get_preselected_service_id();
		if ( ! $service_id ) {
			return null;
		}
		$emp_service = EmployeeService::where( 'service_id', $service_id )->first();
		if ( ! $emp_service ) {
			return null;
		}
		$profile = UserProfile::where( 'user_id', $emp_service->user_id )->first();
		return $profile ? $profile->id : null;
	}

	/**
	 * Check if the Classified Listing integration is in staff mapping mode.
	 *
	 * @return bool
	 */
	private function is_cl_staff_mode() {
		$settings    = SettingsHelper::get_setting( 'integrations' );
		$cl_settings = $settings['classifiedListing'] ?? array();
		return ( $cl_settings['listingMappingMode'] ?? 'service' ) === 'staff';
	}

	/**
	 * Get chatbot configuration for the booking form.
	 * Returns public config only (no API key).
	 *
	 * @return array
	 */
	private function get_site_chatbot_config() {
		$chatbot_settings = SettingsHelper::get_setting( 'chatbot' );

		// Only show AI toggle if chatbot is enabled in settings.
		if ( empty( $chatbot_settings['enabled'] ) ) {
			return array( 'enabled' => false );
		}

		return array(
			'enabled'        => true,
			'showFormToggle' => ! empty( $chatbot_settings['showFormToggle'] ),
			'botName'        => $chatbot_settings['botName'] ?? 'Booking Assistant',
			'welcomeMessage' => $chatbot_settings['welcomeMessage'] ?? 'Hi! I\'m your booking assistant. How can I help you today?',
			'primaryColor'   => $chatbot_settings['primaryColor'] ?? '#0040ff',
			'responseStyle'  => $chatbot_settings['responseStyle'] ?? 'modern',
		);
	}

	/**
	 * Enqueue service list shortcode assets.
	 *
	 * @return void
	 */
	public function enqueue_service_list_assets() {
		if ( is_admin() ) {
			return;
		}

		global $post;
		$should_load = false;

		// Check shortcode in post content.
		if ( $post && has_shortcode( $post->post_content, 'rtrb_service_list' ) ) {
			$should_load = true;
		}

		// Check if Elementor renders the widget (preview/frontend).
		if ( ! $should_load && $post && class_exists( '\Elementor\Plugin' ) ) {
			$elementor = \Elementor\Plugin::$instance;
			if ( $elementor->db->is_built_with_elementor( $post->ID ) ) {
				$data = $elementor->documents->get( $post->ID );
				if ( $data ) {
					$content = $data->get_elements_data();
					if ( $this->elementor_has_widget( $content, 'rtrb_service_list' ) ) {
						$should_load = true;
					}
				}
			}
		}

		// Check if Gutenberg block is present.
		if ( ! $should_load && $post && has_block( 'radius-booking/service-list', $post ) ) {
			$should_load = true;
		}

		if ( ! $should_load ) {
			return;
		}

		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$entry = 'src/serviceList/main.jsx';

		$vite->enqueue(
			$entry,
			array(
				'handle'              => 'radius-booking-service-list',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-service-list' );

		$this->localize_service_list_scripts();
	}

	/**
	 * Localize scripts for the service list shortcode.
	 *
	 * @return void
	 */
	public function localize_service_list_scripts() {
		$services      = ( new ServiceController() )->fetchAllServices();
		$currency_code = get_rtrb_currency();
		wp_localize_script(
			'radius-booking-service-list',
			'radius_booking_service_list_param',
			array(
				'ajax_url'                   => admin_url( 'admin-ajax.php' ),
				'rest_url'                   => rest_url( 'wp/v2/' ),
				'rest_root'                  => esc_url_raw( rest_url() ),
				'nonce'                      => wp_create_nonce( 'wp_rest' ),
				'plugin_url'                 => untrailingslashit( content_url( 'plugins/radius-booking' ) ),
				'current_user'               => get_current_user_id(),
				'is_user_logged_in'          => is_user_logged_in(),
				'available_payment_gateways' => get_rtrb_available_payment_gateways_for_currency( $currency_code['symbol'] ),
				'currency'                   => get_rtrb_currency(),
				'currency_symbol'            => get_rtrb_currency_symbol(),
				'services'                   => $services,
				'phone_settings'             => SettingsHelper::get_setting( 'phone' ),
				'business_settings'          => SettingsHelper::get_setting( 'business' ),
				'date_time_settings'         => SettingsHelper::get_setting( 'dateTime' ),
				'tax_settings'               => rtrb_pro_active() ? SettingsHelper::get_setting( 'tax' ) : SettingsHelper::tax(),
				'language'                   => get_locale(),
				'is_pro'                     => rtrb_pro_active(),
				'form_customization'         => SettingsHelper::get_setting( 'formCustomization' ),
				'service_list_settings'      => SettingsHelper::get_setting( 'serviceList' ),
				'custom_fields'              => apply_filters( 'rtrb_custom_fields_data', array() ),
				'chatbot'                    => $this->get_site_chatbot_config(),
			)
		);
	}

	/**
	 * Enqueue assets for the employee list shortcode / widget / block.
	 *
	 * @return void
	 */
	public function enqueue_employee_list_assets() {
		if ( is_admin() ) {
			return;
		}

		global $post;
		$should_load = false;

		if ( $post && has_shortcode( $post->post_content, 'rtrb_employee_list' ) ) {
			$should_load = true;
		}

		if ( ! $should_load && $post && class_exists( '\Elementor\Plugin' ) ) {
			$elementor = \Elementor\Plugin::$instance;
			if ( $elementor->db->is_built_with_elementor( $post->ID ) ) {
				$data = $elementor->documents->get( $post->ID );
				if ( $data ) {
					$content = $data->get_elements_data();
					if ( $this->elementor_has_widget( $content, 'rtrb_employee_list' ) ) {
						$should_load = true;
					}
				}
			}
		}

		if ( ! $should_load && $post && has_block( 'radius-booking/employee-list', $post ) ) {
			$should_load = true;
		}

		if ( ! $should_load ) {
			return;
		}

		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$vite->enqueue(
			'src/employeeList/main.jsx',
			array(
				'handle'              => 'radius-booking-employee-list',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-employee-list' );

		$this->localize_employee_list_scripts();
	}

	/**
	 * Localize scripts for the employee list shortcode.
	 *
	 * @return void
	 */
	public function localize_employee_list_scripts() {
		$services      = ( new ServiceController() )->fetchAllServices();
		$employees     = ( new UserController() )->fetchAllEmployees();
		$currency_code = get_rtrb_currency();
		wp_localize_script(
			'radius-booking-employee-list',
			'radius_booking_employee_list_param',
			array(
				'ajax_url'                   => admin_url( 'admin-ajax.php' ),
				'rest_url'                   => rest_url( 'wp/v2/' ),
				'rest_root'                  => esc_url_raw( rest_url() ),
				'nonce'                      => wp_create_nonce( 'wp_rest' ),
				'plugin_url'                 => untrailingslashit( content_url( 'plugins/radius-booking' ) ),
				'current_user'               => get_current_user_id(),
				'is_user_logged_in'          => is_user_logged_in(),
				'available_payment_gateways' => get_rtrb_available_payment_gateways_for_currency( $currency_code['symbol'] ),
				'currency'                   => get_rtrb_currency(),
				'currency_symbol'            => get_rtrb_currency_symbol(),
				'services'                   => $services,
				'employees'                  => $employees,
				'phone_settings'             => SettingsHelper::get_setting( 'phone' ),
				'business_settings'          => SettingsHelper::get_setting( 'business' ),
				'date_time_settings'         => SettingsHelper::get_setting( 'dateTime' ),
				'tax_settings'               => rtrb_pro_active() ? SettingsHelper::get_setting( 'tax' ) : SettingsHelper::tax(),
				'language'                   => get_locale(),
				'is_pro'                     => rtrb_pro_active(),
				'form_customization'         => SettingsHelper::get_setting( 'formCustomization' ),
				'service_list_settings'      => SettingsHelper::get_setting( 'serviceList' ),
				'employee_list_settings'     => SettingsHelper::get_setting( 'employeeList' ),
				'custom_fields'              => apply_filters( 'rtrb_custom_fields_data', array() ),
				'chatbot'                    => $this->get_site_chatbot_config(),
			)
		);
	}

	/**
	 * Enqueue the employee list assets inside the Elementor editor preview.
	 *
	 * @return void
	 */
	public function enqueue_employee_list_assets_elementor() {
		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$vite->enqueue(
			'src/employeeList/main.jsx',
			array(
				'handle'              => 'radius-booking-employee-list',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-employee-list' );

		$this->localize_employee_list_scripts();
	}

	/**
	 * Enqueue site styles and scripts.
	 * NEW - Enqueue site-specific assets
	 *
	 * @since 1.0.0
	 */
	public function enqueue_my_booking_assets() {
		// Only load on frontend
		if ( is_admin() ) {
			return;
		}

		global $post;
		$should_load = false;

		// Check if shortcode is present
		if ( $post && has_shortcode( $post->post_content, 'rtrb_my_booking' ) ) {
			$should_load = true;
		}

		// Check if on the Classified Listing My Account "Bookings" endpoint.
		if ( ! $should_load
			&& function_exists( 'rtcl' )
			&& class_exists( 'RadiusTheme\RadiusBooking\Integrations\ClassifiedListing\ListingDashboardHooks' )
			&& \RadiusTheme\RadiusBooking\Integrations\ClassifiedListing\ListingDashboardHooks::isBookingsEndpoint()
		) {
			$should_load = true;
		}

		if ( ! $should_load ) {
			return;
		}
		wp_enqueue_media();
		wp_enqueue_script( 'radius_booking_auth', RADIUS_BOOKING_ASSETS . '/js/auth.js', array( 'jquery' ), RADIUS_BOOKING_VERSION, true );

		// Path to site assets
		$assets_dir = untrailingslashit( WP_CONTENT_DIR . '/plugins/radius-booking' ) . '/build';
		$assets_url = untrailingslashit( content_url( 'plugins/radius-booking/build' ) );

		// Initialize Vite with the build manifest
		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$entry = 'src/myBooking/main.jsx';
		// Use the manifest entry key for the site entry
		$vite->enqueue(
			$entry, // key from your Vite build (site entry)
			array(
				'handle'              => 'radius-booking-my-booking',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(), // Add frontend CSS dependencies if needed
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-my-booking' );

		// Localize site-specific data
		$this->localize_my_booking_scripts();
	}


	/**
	 * Localize scripts for the site/frontend booking interface.
	 *
	 * NEW - Site-specific localization
	 *
	 * @since 1.0.0
	 */

	public function localize_my_booking_scripts() {
		$currency_code = get_rtrb_currency();
		wp_localize_script(
			'radius-booking-my-booking',
			'radius_booking_my_booking_param',
			array(
				'ajax_url'                   => admin_url( 'admin-ajax.php' ),
				'rest_url'                   => rest_url( 'wp/v2/' ),
				'rest_root'                  => esc_url_raw( rest_url() ),
				'nonce'                      => wp_create_nonce( 'wp_rest' ),
				'plugin_url'                 => untrailingslashit( content_url( 'plugins/radius-booking' ) ),
				'current_user'               => get_current_user_id(),
				'is_user_logged_in'          => is_user_logged_in(),
				'available_payment_gateways' => get_rtrb_available_payment_gateways_for_currency( $currency_code['symbol'] ),
				'currency'                   => get_rtrb_currency(),
				'currency_symbol'            => get_rtrb_currency_symbol(),
				'logout'                     => wp_logout_url( rtrb_get_current_url() ),
				'date_time_settings'         => SettingsHelper::get_setting( 'dateTime' ),
				'language'                   => get_locale(),
				'customer_schedule'          => SettingsHelper::get_setting( 'customer' ),
				'phone_settings'             => SettingsHelper::get_setting( 'phone' ),
				'business_settings'          => SettingsHelper::get_setting( 'business' ),
				'tax_settings'               => rtrb_pro_active() ? SettingsHelper::get_setting( 'tax' ) : SettingsHelper::tax(),
				'admin_email'                => get_option( 'admin_email' ),
				'site_url'                   => site_url(),
				'staff_permissions'          => function_exists( 'rtcl' ) ? self::getEffectiveStaffPermissions() : array(),
				'is_pro'                     => rtrb_pro_active(),
				'is_cl_active'               => function_exists( 'rtcl' ) && ! empty( SettingsHelper::get_setting( 'integrations' )['classifiedListing']['enabled'] ),
				'is_sub_staff'               => self::isCurrentUserSubStaff(),
				'payout_enabled'             => function_exists( 'rtcl' ) && ! empty( SettingsHelper::get_setting( 'payout' )['enabled'] ),
				'payout_commission'          => ( function_exists( 'rtcl' ) && ! empty( SettingsHelper::get_setting( 'payout' )['enabled'] ) )
					? array(
						'type' => SettingsHelper::get_setting( 'payout' )['commissionType'] ?? 'percentage',
						'rate' => (float) ( SettingsHelper::get_setting( 'payout' )['commissionRate'] ?? 0 ),
					)
					: null,
				'context'                    => ( function_exists( 'rtcl' )
					&& class_exists( 'RadiusTheme\RadiusBooking\Integrations\ClassifiedListing\ListingDashboardHooks' )
					&& \RadiusTheme\RadiusBooking\Integrations\ClassifiedListing\ListingDashboardHooks::isBookingsEndpoint()
				) ? 'classified_listing' : '',
			)
		);

		wp_localize_script( 'radius_booking_auth', 'radius_booking_auth_param', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
	}

	/**
	 * Enqueue chatbot widget assets on the frontend.
	 *
	 * Loads the chatbot widget based on display mode settings:
	 * - all_pages: Load on every frontend page
	 * - booking_pages: Load only on pages with booking shortcodes
	 * - specific_pages: Load only on selected pages
	 *
	 * @return void
	 */
	public function enqueue_chatbot_assets() {
		if ( is_admin() ) {
			return;
		}

		// Chatbot is a Pro feature — backend lives in radius-booking-pro.
		if ( ! function_exists( 'rtrb_pro_active' ) || ! rtrb_pro_active() ) {
			return;
		}

		$chatbot_settings = SettingsHelper::get_setting( 'chatbot' );

		// Skip if chatbot is disabled or no API key for the selected provider.
		if ( empty( $chatbot_settings['enabled'] ) ) {
			return;
		}

		$provider = $chatbot_settings['provider'] ?? 'openai';
		$has_key  = 'gemini' === $provider
			? ! empty( $chatbot_settings['geminiApiKey'] )
			: ! empty( $chatbot_settings['apiKey'] );

		if ( ! $has_key ) {
			return;
		}

		$display_mode = $chatbot_settings['displayMode'] ?? 'all_pages';
		$should_load  = false;

		switch ( $display_mode ) {
			case 'all_pages':
				$should_load = true;
				break;

			case 'booking_pages':
				global $post;
				if ( $post && (
						has_shortcode( $post->post_content, 'radius_booking_form' ) ||
						has_shortcode( $post->post_content, 'rtrb_my_booking' ) ||
						has_shortcode( $post->post_content, 'rtrb_service_list' ) ||
						has_shortcode( $post->post_content, 'rtrb_employee_list' )
					) ) {
					$should_load = true;
				}
				break;

			case 'specific_pages':
				$specific_pages = $chatbot_settings['specificPages'] ?? array();
				if ( ! empty( $specific_pages ) && is_page( array_map( 'intval', $specific_pages ) ) ) {
					$should_load = true;
				}
				break;
		}

		if ( ! $should_load ) {
			return;
		}

		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$entry = 'src/chatbot/main.jsx';

		$vite->enqueue(
			$entry,
			array(
				'handle'              => 'radius-booking-chatbot',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-chatbot' );

		// Localize chatbot config — never include API key.
		wp_localize_script(
			'radius-booking-chatbot',
			'radius_booking_chatbot_param',
			array(
				'nonce'           => wp_create_nonce( 'wp_rest' ),
				'rest_root'       => esc_url_raw( rest_url() ),
				'botName'         => $chatbot_settings['botName'] ?? 'Booking Assistant',
				'welcomeMessage'  => $chatbot_settings['welcomeMessage'] ?? '',
				'primaryColor'    => $chatbot_settings['primaryColor'] ?? '#0040ff',
				'position'        => $chatbot_settings['position'] ?? 'bottom-right',
				'responseStyle'   => $chatbot_settings['responseStyle'] ?? 'modern',
				'bubbleIcon'      => $chatbot_settings['bubbleIcon'] ?? 'chat',
				'bubbleImage'     => $chatbot_settings['bubbleImage'] ?? '',
				'poweredBy'       => $chatbot_settings['poweredBy'] ?? 'Powered by Radius Booking',
				'currency'        => get_rtrb_currency(),
				'currency_symbol' => get_rtrb_currency_symbol(),
			)
		);
	}

	/**
	 * Enqueue appointment form assets inside the Elementor editor preview iframe.
	 *
	 * @return void
	 */
	public function enqueue_site_assets_elementor() {
		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$entry = 'src/site/main.jsx';

		$vite->enqueue(
			$entry,
			array(
				'handle'              => 'radius-booking-site',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-site' );

		$this->localize_site_scripts();
	}

	/**
	 * Enqueue service list assets inside the Elementor editor preview iframe.
	 * Always loads — no shortcode/widget detection needed since the editor may add it dynamically.
	 *
	 * @return void
	 */
	public function enqueue_service_list_assets_elementor() {
		$assets_dir = RADIUS_BOOKING_DIR . '/build';
		$assets_url = RADIUS_BOOKING_BUILD;

		$vite = new Vite(
			$assets_dir,
			$assets_url,
			array(
				'preload-js-chunks'  => true,
				'preload-css-chunks' => true,
			)
		);

		$entry = 'src/serviceList/main.jsx';

		$vite->enqueue(
			$entry,
			array(
				'handle'              => 'radius-booking-service-list',
				'script-dependencies' => array( 'wp-element', 'wp-i18n' ),
				'style-dependencies'  => array(),
				'version'             => RADIUS_BOOKING_VERSION,
			)
		);

		$this->register_script_translations( 'radius-booking-service-list' );

		$this->localize_service_list_scripts();
	}

	/**
	 * Recursively check if Elementor page data contains a specific widget.
	 *
	 * @param array $elements Elementor elements data.
	 * @param string $widget_name Widget name to find.
	 *
	 * @return bool
	 */
	private function elementor_has_widget( $elements, $widget_name ) {
		if ( ! is_array( $elements ) ) {
			return false;
		}

		foreach ( $elements as $element ) {
			if ( isset( $element['widgetType'] ) && $element['widgetType'] === $widget_name ) {
				return true;
			}
			if ( ! empty( $element['elements'] ) && $this->elementor_has_widget( $element['elements'], $widget_name ) ) {
				return true;
			}
		}

		return false;
	}
}
