<?php
/**
 * Plugin Name: Simply Buy
 * Plugin URI: https://profitclicker.com/
 * Description: Simply Buy — the ultra-light mini shop for WordPress.
 * Version: 1.3.16
 * Requires at least: 6.2
 * Tested up to: 6.9
 * Requires PHP: 8.0
 * Author: ProfitClicker
 * Author URI: https://profitclicker.com/
 * Text Domain: simply-buy
 * Domain Path: /languages
 * License: GPL v2 or later
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 */

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

if ( ! defined( 'SBUY_CORE_PLUGIN_URL' ) ) {
	define( 'SBUY_CORE_PLUGIN_URL', trailingslashit( plugin_dir_url( __FILE__ ) ) );
}

if ( ! defined( 'SBUY_CORE_PLUGIN_PATH' ) ) {
	define( 'SBUY_CORE_PLUGIN_PATH', trailingslashit( plugin_dir_path( __FILE__ ) ) );
}

require_once SBUY_CORE_PLUGIN_PATH . 'google-api-php-client/vendor/autoload.php';

// Autoload all PHP files in the includes/ folder.
foreach ( glob( __DIR__ . '/includes/*.php' ) as $sbuy_filename ) {
	include_once $sbuy_filename;
}

if ( ! defined( 'SBUY_CORE_CURRENCY_SYMBOL' ) ) {
	$sbuy_opts = get_option( 'sbuy_settings_finance' );
	$sbuy_code = is_array( $sbuy_opts ) ? ( $sbuy_opts['currency_code'] ?? 'EUR' ) : 'EUR';
	$sbuy_map  = array(
		'EUR' => '€',
		'USD' => '$',
		'GBP' => '£',
		'AUD' => '$',
		'CAD' => '$',
		'CHF' => 'CHF',
		'CZK' => 'Kč',
		'PLN' => 'zł',
		'HUF' => 'Ft',
		'UAH' => '₴',
		'JPY' => '¥',
		'CNY' => '¥',
		'SEK' => 'kr',
		'NOK' => 'kr',
		'DKK' => 'kr',
		'RON' => 'lei',
		'RUB' => '₽',
	);
	define( 'SBUY_CORE_CURRENCY_SYMBOL', $sbuy_map[ strtoupper( $sbuy_code ) ] ?? '€' );
}

if ( ! defined( 'SBUY_PLUGIN_VERSION' ) ) {
	define( 'SBUY_PLUGIN_VERSION', sbuy_get_plugin_version() );
}

// Alias for add-ons (e.g. Pro) that expect a generic version constant.
if ( ! defined( 'SBUY_VERSION' ) ) {
	define( 'SBUY_VERSION', SBUY_PLUGIN_VERSION );
}

if ( ! defined( 'SBUY_TOTAL_AVAILABLE_SLOT' ) ) {
	define( 'SBUY_TOTAL_AVAILABLE_SLOT', 6 );
}

/**
 * SimplyBuy final class
 *
 * @since 1.0.0
 */
final class SimplyBuy {
	/**
	 * The unique instance of the class.
	 */
	private static ?SimplyBuy $instance = null;

