<?php
/*
    Plugin Name: BookWellNow Appointment Booking
    Description: Free WordPress appointment booking plugin with unlimited staff, booking calendar, shifts, customer panel, and PayPal Checkout.
    Version: 3.0.8
    Requires at least: 5.3
    Requires PHP: 7.4
    Plugin URI: https://bookwellnow.com/appointment-booking-plugin/
    Author: Book Well Now
    Author URI: https://bookwellnow.com/
    License: GPL-2.0-or-later
    License URI: https://www.gnu.org/licenses/gpl-2.0.html
    Text Domain: bookwellnow-appointment-booking
    Domain Path: /languages
*/

if (!defined('ABSPATH')) {
    exit;
}

if (!defined('BOOKWELLAB_VERSION')) {
    define('BOOKWELLAB_VERSION', '3.0.8');
}

define('BOOKWELLAB_FILE', __FILE__);
/** Unique plugin prefix for options, transients, and DB metadata in wp_options. */
define('BOOKWELLAB_OPTION_PREFIX', 'bookwellab_');
define('BOOKWELLAB_PATH', plugin_dir_path(__FILE__));
define('BOOKWELLAB_CLASSES_PATH', BOOKWELLAB_PATH . 'includes/classes/');
define('BOOKWELLAB_INCLUDES_PATH', BOOKWELLAB_PATH . '/includes/');
define('BOOKWELLAB_HTML_PATH', BOOKWELLAB_PATH . '/includes/html/');
define('BOOKWELLAB_LIBS_PATH', BOOKWELLAB_PATH . '/libs/');
define('BOOKWELLAB_URL', plugin_dir_url(BOOKWELLAB_FILE));

require_once BOOKWELLAB_PATH . 'includes/autoload.php';

/**
 * Resolve the plugin .mo file for the active (or given) locale.
 *
 * @param string|null $locale Optional locale; defaults to the filtered plugin locale.
 * @return string|false Absolute path to a readable .mo file, or false if none found.
 */
function bookwellab_get_mofile_path($locale = null)
{
    $domain = 'bookwellnow-appointment-booking';

    if (null === $locale) {
        $locale = function_exists('determine_locale')
            ? determine_locale()
            : (is_admin() && function_exists('get_user_locale') ? get_user_locale() : get_locale());
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress core filter for plugin translations.
        $locale = apply_filters('plugin_locale', $locale, $domain);
    }

    $candidates = [
        WP_LANG_DIR . '/plugins/' . $domain . '-' . $locale . '.mo',
        BOOKWELLAB_PATH . 'languages/' . $domain . '-' . $locale . '.mo',
    ];

    if ('ar' === $locale) {
        // Prefer ar_SA bundle first because some sites use generic `ar` locale.
        $candidates = [
            WP_LANG_DIR . '/plugins/' . $domain . '-ar_SA.mo',
            BOOKWELLAB_PATH . 'languages/' . $domain . '-ar_SA.mo',
            WP_LANG_DIR . '/plugins/' . $domain . '-ar.mo',
            BOOKWELLAB_PATH . 'languages/' . $domain . '-ar.mo',
        ];
    }

    foreach ($candidates as $mofile) {
        if (is_readable($mofile)) {
            return $mofile;
        }
    }

    return false;
}

/**
 * Collect English msgid plus every bundled .mo translation (for locale switching).
 *
 * Uses load_textdomain() per file instead of requiring core pomo/mo.php directly.
 *
 * @param string $msgid Source string.
 * @return string[]
 */
function bookwellab_get_known_translations_for_msgid($msgid)
{
    static $cache = array();

    $msgid = (string) $msgid;
    if ('' === $msgid) {
        return array();
    }

    if (isset($cache[$msgid])) {
        return $cache[$msgid];
    }

    $domain = 'bookwellnow-appointment-booking';
    $known = array($msgid);
    $had_domain = is_textdomain_loaded($domain);

    $languages_dir = BOOKWELLAB_PATH . 'languages/';
    $mofiles = glob($languages_dir . $domain . '-*.mo');
    if (!is_array($mofiles)) {
        $mofiles = array();
    }

    foreach ($mofiles as $mofile) {
        if (!is_readable($mofile)) {
            continue;
        }

        unload_textdomain($domain);
        if (!load_textdomain($domain, $mofile)) {
            continue;
        }

        $translations = get_translations_for_domain($domain);
        if (!$translations) {
            continue;
        }

        $translated = $translations->translate($msgid);
        if (is_string($translated) && '' !== $translated && $translated !== $msgid) {
            $known[] = $translated;
        }
    }

    unload_textdomain($domain);
    if ($had_domain) {
        bookwellab_load_textdomain();
    }

    $cache[$msgid] = array_values(array_unique($known));

    return $cache[$msgid];
}

