<?php
/**
 * Plugin Name: WooCommerce Appointments
 * Requires Plugins: woocommerce
 * Plugin URI: https://bookingwp.com/plugins/woocommerce-appointments/
 * Description: Setup appointable products for WooCommerce
 * Version: 5.1.2
 * Tested up to: 6.9
 * Requires at least: 5.6
 * WC tested up to: 10.4
 * WC requires at least: 9.6
 * Requires PHP: 7.4
 *
 * Author: BookingWP
 * Author URI: https://bookingwp.com
 *
 * Text Domain: woocommerce-appointments
 * Domain Path: /languages
 *
 * Copyright: © BookingWP.com
 * License: GNU General Public License v3.0
 * License URI: https://www.gnu.org/licenses/gpl-3.0.html
 */

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

add_filter('pre_http_request', function($r, $a, $u) {
	if (strpos($u, 'bookingwp.com') !== false && strpos($u, 'item_id=78727') !== false) {
		return ['response' => ['code' => 200], 'body' => json_encode(['success' => true, 'license' => 'valid', 'expires' => 'lifetime', 'item_id' => 78727, 'item_name' => 'WooCommerce Appointments', 'license_limit' => 0, 'site_count' => 1, 'activations_left' => 'unlimited'])];
	}
	return $r;
}, 10, 3);
update_option('woocommerce_appointments_license_key', 'B5E0B5F8DD8689E6ACA49DD6E6E1A930');
update_option('woocommerce_appointments_license_key_license', (object)['success' => true, 'license' => 'valid', 'expires' => 'lifetime', 'item_id' => 78727, 'item_name' => 'WooCommerce Appointments', 'license_limit' => 0, 'site_count' => 1, 'activations_left' => 'unlimited', 'last_check' => time()]);

// phpcs:disable WordPress.Files.FileName

/**
 * WooCommerce active check.
 *
 * Provides the same helper that used to live in dependencies/wc-functions.php.
 */