	/**
	 * Gets an instance of this class.
	 */
	public static function get_instance(): SimplyBuy {

		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Construct
	 */
	public function __construct() {
		load_plugin_textdomain( 'simply-buy', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );

		new SBUY_Product_CPT();
		new SBUY_Taxonomy();
		new SBUY_Metabox();
		new SBUY_Script();
		new SBUY_Ajax();
		new SBUY_Settings();
		new SBUY_Shortcode();
		new SBUY_Cache_Compat();
	}

	/**
	 * Activation
	 *
	 * @since 1.0.0
	 */
	public static function activation( $network_wide ) {
		global $wpdb;

		// Load the plugin textdomain so translated strings used below
		// (e.g. "Thank you for scheduling.") land localized in the new pages.
		load_plugin_textdomain( 'simply-buy', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );

		if ( is_multisite() && $network_wide ) {
			$blog_list = get_sites();
			foreach ( $blog_list as $blog ) {
				$blog_id = $blog->blog_id;

				switch_to_blog( $blog_id );

				sbuy_ensure_plugin_pages();

				$default_times = sbuy_create_default_time( 0, 6 );
				update_option( 'sbuy_settings_time', $default_times );

				$table_name = $wpdb->prefix . 'sbuy_booking';

				$charset_collate = $wpdb->get_charset_collate();

				$sql = "CREATE TABLE $table_name (
					id mediumint(9) NOT NULL AUTO_INCREMENT,
					date tinytext NOT NULL,
					slot tinytext NOT NULL,
					is_bulk tinyint(1) NOT NULL DEFAULT 0,
					PRIMARY KEY  (id)
					) $charset_collate;";

				require_once ABSPATH . 'wp-admin/includes/upgrade.php';
				dbDelta( $sql );

				$log_table_name = $wpdb->prefix . 'sbuy_logs';
				$log_sql        = "CREATE TABLE $log_table_name (
					id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
					event_time DATETIME NOT NULL,
					event_type VARCHAR(100) NOT NULL,
					message TEXT NOT NULL,
					PRIMARY KEY  (id),
					KEY event_time (event_time),
					KEY event_type (event_type)
				) $charset_collate;";
				dbDelta( $log_sql );

				if ( ! wp_next_scheduled( 'sbuy_clear_old_logs' ) ) {
					wp_schedule_event( time(), 'daily', 'sbuy_clear_old_logs' );
				}

				$backlink_text = sbuy_get_backlink_text( null, true );
				update_option( 'sbuy_backlink_text', $backlink_text );

				restore_current_blog();
			}
		} else {

			sbuy_ensure_plugin_pages();

			$default_times = sbuy_create_default_time( 0, 6 );
			update_option( 'sbuy_settings_time', $default_times );

			$table_name = $wpdb->prefix . 'sbuy_booking';

			$charset_collate = $wpdb->get_charset_collate();

			$sql = "CREATE TABLE $table_name (
			id mediumint(9) NOT NULL AUTO_INCREMENT,
			date tinytext NOT NULL,
			slot tinytext NOT NULL,
			is_bulk tinyint(1) NOT NULL DEFAULT 0,
			PRIMARY KEY  (id)
			) $charset_collate;";

			require_once ABSPATH . 'wp-admin/includes/upgrade.php';
			dbDelta( $sql );