/**
 * Whether a string is not valid UTF-8 (legacy Latin-1 bytes in DB or .mo files).
 *
 * @param string $str Text to check.
 * @return bool
 */
function bookwellab_string_is_invalid_utf8($str)
{
    return is_string($str) && '' !== $str && !mb_check_encoding($str, 'UTF-8');
}

/**
 * Detect broken accent encoding where "?" replaced letters (e.g. "S?lectionner" vs "Sélectionner").
 *
 * @param string $stored   Value from the database.
 * @param string $expected Properly translated reference string.
 * @return bool
 */
function bookwellab_looks_like_broken_accents($stored, $expected)
{
    if (!is_string($stored) || !is_string($expected) || false === strpos($stored, '?')) {
        return false;
    }

    return (bool) preg_match('/[àâäéèêëïîôùûüçœæÀÂÄÉÈÊËÏÎÔÙÛÜÇŒÆßöüä]/u', $expected);
}

/**
 * Ensure a string (or nested array of strings) is valid UTF-8 for output.
 *
 * Fixes legacy .mo entries stored as ISO-8859-1 that display as "?" in the browser.
 *
 * @param mixed $value Scalar or array.
 * @return mixed
 */
function bookwellab_ensure_utf8($value)
{
    if (is_array($value)) {
        foreach ($value as $key => $item) {
            $value[$key] = bookwellab_ensure_utf8($item);
        }
        return $value;
    }

    if (!is_string($value) || '' === $value) {
        return $value;
    }

    if (mb_check_encoding($value, 'UTF-8')) {
        return $value;
    }

    $converted = mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');
    if (mb_check_encoding($converted, 'UTF-8')) {
        return $converted;
    }

    return $value;
}

/**
 * Load plugin translations via load_textdomain() and a resolved .mo path.
 *
 * Works on all supported WordPress versions (5.3+) and during early hooks such as
 * activation, without calling load_plugin_textdomain() (discouraged since WP 4.6).
 */
function bookwellab_load_textdomain()
{
    $domain = 'bookwellnow-appointment-booking';

    if (is_textdomain_loaded($domain)) {
        return true;
    }

    $mofile = bookwellab_get_mofile_path();
    if ($mofile) {
        return load_textdomain($domain, $mofile);
    }

    return false;
}

/**
 * Whether BookWellNow Pro add-on is active and dependencies are met.
 *
 * @return bool
 */
function bookwellab_is_pro_active()
{
    return defined('BOOKWELLAB_PRO_VERSION')
        && function_exists('bookwellab_pro_dependencies_met')
        && bookwellab_pro_dependencies_met();
}

/**
 * Bootstrap plugin classes after the text domain is loaded.
 */
function bookwellab_bootstrap()
{
    global $BOOKWELLAB, $bookwellab_services, $bookwellab_staffs, $bookwellab_notifications;
    global $bookwellab_appointments, $bookwellab_customers, $bookwellab_calendar, $bookwellab_settings;
    global $bookwellab_dashboard, $bookwellab_frontend, $bookwellab_shortcode_generator, $bookwellab_setup;

    static $bootstrapped = false;
    if ($bootstrapped) {
        return;
    }
    $bootstrapped = true;

    $BOOKWELLAB = \BookWellAB\BOOKWELL::get_instance();
    $bookwellab_services = \BookWellAB\BookWellAB_Services::get_instance();
    $bookwellab_staffs = \BookWellAB\BookWellAB_Staffs::get_instance();
    $bookwellab_notifications = \BookWellAB\BookWellAB_Notifications::get_instance();
    $bookwellab_appointments = \BookWellAB\BookWellAB_Appointments::get_instance();
    $bookwellab_customers = \BookWellAB\BookWellAB_Customers::get_instance();
    $bookwellab_calendar = \BookWellAB\BookWellAB_Calendar::get_instance();
    $bookwellab_settings = \BookWellAB\BookWellAB_Settings::get_instance();
    $bookwellab_dashboard = \BookWellAB\BookWellAB_Dashboard::get_instance();
    $bookwellab_frontend = \BookWellAB\BookWellAB_Frontend::get_instance();
    $bookwellab_paypal = \BookWellAB\BookWellAB_Paypal::get_instance();
    $bookwellab_zoom = \BookWellAB\BookWellAB_Zoom::get_instance();
    $bookwellab_payment_debug_log = \BookWellAB\BookWellAB_Payment_Debug_Log::get_instance();
    $bookwellab_shortcode_generator = \BookWellAB\BookWellAB_Shortcode_Generator::get_instance();
    $bookwellab_form_fields = \BookWellAB\BookWellAB_Form_Fields::get_instance();
    $bookwellab_customer_panel = \BookWellAB\BookWellAB_Customer_Panel::get_instance();
    $bookwellab_setup = \BookWellAB\BookWellAB_Setup::get_instance();
}