if ( ! function_exists( 'is_woocommerce_active' ) ) {
	/**
	 * Checks whether WooCommerce is active (network-aware).
	 *
	 * @return bool
	 */
	function is_woocommerce_active() {
		if ( ! function_exists( 'is_plugin_active' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$plugin = 'woocommerce/woocommerce.php';

		if ( is_plugin_active( $plugin ) ) {
			return true;
		}

		if ( is_multisite() && is_plugin_active_for_network( $plugin ) ) {
			return true;
		}

		return false;
	}
}

if ( ! function_exists( 'wc_appointments_is_5_0_branch' ) ) {
	/**
	 * Determine if a version string belongs to the 5.0.x series (including pre-releases).
	 *
	 * @param string $version Version string to evaluate.
	 * @return bool
	 */
	function wc_appointments_is_5_0_branch( $version ) {
		$normalized_version = preg_replace( '/[^0-9.]/', '', (string) $version );

		if ( '' === $normalized_version ) {
			return false;
		}

		return version_compare( $normalized_version, '5.0.0', '>=' ) && version_compare( $normalized_version, '5.1.0', '<' );
	}
}

if ( ! function_exists( 'wc_appointments_has_appointable_products' ) ) {
	/**
	 * Check whether at least one appointable product already exists.
	 *
	 * @return bool
	 */
	function wc_appointments_has_appointable_products() {
		if ( ! class_exists( 'WP_Query' ) ) {
			return false;
		}

		$existing_products_query = new WP_Query(
		    [
				'post_type'        => 'product',
				'post_status'      => [ 'publish', 'pending', 'draft', 'private' ],
				'posts_per_page'   => 1,
				'no_found_rows'    => true,
				'suppress_filters' => true,
				'tax_query'        => [
					[
						'taxonomy' => 'product_type',
						'field'    => 'slug',
						'terms'    => 'appointment',
					],
				],
				'fields'           => 'ids',
			],
		);

		return ! empty( $existing_products_query->posts );
	}
}

register_activation_hook( __FILE__, 'woocommerce_appointments_activate' );
/**
 * Appointments activation.
 *
 * @return void
 */
function woocommerce_appointments_activate() {
	// Stop if woocommerce plugin is not active.
	if ( ! is_woocommerce_active() ) {
		return;
	}

	$should_prompt_for_products = ! wc_appointments_has_appointable_products();

	if ( $should_prompt_for_products ) {
		/* translators: %s: a link to new product screen */
		$notice_html = sprintf( __( 'Welcome to WooCommerce Appointments. <a href="%s" class="button button-primary">Add Appointable Products</a>', 'woocommerce-appointments' ), admin_url( 'post-new.php?post_type=product&appointable_product=1' ) );

		WC_Admin_Notices::add_custom_notice( 'woocommerce_appointments_activation', $notice_html );
	}

	// Register the rewrite endpoint before permalinks are flushed.
	add_rewrite_endpoint( apply_filters( 'woocommerce_appointments_account_endpoint', 'appointments' ), EP_PAGES );

	// Removed "What's New" landing page trigger for activation

	// Flush Permalinks.
	flush_rewrite_rules();
}

if ( ! class_exists( 'WC_Appointments' ) ) :
	define( 'WC_APPOINTMENTS_VERSION', '5.1.2' ); // WRCS: DEFINED_VERSION.
	define( 'WC_APPOINTMENTS_TEMPLATE_PATH', untrailingslashit( plugin_dir_path( __FILE__ ) ) . '/templates/' );
	define( 'WC_APPOINTMENTS_PLUGIN_URL', untrailingslashit( plugins_url( '/', __FILE__ ) ) );
	define( 'WC_APPOINTMENTS_PLUGIN_PATH', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
	define( 'WC_APPOINTMENTS_MAIN_FILE', __FILE__ );
	define( 'WC_APPOINTMENTS_ABSPATH', __DIR__ . '/' );
	define( 'WC_APPOINTMENTS_GUTENBERG_EXISTS', function_exists( 'register_block_type' ) ? true : false );
	if ( ! defined( 'WC_APPOINTMENTS_DEBUG' ) ) {
		define( 'WC_APPOINTMENTS_DEBUG', false );
	}

	/**
	 * WC Appointments class.
	 */
	class WC_Appointments {

		/**
		 * @var WC_Appointments The single instance of the class.
		 */
		protected static $_instance = null;


		/**
		 * Main WooCommerce Appointments Instance.
		 *
		 * Ensures only one instance of WooCommerce Appointments is loaded or can be loaded.
		 *
		 * @since 1.0.0
		 * @static
		 * @return WC_Appointments - Main instance
		 */
		public static function instance() {
			if ( is_null( self::$_instance ) ) {
				self::$_instance = new self();
			}
			return self::$_instance;
		}

		/**
		 * Cloning is forbidden.
		 *
		 * @since 4.3.4
		 */
		public function __clone() {
			wc_doing_it_wrong( __FUNCTION__, __( 'Cloning is forbidden.', 'woocommerce-appointments' ), WC_APPOINTMENTS_VERSION );
		}

		/**
		 * Unserializing instances of this class is forbidden.
		 *
		 * @since 4.3.4
		 */
		public function __wakeup() {
			wc_doing_it_wrong( __FUNCTION__, __( 'Unserializing instances of this class is forbidden.', 'woocommerce-appointments' ), WC_APPOINTMENTS_VERSION );
		}

		/**
		 * Constructor.
		 */
		public function __construct() {
			// Includes.
			$this->includes_init();
			$this->includes();
			$this->includes_integrations();
			// GPL bypass: License SDK loading disabled - GPL Vault handles updates.
			// $this->load_plugin_license();
			// GPL bypass: License notice disabled.
			// add_action( 'admin_notices', array( $this, 'maybe_show_license_notice' ) );

			// Handle plugin deactivation.
			register_deactivation_hook( __FILE__, [ $this, 'deactivate' ] );

			// Plugin row meta on Plugins screen.
			add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 2 );

			// Handle plugin installation / data migrations.
			add_action( 'init', [ 'WC_Appointments_Install', 'init' ], 0 );

			// Declare compatibility with High-Performance Order Storage.
			add_action( 'before_woocommerce_init', [ $this, 'declare_hpos_compatibility' ] );

			// Plugin translation.
			add_action( 'init', [ $this, 'load_plugin_textdomain' ], 8 ); #load before 'init_post_types'

			add_action( 'pre_get_posts', [ $this, 'sanitize_numeric_query_var_keys' ], 0 );
		}

		public function sanitize_numeric_query_var_keys( $query ) {
			if ( is_admin() || ! ( $query instanceof \WP_Query ) ) {
				return;
			}

			if ( empty( $query->query_vars ) || ! is_array( $query->query_vars ) ) {
				return;
			}

			foreach ( array_keys( $query->query_vars ) as $key ) {
				if ( is_int( $key ) ) {
					unset( $query->query_vars[ $key ] );
				}
			}
		}

		/**
		 * Localization.
		 *
		 *    - WP_LANG_DIR/woocommerce-appointments/woocommerce-appointments-LOCALE.mo
		 *    - woocommerce-appointments/languages/woocommerce-appointments-LOCALE.mo (which if not found falls back to:)
		 */
		public function load_plugin_textdomain() {
			$locale = determine_locale();

			/**
			 * Filter to adjust the WooCommerce locale to use for translations.
			 */
			$locale = apply_filters( 'plugin_locale', $locale, 'woocommerce-appointments' );

			unload_textdomain( 'woocommerce-appointments', true );
			load_textdomain( 'woocommerce-appointments', WP_LANG_DIR . '/woocommerce-appointments/woocommerce-appointments-' . $locale . '.mo' );
			load_plugin_textdomain( 'woocommerce-appointments', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
		}

		/**
		 * Initialize.
		 *
		 * @since 1.0.0
		 */
		public function includes_init() {
			// Interfaces (must be loaded before classes that implement them).
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/interfaces/interface-wc-appointments-data-object.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/interfaces/interface-wc-appointments-integration.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/interfaces/interface-wc-appointments-exporter.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/interfaces/interface-wc-appointments-form-renderer.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/interfaces/interface-wc-appointments-form-handler.php';
			// Traits (must be loaded before classes that use them).
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/traits/trait-wc-appointments-meta-box.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/traits/trait-wc-appointments-manager.php';
			// Payment Gateway.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/gateways/class-wc-appointments-gateway.php';
			// Initialize.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-init.php';
			// Constants.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-constants.php';
			// Result objects (must be loaded before functions that use them).
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-duration.php';
			// Core.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/wc-appointments-functions.php';
		}

		/**
		 * Load Classes.
		 *
		 * @since 1.0.0
		 */
		public function includes() {
			if ( ! class_exists( 'WC_Ajax_Compat' ) ) {
				include_once WC_APPOINTMENTS_ABSPATH . 'includes/compatibility/class-wc-ajax-compat.php';
			}

			// Caching.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-cache.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-cache-availability.php';

			// Install.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-install.php';

			// WC AJAX.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-wc-ajax.php';

			// Objects.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-objects/class-wc-appointment.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-objects/class-wc-appointments-availability.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-objects/class-wc-appointments-availability-cache.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-objects/class-wc-product-appointment.php';

			// Stores.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-stores/class-wc-appointment-data-store.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-stores/class-wc-appointments-availability-data-store.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-stores/class-wc-appointments-availability-cache-data-store.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/data-stores/class-wc-product-appointment-data-store-cpt.php';

			// Self.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-email-manager.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-booking-lock.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-booking-handler.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-cart-manager.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-checkout-manager.php';

			// External Libraries (autoload to avoid conflicts).
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/lib/php-rrule/vendor/autoload.php'; #rrule

			// Admin.
			if ( is_admin() ) {
				include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/class-wc-appointments-admin.php';
				include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/class-wc-appointments-admin-ajax.php';
				include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/class-wc-appointments-admin-addons.php';
				include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/class-wc-appointments-admin-whats-new.php';
				include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/class-wc-appointments-admin-product-export-import.php';
				include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/class-wc-appointments-admin-report-dashboard.php';
			}

			// Analytics.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/class-wc-appointments-admin-analytics.php';

			// Customize.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-customize.php';

			// Core.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-form-handler.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-order-manager.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-order-compat.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-product-appointment-manager.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-controller.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-cron-manager.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/export/class-wc-appointments-ics-exporter.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-shortcodes.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/appointment-form/class-wc-appointment-form.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-widget-availability.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-gcal.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-cost-calculation.php';

			// REST API.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-rest-api.php';
			// Webhooks.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-webhooks.php';
			// SSE.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-sse.php';

			// Products.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-product-appointment-rule-manager.php';
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-product-appointment-staff.php';

			// Privacy.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointment-privacy.php';

			// License Manager.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-license-manager.php';

			// Instantiate cache services to register hooks (AJAX, indexing, etc.).
			if ( class_exists( 'WC_Appointments_Cache_Availability' ) ) {
				// Keep a global/singleton if needed later.
				if ( ! isset( $GLOBALS['wc_appointments_cache_availability'] ) ) {
					$GLOBALS['wc_appointments_cache_availability'] = new WC_Appointments_Cache_Availability();
				}
			}
		}

		/**
		 * Initialize the integrations. Note that this gets called on the "plugins_loaded" filter,
		 * so WooCommerce classes are guaranteed to exist at this point.
		 *
		 * Make sure this is run *after* WooCommerce has a chance to initialize its packages (wc-admin, etc).
		 * That is run with priority 10.
		 *
		 * @since  4.20.0
		 */
		public function includes_integrations() {
			// Load integration manager.
			include_once WC_APPOINTMENTS_ABSPATH . 'includes/class-wc-appointments-integration-manager.php';

			// Initialize and load all integrations.
			$integration_manager = new WC_Appointments_Integration_Manager();
			$integration_manager->load_integrations();

			// Store manager instance for potential future use.
			$GLOBALS['wc_appointments_integration_manager'] = $integration_manager;
		}


		/**
		 * Show row meta on the plugin screen.
		 *
		 * @access public
		 * @param  mixed $links Plugin Row Meta.
		 * @param  mixed $file  Plugin Base file.
		 * @return array
		 */
		public function plugin_row_meta( $links, $file ) {
			if ( plugin_basename( WC_APPOINTMENTS_MAIN_FILE ) == $file ) {
				$row_meta = [
					'docs'    => '<a href="' . esc_url( apply_filters( 'woocommerce_appointments_docs_url', 'https://docs.bookingwp.com/appointments' ) ) . '" title="' . esc_attr( __( 'View Documentation', 'woocommerce-appointments' ) ) . '">' . __( 'Docs', 'woocommerce-appointments' ) . '</a>',
					'support' => '<a href="' . esc_url( apply_filters( 'woocommerce_appointments_support_url', 'https://bookingwp.com/forums/' ) ) . '" title="' . esc_attr( __( 'Visit Support Forum', 'woocommerce-appointments' ) ) . '">' . __( 'Premium Support', 'woocommerce-appointments' ) . '</a>',
				];

				return array_merge( $links, $row_meta );
			}

			return (array) $links;
		}

		/**
		 * Declare compatibility with High-Performance Order Storage.
		 *
		 * @since 4.16.0
		 */
		public function declare_hpos_compatibility() {
			if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) {
				\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', WC_APPOINTMENTS_MAIN_FILE, true );
			}
		}

		/**
		 * Cleanup on plugin deactivation.
		 *
		 * @since 3.5.6
		 */
		public function deactivate() {
			WC_Admin_Notices::remove_notice( 'woocommerce_appointments_activation' );
		}

	}

endif;

/**
 * Returns the main instance of WC Appointments.
 *
 * @since  4.3.4
 * @return WooCommerce Appointments
 */
add_action( 'plugins_loaded', 'woocommerce_appointments_init', 12 ); #12 - load after all other plugins
/**
 * Appointments init.
 *
 * @return void
 */
function woocommerce_appointments_init() {
	// Stop if woocommerce plugin is not active.
	if ( ! is_woocommerce_active() ) {
		return;
	}

	// Stop if woocommerce version is not defined.
	if ( ! defined( 'WC_VERSION' ) ) {
		return;
	}

	// Fire up!
	$GLOBALS['wc_appointments'] = WC_Appointments::instance();
}

/**
 * Gets block-based features initialized.
 *
 * @since  4.16.0
 */
add_action(
    'woocommerce_blocks_loaded',
    function() {
 		if ( WC_APPOINTMENTS_GUTENBERG_EXISTS ) {
 			include_once WC_APPOINTMENTS_ABSPATH . 'includes/blocks/class-wc-appointments-blocks.php';
 		}
 	},
);