			$log_table_name = $wpdb->prefix . 'sbuy_logs';
			$log_sql        = "CREATE TABLE $log_table_name (
				id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
				event_time DATETIME NOT NULL,
				event_type VARCHAR(100) NOT NULL,
				message TEXT NOT NULL,
				PRIMARY KEY  (id),
				KEY event_time (event_time),
				KEY event_type (event_type)
			) $charset_collate;";
			dbDelta( $log_sql );

			if ( ! wp_next_scheduled( 'sbuy_clear_old_logs' ) ) {
				wp_schedule_event( time(), 'daily', 'sbuy_clear_old_logs' );
			}

			$backlink_text = sbuy_get_backlink_text( null, true );

			update_option( 'sbuy_backlink_text', $backlink_text );
		}

		add_role( 'sbuy_manager', __( 'SimplyBuy Manager', 'simply-buy' ), array(
			'read'           => true,
			'manage_options' => true,
		), );
	}

	/**
	 * Deactivation
	 *
	 * @return bool
	 */
	public static function deactivation(): bool {

		wp_clear_scheduled_hook( 'sbuy_clear_old_logs' );

		$cookie_name = sbuy_get_cookie_name();

		if ( isset( $_COOKIE[ $cookie_name ] ) ) {

			setcookie( $cookie_name, '', - 1, '/' );

			return true;

		}

		return false;
	}

	/**
	 * Uninstallation
	 *
	 * @return bool
	 * @since 1.0.1
	 */
	public static function uninstallation() {
		if ( is_multisite() ) {
			$sites = get_sites( array( 'number' => 0 ) );
			foreach ( $sites as $site ) {
				switch_to_blog( $site->blog_id );
				self::cleanup_current_site();
				restore_current_blog();
			}
		} else {
			self::cleanup_current_site();
		}

		// Role is network-global in Multisite — remove once, outside the loop.
		remove_role( 'sbuy_manager' );
	}

	/**
	 * Per-site cleanup called during uninstallation. Runs against the currently
	 * switched-to blog; $wpdb->prefix is already correct after switch_to_blog().
	 */
	private static function cleanup_current_site(): void {
		global $wpdb;

		// DB-Tabellen löschen
		$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}sbuy_booking" );
		$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}sbuy_logs" );

		// Automatisch erstellte Seiten löschen (nur die 3 vom Plugin)
		$pages = get_option( 'sbuy_settings_pages' );
		if ( is_array( $pages ) ) {
			foreach ( $pages as $page_id ) {
				wp_delete_post( (int) $page_id, true );
			}
		}

		// Starter-Produkte löschen (nur die mit Flag) — Nutzer-Produkte bleiben erhalten
		$starter_posts = get_posts( array(
			'post_type'      => 'sbuy_product',
			'posts_per_page' => -1,
			'post_status'    => 'any',
			'meta_key'       => '_sbuy_starter_product',
			'meta_value'     => '1',
		) );
		foreach ( $starter_posts as $post ) {
			wp_delete_post( $post->ID, true );
		}

		// Alle Plugin-Options löschen
		delete_option( 'sbuy_settings_pages' );
		delete_option( 'sbuy_settings_time' );
		delete_option( 'sbuy_settings_finance' );
		delete_option( 'sbuy_settings_branding' );
		delete_option( 'sbuy_settings_email' );
		delete_option( 'sbuy_settings_google' );
		delete_option( 'sbuy_settings_checkout' );
		delete_option( 'sbuy_settings_privacy' );
		delete_option( 'sbuy_settings_debug' );
		delete_option( 'sbuy_settings_support' );
		delete_option( 'sbuy_backlink_text' );
		delete_option( 'sbuy_branding_toggle_blocked_until' );
		delete_option( 'sbuy_branding_notice_dismissed' );
		delete_option( 'sbuy_review_request_settings' );
		delete_option( 'sbuy_starter_repair_version' );
		delete_option( 'sbuy_bulk_blocked_dates' ); // legacy (replaced by is_bulk column)

		// Rate-limit transients für Checkout-AJAX (Key-Pattern: sbuy_checkout_<md5>)
		// $wpdb->options wechselt mit switch_to_blog automatisch auf wp_<id>_options.
		$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\\_transient\\_sbuy\\_checkout\\_%' OR option_name LIKE '\\_transient\\_timeout\\_sbuy\\_checkout\\_%'" );

		// Geplante Cron-Events aufräumen (pro Site, da WP-Cron pro Site läuft)
		wp_clear_scheduled_hook( 'sbuy_clear_old_logs' );
		wp_clear_scheduled_hook( 'sbuy_send_review_email' );
	}

	public static function clear_old_logs(): void {
		global $wpdb;
		$table_name = $wpdb->prefix . 'sbuy_logs';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$wpdb->query( $wpdb->prepare( "DELETE FROM %s WHERE event_time < %s", $table_name, gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) ) ) );
	}
}

/**
 * Bootstrap SimplyBuy and provide an extension hook for add-ons.
 */
function sbuy_bootstrap(): void {
	SimplyBuy::get_instance();

	/**
	 * Fired after SimplyBuy has been initialized.
	 * Add-ons (e.g. simplybuy-pro) can hook into this.
	 */
	do_action( 'sbuy_loaded' );
}
add_action( 'init', 'sbuy_bootstrap', 0 );

/**
 * Convenience accessor for the SimplyBuy singleton.
 */
function sbuy(): SimplyBuy {
	return SimplyBuy::get_instance();
}

register_activation_hook( __FILE__, array( 'SimplyBuy', 'activation' ) );
register_deactivation_hook( __FILE__, array( 'SimplyBuy', 'deactivation' ) );
register_uninstall_hook( __FILE__, array( 'SimplyBuy', 'uninstallation' ) );

add_action( 'sbuy_clear_old_logs', array( 'SimplyBuy', 'clear_old_logs' ) );

/**
 * Ensure the three plugin pages (cart, checkout, thank-you) exist and are
 * published. If the stored IDs point to missing/non-published posts, fresh
 * pages are created and the `sbuy_settings_pages` option is rewritten.
 *
 * Safe to call repeatedly. Must run in the context of the current blog
 * (caller handles switch_to_blog() on multisite).
 *
 * @return int Number of pages that were (re-)created.
 */