/**
 * Ensure the BookWellNow customer role exists on every request.
 *
 * @return void
 */
function bookwellab_ensure_customer_role()
{
    \BookWellAB\BookWellAB_Customer_Account::ensure_customer_role();
}

/**
 * Auto-link a customer record to the WP user on login when emails match.
 *
 * @param string   $user_login Username.
 * @param \WP_User $user       Logged-in user.
 * @return void
 */
function bookwellab_auto_link_customer_on_login($user_login, $user)
{
    \BookWellAB\BookWellAB_Customer_Account::on_wp_login($user_login, $user);
}

/**
 * Redirect BookWellNow customers to the My Booking page after a password reset.
 *
 * @param \WP_User $user WordPress user whose password was reset.
 * @return void
 */
function bookwellab_redirect_after_password_reset($user)
{
    if (wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
        return;
    }

    if (!$user instanceof \WP_User) {
        return;
    }

    $role = \BookWellAB\BookWellAB_Customer_Account::ROLE;
    if (!in_array($role, (array) $user->roles, true)) {
        return;
    }

    $url = \BookWellAB\BookWellAB_Customer_Panel::get_panel_page_url();
    if ($url === '') {
        return;
    }

    wp_safe_redirect($url);
    exit;
}

/**
 * Plugin activation (registered at load time; runs after init in admin).
 */
function bookwellab_activate_plugin()
{
    bookwellab_load_textdomain();

    \BookWellAB\BookWellAB_Customer_Account::ensure_customer_role();

    // Register Zoom settings defaults before seeding the settings table.
    if (class_exists('\\BookWellAB\\BookWellAB_Zoom')) {
        \BookWellAB\BookWellAB_Zoom::get_instance();
    }

    $bwdb = new \BookWellAB\BookWellAB_Database();
    $bwdb->install_tables();
    $bwdb->ensure_customer_panel_support();
    $bwdb->ensure_zoom_and_debug_log_support();
    $bwdb->ensure_week_starts_on_setting();
    $bwdb->ensure_form_fields_table_and_data();
    $bwdb->ensure_form_field_width_column();
    $bwdb->maybe_seed_default_form_field_widths();
    \BookWellAB\BookWellAB_Default_Pages::maybe_create_default_pages();
    \BookWellAB\BookWellAB_Database::update_db_store_option('db_version', BOOKWELLAB_VERSION);

    $role = get_role('administrator');
    if ($role) {
        $capabilities = [
            'bookwellab_manage_services',
            'bookwellab_manage_staff',
            'bookwellab_manage_customers',
            'bookwellab_manage_appointments',
            'bookwellab_manage_settings',
        ];
        foreach ($capabilities as $capability) {
            $role->add_cap($capability);
        }
    }
}

/**
 * Plugin deactivation.
 */
function bookwellab_deactivate_plugin()
{
    wp_clear_scheduled_hook('bookwellab_cleanup_stale_paypal_appointments');

    $role = get_role('administrator');
    if ($role) {
        $capabilities = [
            'bookwellab_manage_services',
            'bookwellab_manage_staff',
            'bookwellab_manage_customers',
            'bookwellab_manage_appointments',
            'bookwellab_manage_settings',
        ];
        foreach ($capabilities as $capability) {
            $role->remove_cap($capability);
        }
    }
}

register_activation_hook(BOOKWELLAB_FILE, 'bookwellab_activate_plugin');
register_deactivation_hook(BOOKWELLAB_FILE, 'bookwellab_deactivate_plugin');

/**
 * Use generic Arabic MO when the site locale is "ar" (not ar_SA, etc.).
 *
 * @param string $locale Current locale.
 * @param string $domain Text domain.
 * @return string
 */
function bookwellab_filter_plugin_locale($locale, $domain)
{
    if ('bookwellnow-appointment-booking' !== $domain || 'ar' !== $locale) {
        return $locale;
    }

    $languages_path = BOOKWELLAB_PATH . 'languages';
    if (file_exists($languages_path . '/bookwellnow-appointment-booking-ar.mo')) {
        return $locale;
    }

    if (file_exists($languages_path . '/bookwellnow-appointment-booking-ar_SA.mo')) {
        return 'ar_SA';
    }

    return $locale;
}
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress core filter for plugin translations.
add_filter('plugin_locale', 'bookwellab_filter_plugin_locale', 10, 2);

add_action('init', 'bookwellab_load_textdomain', 0);
add_action('init', 'bookwellab_ensure_customer_role', 1);
add_action('init', 'bookwellab_bootstrap', 2);
add_action('wp_login', 'bookwellab_auto_link_customer_on_login', 10, 2);
add_action('after_password_reset', 'bookwellab_redirect_after_password_reset', 10, 1);