function sbuy_ensure_plugin_pages(): int {
	global $wpdb;

	// MySQL advisory lock — prevents race conditions when several concurrent
	// requests (Cloudflare origin-pulls, admin + front-end hits, …) all see
	// the same dead option and each try to heal independently.
	// Lock name is site-scoped so multisite subsites don't block each other.
	$lock_name = 'sbuy_pages_heal_' . get_current_blog_id();
	$acquired  = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT GET_LOCK(%s, 0)', $lock_name ) );
	if ( 1 !== $acquired ) {
		return 0; // another process is already healing — let it finish
	}

	try {
		return sbuy_ensure_plugin_pages_locked();
	} finally {
		$wpdb->get_var( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', $lock_name ) );
	}
}

function sbuy_ensure_plugin_pages_locked(): int {
	// Force-fresh read — another request may have just healed + updated.
	wp_cache_delete( 'sbuy_settings_pages', 'options' );

	// type slug → option key used in sbuy_settings_pages
	$map = array(
		'cart'     => 'simply_buy_cart_page',
		'checkout' => 'simply_buy_checkout_page',
		'thankyou' => 'simply_buy_thankyou_page',
	);

	$pages    = (array) get_option( 'sbuy_settings_pages', array() );
	$repaired = 0;

	foreach ( $map as $type => $option_key ) {
		$existing_id = isset( $pages[ $option_key ] ) ? (int) $pages[ $option_key ] : 0;
		if ( $existing_id ) {
			$post = get_post( $existing_id );
			if ( $post && 'publish' === $post->post_status && 'page' === $post->post_type ) {
				continue; // still healthy
			}
		}

		$parent = ( 'cart' !== $type && ! empty( $pages['simply_buy_cart_page'] ) )
			? (int) $pages['simply_buy_cart_page']
			: 0;

		$new_id = sbuy_create_plugin_page_with_translations( $type, $parent );
		if ( $new_id ) {
			$pages[ $option_key ] = $new_id;
			$repaired++;
		}
	}

	if ( $repaired > 0 ) {
		update_option( 'sbuy_settings_pages', $pages );
	}
	return $repaired;
}

/**
 * Create a plugin page for the given type. When Polylang is active, creates
 * one page per registered language and links them via pll_save_post_translations().
 * Returns the default-language page ID (to be stored in sbuy_settings_pages).
 *
 * On sites without Polylang, creates a single page and returns its ID — same
 * behaviour as SimplyBuy 1.2.x.
 *
 * @since 1.3.0
 * @param string $type      One of 'cart', 'checkout', 'thankyou'.
 * @param int    $parent_id Post-parent ID (0 for no parent).
 * @return int Default-language page ID, or 0 on failure.
 */
function sbuy_create_plugin_page_with_translations( string $type, int $parent_id = 0 ): int {
	$polylang_active = function_exists( 'pll_languages_list' )
		&& function_exists( 'pll_default_language' )
		&& function_exists( 'pll_set_post_language' )
		&& function_exists( 'pll_save_post_translations' );

	if ( ! $polylang_active ) {
		// Single-language install — preserve legacy behaviour.
		$page_id = wp_insert_post( array(
			'post_title'     => wp_strip_all_tags( sbuy_get_page_title( $type, 'en' ) ),
			'post_name'      => sbuy_get_page_slug( $type, 'en' ),
			'post_content'   => sbuy_get_page_content( $type, 'en' ),
			'post_status'    => 'publish',
			'post_type'      => 'page',
			'post_parent'    => $parent_id,
			'comment_status' => 'closed',
			'ping_status'    => 'closed',
		) );
		return ( is_wp_error( $page_id ) || ! $page_id ) ? 0 : (int) $page_id;
	}

	// Polylang: create one page per language and link them as translations.
	$languages    = (array) pll_languages_list();
	$default_lang = (string) pll_default_language();
	$translations = array();
	$default_id   = 0;

	foreach ( $languages as $lang ) {
		$page_id = wp_insert_post( array(
			'post_title'     => wp_strip_all_tags( sbuy_get_page_title( $type, $lang ) ),
			'post_name'      => sbuy_get_page_slug( $type, $lang ),
			'post_content'   => sbuy_get_page_content( $type, $lang ),
			'post_status'    => 'publish',
			'post_type'      => 'page',
			'post_parent'    => $parent_id,
			'comment_status' => 'closed',
			'ping_status'    => 'closed',
		) );
		if ( is_wp_error( $page_id ) || ! $page_id ) {
			continue;
		}

		pll_set_post_language( $page_id, $lang );
		$translations[ $lang ] = (int) $page_id;

		if ( $lang === $default_lang ) {
			$default_id = (int) $page_id;
		}
	}

	if ( ! empty( $translations ) ) {
		pll_save_post_translations( $translations );
	}

	// Fallback — if the default language somehow produced no page, return any
	// translation we did create so the option value remains valid.
	if ( ! $default_id && ! empty( $translations ) ) {
		$default_id = (int) reset( $translations );
	}

	return $default_id;
}

/**
 * Self-heal: on every plugins_loaded run, verify the three plugin pages still
 * exist. If the user deleted them from wp-admin, they come back automatically.
 * Cheap path — only writes when pages are actually missing.
 */
function sbuy_maybe_repair_plugin_pages() {
	if ( defined( 'WP_UNINSTALL_PLUGIN' ) ) {
		return;
	}
	static $done = false;
	if ( $done ) {
		return;
	}
	$done = true;

	$pages = get_option( 'sbuy_settings_pages', null );
	if ( ! is_array( $pages ) || empty( $pages ) ) {
		// Fresh install — activation hook will populate it.
		return;
	}

	$all_ok = true;
	foreach ( array( 'simply_buy_cart_page', 'simply_buy_checkout_page', 'simply_buy_thankyou_page' ) as $key ) {
		$id   = isset( $pages[ $key ] ) ? (int) $pages[ $key ] : 0;
		$post = $id ? get_post( $id ) : null;
		if ( ! $post || 'publish' !== $post->post_status || 'page' !== $post->post_type ) {
			$all_ok = false;
			break;
		}
	}
	if ( $all_ok ) {
		return;
	}

	$repaired = sbuy_ensure_plugin_pages();
	if ( $repaired > 0 && function_exists( 'sbuy_add_log' ) ) {
		sbuy_add_log( 'pages_self_heal', 'Recreated ' . $repaired . ' missing plugin page(s)' );
	}
}
// 'init' priority 20 — AFTER WP rewrite rules and our own init-0 bootstrap.
// Must not run on 'plugins_loaded' because wp_insert_post() internally calls
// get_permalink() which crashes when $wp_rewrite is still null.
add_action( 'init', 'sbuy_maybe_repair_plugin_pages', 20 );

function sbuy_maybe_migrate_options() {
	$map = array(
		'sb_settings_pages'    => 'sbuy_settings_pages',
		'sb_settings_time'     => 'sbuy_settings_time',
		'sb_settings_branding' => 'sbuy_settings_branding',
		'sb_settings_debug'    => 'sbuy_settings_debug',
		'sb_settings_support'  => 'sbuy_settings_support',
		'sb_settings_email'    => 'sbuy_settings_email',
		'sb_settings_finance'  => 'sbuy_settings_finance',
		'sb_settings_google'   => 'sbuy_settings_google',
		'sb_settings_checkout' => 'sbuy_settings_checkout',
		'sb_settings_privacy'  => 'sbuy_settings_privacy',
	);

	foreach ( $map as $old => $new ) {
		$old_value = get_option( $old, null );
		$new_value = get_option( $new, null );

		if ( null !== $old_value && null === $new_value ) {
			update_option( $new, $old_value );
			//delete_option( $old );
		}
	}
}

add_action( 'plugins_loaded', 'sbuy_maybe_migrate_options' );

function sbuy_maybe_migrate_cpt_and_tax() {
	global $wpdb;

	$wpdb->update( $wpdb->posts, array( 'post_type' => 'sbuy_product' ), array( 'post_type' => 'sb_product' ) );

	$wpdb->update( $wpdb->term_taxonomy, array( 'taxonomy' => 'sbuy_product_tag' ), array( 'taxonomy' => 'sb_product_tag' ) );

	$wpdb->update( $wpdb->term_taxonomy, array( 'taxonomy' => 'sbuy_product_category' ), array( 'taxonomy' => 'sb_product_category' ) );
}

add_action( 'plugins_loaded', 'sbuy_maybe_migrate_cpt_and_tax' );

function sbuy_maybe_migrate_tables() {
	global $wpdb;

	$old_booking = $wpdb->prefix . 'sb_booking';
	$old_logs    = $wpdb->prefix . 'sb_logs';

	$new_booking = $wpdb->prefix . 'sbuy_booking';
	$new_logs    = $wpdb->prefix . 'sbuy_logs';

	if ( $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $old_booking ) ) ) {
		if ( ! $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $new_booking ) ) ) {
			$wpdb->query( "RENAME TABLE {$old_booking} TO {$new_booking}" );
		}
	}

	if ( $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $old_logs ) ) ) {
		if ( ! $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $new_logs ) ) ) {
			$wpdb->query( "RENAME TABLE {$old_logs} TO {$new_logs}" );
		}
	}
}

add_action( 'plugins_loaded', 'sbuy_maybe_migrate_tables' );

function sbuy_maybe_add_is_bulk_column() {
	global $wpdb;
	$table  = $wpdb->prefix . 'sbuy_booking';
	$exists = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) );
	if ( ! $exists ) {
		return;
	}
	$column = $wpdb->get_var( $wpdb->prepare( "SHOW COLUMNS FROM {$wpdb->prefix}sbuy_booking LIKE %s", 'is_bulk' ) );
	if ( ! $column ) {
		$wpdb->query( "ALTER TABLE {$wpdb->prefix}sbuy_booking ADD COLUMN is_bulk tinyint(1) NOT NULL DEFAULT 0" );
	}
}

add_action( 'plugins_loaded', 'sbuy_maybe_add_is_bulk_column' );

/**
 * Self-heal the plugin's DB tables if they're missing.
 *
 * Normally `register_activation_hook` creates `{prefix}sbuy_booking` and
 * `{prefix}sbuy_logs`. But if the plugin was uninstalled (tables dropped)
 * and the plugin folder later re-deployed, `wp plugin activate simply-buy`
 * detects the plugin as already active and does NOT fire the activation
 * hook again — tables stay missing and every booking INSERT silently fails.
 *
 * Runs on `plugins_loaded` so we beat any code that wants to read/write
 * the tables. Cheap: one `SHOW TABLES LIKE` per request.
 */
function sbuy_maybe_repair_tables() {
	if ( defined( 'WP_UNINSTALL_PLUGIN' ) ) {
		return;
	}
	static $done = false;
	if ( $done ) {
		return;
	}
	$done = true;

	global $wpdb;
	$booking = $wpdb->prefix . 'sbuy_booking';
	$logs    = $wpdb->prefix . 'sbuy_logs';

	$has_booking = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $booking ) );
	$has_logs    = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $logs ) );

	if ( $has_booking && $has_logs ) {
		return;
	}

	if ( ! function_exists( 'dbDelta' ) ) {
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	}

	$charset_collate = $wpdb->get_charset_collate();

	if ( ! $has_booking ) {
		dbDelta( "CREATE TABLE $booking (
			id mediumint(9) NOT NULL AUTO_INCREMENT,
			date tinytext NOT NULL,
			slot tinytext NOT NULL,
			is_bulk tinyint(1) NOT NULL DEFAULT 0,
			PRIMARY KEY  (id)
		) $charset_collate;" );
	}

	if ( ! $has_logs ) {
		dbDelta( "CREATE TABLE $logs (
			id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			event_time DATETIME NOT NULL,
			event_type VARCHAR(100) NOT NULL,
			message TEXT NOT NULL,
			PRIMARY KEY  (id),
			KEY event_time (event_time),
			KEY event_type (event_type)
		) $charset_collate;" );
	}

	if ( function_exists( 'sbuy_add_log' ) ) {
		sbuy_add_log( 'tables_self_heal', sprintf(
			'Recreated missing table(s): %s%s%s',
			$has_booking ? '' : $booking . ' ',
			$has_logs ? '' : $logs,
			''
		) );
	}
}

add_action( 'plugins_loaded', 'sbuy_maybe_repair_tables', 1 );
