<?php
namespace BookWellAB;

defined('ABSPATH') || exit;

if (!class_exists('BookWellAB_Staffs')) {
    class BookWellAB_Staffs
    {
        private static $instance = null;
        private $bookwell_page = 'staff';
        private $bwdb;
        private $bw_wp_nonce;

        public static function get_instance()
        {
            if (self::$instance === null) {
                self::$instance = new self();
            }
            return self::$instance;
        }

        public function __construct()
        {
            global $BOOKWELLAB;
            $this->bwdb = new BookWellAB_Database();
            $current_page = (isset($BOOKWELLAB) && $BOOKWELLAB instanceof BOOKWELL)
                ? $BOOKWELLAB->get_bookwell_admin_page()
                : '';

            if (is_admin() && $current_page == $this->bookwell_page) {
                add_filter('bookwellab_admin_script_vriable_data', [$this, 'bookwellab_admin_script_vriable_data'], 10, 2);
                add_action('bookwellab_staff_admin_html', [$this, 'bookwellab_staff_admin_html_func'], 10);
                add_filter('bookwellab_page_method_data', [$this, 'bookwellab_page_method_data_func'], 10, 2);
                add_filter('bookwellab_page_mounted_data', [$this, 'bookwellab_page_mounted_data_func'], 10, 2);
            }

            add_action('wp_ajax_bookwellab_get_staff', [$this, 'bookwellab_get_staff_func']);
            add_action('wp_ajax_bookwellab_add_staff', [$this, 'bookwellab_add_staff_func']);
            add_action('wp_ajax_bookwellab_delete_staff', [$this, 'bookwellab_delete_staff_func']);
            add_action('wp_ajax_bookwellab_update_staff_orders', [$this, 'bookwellab_update_staff_orders_func']);
            add_action('wp_ajax_bookwellab_search_wp_users', [$this, 'bookwellab_search_wp_users_func']);
            add_action('wp_ajax_bookwellab_get_staff_shifts', [$this, 'bookwellab_get_staff_shifts_func']);
            add_action('wp_ajax_bookwellab_save_staff_shifts', [$this, 'bookwellab_save_staff_shifts_func']);

            add_action('wp_ajax_bookwellab_get_staff_special_days', [$this, 'bookwellab_get_staff_special_days_func']);
            add_action('wp_ajax_bookwellab_save_staff_special_day', [$this, 'bookwellab_save_staff_special_day_func']);
            add_action('wp_ajax_bookwellab_delete_staff_special_day', [$this, 'bookwellab_delete_staff_special_day_func']);
            add_action('wp_ajax_bookwellab_toggle_staff_status', [$this, 'bookwellab_toggle_staff_status_func']);
        }

        public function bookwellab_update_staff_orders_func()
        {
            global $wpdb;

            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Unauthorized', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Do not have permission to access this request.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $verified_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$verified_nonce) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $response = array();
            $response['type'] = 'error';
            $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
            $response['msg'] = esc_html__('Records Not Updated.', 'bookwellnow-appointment-booking');

            // BookWellAB_Input: decoded JSON only; IDs cast to absint before DB update.
            $staff_order = array_map('absint', BookWellAB_Input::get_post_json('staff_order'));

            if (!empty($staff_order) && is_array($staff_order)) {
                $staff_table = $this->bwdb->get_table_name('staffs');
                $position = 1;
                foreach ($staff_order as $staff_id) {
                    $args = array('bw_staff_position_order' => $position);
                    $this->bwdb->upsert($staff_table, $args, array('bw_staff_id' => $staff_id));
                    $position++;
                }

                \BookWellAB\BookWellAB_Frontend::clear_front_booking_cache();

                $response['type'] = 'success';
                $response['title'] = esc_html__('Success', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Staff order successfully updated.', 'bookwellnow-appointment-booking');
            }

            wp_send_json($response);
            exit;
        }



        function bookwellab_page_mounted_data_func($bookwellab_page_mounted_data, $bookwellpage)
        {
            if ($this->bookwell_page != $bookwellpage) {
                return $bookwellab_page_mounted_data;
            }

            $bookwellab_page_mounted_data .= '
                const vuedata = this;
                vuedata.bwab_getStaff();
                vuedata.fetchServices();
            ';

            return $bookwellab_page_mounted_data;
        }

        public function bookwellab_delete_staff_func()
        {
            // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            global $wpdb;
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Unauthorized', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Do not have permission to access this request.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $verified_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$verified_nonce) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $response = array();
            $response['type'] = 'error';
            $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
            $response['msg'] = esc_html__('Record Not Deleted.', 'bookwellnow-appointment-booking');

            $id = isset($_POST['id']) ? intval(wp_unslash($_POST['id'])) : 0;
            if ($id == 0) {
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Invalid record id', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $staff_table = $this->bwdb->get_table_name('staffs');
            $appointments_table = esc_sql($this->bwdb->get_table_name('appointments'));

            // Check if staff has any related appointments
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Dynamic table name is plugin-controlled and escaped.
            $appointment_count = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $appointments_table WHERE bw_staff_id = %d", $id));

            if ($appointment_count > 0) {
                // Deactivate the staff member instead of deleting
                $wpdb->update($staff_table, ['bw_staff_is_active' => 0], ['bw_staff_id' => $id]);
                
                $response['type'] = 'success';
                $response['title'] = esc_html__('Success', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('This staff member cannot be deleted because it is associated with existing appointments. It has been deactivated instead.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                exit;
            }

            // No appointments found, proceed with deletion
            $wpdb->delete($staff_table, array('bw_staff_id' => $id));

            // Delete staff-service relationships
            $staff_services_table = $this->bwdb->get_table_name('staff_services');
            $wpdb->delete($staff_services_table, array('bw_staff_id' => $id));

            $response['type'] = 'success';
            $response['title'] = esc_html__('Success', 'bookwellnow-appointment-booking');
            $response['msg'] = esc_html__('Staff successfully deleted.', 'bookwellnow-appointment-booking');
            wp_send_json($response);
            // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            exit;
        }

        public function bookwellab_add_staff_func()
        {
            global $wpdb;
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Unauthorized', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Do not have permission to access this request.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $verified_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$verified_nonce) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $response = array();
            $response['type'] = 'error';
            $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
            $response['msg'] = esc_html__('Data Not Found.', 'bookwellnow-appointment-booking');

            // BookWellAB_Input: decoded JSON only; each bw_* field sanitized below before DB upsert.
            $staff_data = BookWellAB_Input::get_post_json('staff_data');

            $bw_staff_name = isset($staff_data['bw_staff_name']) ? sanitize_text_field($staff_data['bw_staff_name']) : '';
            $bw_staff_email = isset($staff_data['bw_staff_email']) ? sanitize_email($staff_data['bw_staff_email']) : '';
            $bw_staff_phone = isset($staff_data['bw_staff_phone']) ? sanitize_text_field($staff_data['bw_staff_phone']) : '';
            $bw_staff_position = isset($staff_data['bw_staff_position']) ? sanitize_text_field($staff_data['bw_staff_position']) : '';
            $bw_staff_bio = isset($staff_data['bw_staff_bio']) ? sanitize_textarea_field($staff_data['bw_staff_bio']) : '';
            $bw_staff_image = isset($staff_data['bw_staff_image']) ? esc_url_raw($staff_data['bw_staff_image']) : '';
            $bw_wp_user_id = isset($staff_data['bw_wp_user_id']) ? intval($staff_data['bw_wp_user_id']) : 0;
            $bw_staff_is_active = isset($staff_data['bw_staff_is_active']) ? intval($staff_data['bw_staff_is_active']) : 1;
            $bw_staff_services_raw = isset($staff_data['bw_staff_services']) ? $staff_data['bw_staff_services'] : array();
            $bw_staff_services = $this->sanitize_staff_services($bw_staff_services_raw);
            $bw_staff_id = isset($staff_data['bw_staff_id']) ? intval($staff_data['bw_staff_id']) : 0;
            $bw_zoom_enabled = !empty($staff_data['bw_zoom_enabled']) ? 1 : 0;
            $bw_zoom_user_id = isset($staff_data['bw_zoom_user_id']) ? sanitize_text_field((string) $staff_data['bw_zoom_user_id']) : '';
            if ($bw_zoom_enabled !== 1) {
                $bw_zoom_user_id = '';
            }

            $args = array(
                'bw_staff_name' => $bw_staff_name,
                'bw_staff_email' => $bw_staff_email,
                'bw_staff_phone' => $bw_staff_phone,
                'bw_staff_position' => $bw_staff_position,
                'bw_staff_bio' => $bw_staff_bio,
                'bw_staff_image' => $bw_staff_image,
                'bw_wp_user_id' => $bw_wp_user_id,
                'bw_staff_is_active' => $bw_staff_is_active,
                'bw_zoom_enabled' => $bw_zoom_enabled,
                'bw_zoom_user_id' => $bw_zoom_user_id,
                'bw_staff_update_at' => current_time('mysql')
            );

            /**
             * Filter staff DB columns before insert/update.
             *
             * @param array $args       Column => value map.
             * @param array $staff_data Raw sanitized staff payload.
             */
            $args = apply_filters('bookwellab_staff_save_args', $args, is_array($staff_data) ? $staff_data : array());
            if (!is_array($args)) {
                $args = array();
            }

            $last_insert_id = '';
            $staff_table = $this->bwdb->get_table_name('staffs');

            if ($bw_staff_id == 0) {
                $bw_staff_position_order = intval($this->bwdb->select('var', 'bw_staff_position_order', "{$staff_table}", '', array(), 'bw_staff_position_order DESC'));
                $bw_staff_position_order = $bw_staff_position_order + 1;
                $args['bw_staff_position_order'] = $bw_staff_position_order;
                $args['bw_staff_create_at'] = current_time('mysql');
                $last_insert_id = $this->bwdb->upsert($staff_table, $args);

                // Add Default Shifts (Mon-Fri 09:00-17:00)
                $days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
                $hours_table = $this->bwdb->get_table_name('staff_working_hours');

                foreach ($days as $day) {
                    $is_off = ($day == 'Saturday' || $day == 'Sunday') ? 1 : 0;
                    $shift_data = [
                        'bw_staff_id' => $last_insert_id,
                        'bw_day_name' => $day,
                        'bw_start_time' => '09:00',
                        'bw_end_time' => '17:00',
                        'bw_is_off' => $is_off,
                        'bw_breaks' => '[]'
                    ];
                    $this->bwdb->upsert($hours_table, $shift_data);
                }

                $msg = esc_html__('Staff has been added successfully.', 'bookwellnow-appointment-booking');
            } else {
                $this->bwdb->upsert($staff_table, $args, array('bw_staff_id' => $bw_staff_id));
                $last_insert_id = $bw_staff_id;
                $msg = esc_html__('Staff has been updated successfully.', 'bookwellnow-appointment-booking');
            }

            // Update staff-service relationships
            if ($last_insert_id) {
                $this->bwdb->update_staff_services($last_insert_id, $bw_staff_services);
            }

            $response['type'] = 'success';
            $response['title'] = esc_html__('Success', 'bookwellnow-appointment-booking');
            $response['msg'] = $msg;

            \BookWellAB\BookWellAB_Frontend::clear_front_booking_cache();

            wp_send_json($response);
            exit;
        }

        public function bookwellab_get_staff_func()
        {
            global $wpdb;
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Unauthorized', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Do not have permission to access this request.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $verified_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$verified_nonce) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $response = array();
            $response['type'] = 'error';
            $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
            $response['msg'] = esc_html__('Data Not Found.', 'bookwellnow-appointment-booking');

            $staff_table = $this->bwdb->get_table_name('staffs');
            $where = '';
            $where_args = array();

            // BookWellAB_Input: scalars sanitized via map_deep; filter keys sanitized again below before query.
            $filter_raw = BookWellAB_Input::get_post_array('filter');
            $filter = array();
            if (!empty($filter_raw)) {
                $filter = array(
                    'bw_staff_name' => isset($filter_raw['bw_staff_name']) ? sanitize_text_field($filter_raw['bw_staff_name']) : '',
                    'bw_staff_is_active' => isset($filter_raw['bw_staff_is_active']) ? sanitize_text_field((string) $filter_raw['bw_staff_is_active']) : '',
                );
            }

            if (!empty($filter['bw_staff_name'])) {
                $search_staff_name = $filter['bw_staff_name'];
                $where .= " staff.bw_staff_name LIKE %s ";
                $where_args[] = "%{$search_staff_name}%";
            }

            if (isset($filter['bw_staff_is_active']) && $filter['bw_staff_is_active'] !== '') {
                $bw_staff_is_active = intval($filter['bw_staff_is_active']);
                if (empty($where)) {
                    $where = 'staff.bw_staff_is_active = %d';
                } else {
                    $where .= " AND staff.bw_staff_is_active = %d ";
                }
                $where_args[] = $bw_staff_is_active;
            }

            $staff_res = $this->bwdb->select('results', 'staff.*', "{$staff_table} staff", $where, $where_args, 'bw_staff_position_order ASC');


            $staff_list = array();
            if (!empty($staff_res)) {
                foreach ($staff_res as $staff_row) {
                    $staff_row_single = (array) $staff_row;
                    $staff_row_single['bw_staff_name'] = BookWellAB_Input::decode_entities_for_display($staff_row['bw_staff_name'] ?? '');
                    $staff_row_single['bw_staff_email'] = BookWellAB_Input::decode_entities_for_display($staff_row['bw_staff_email'] ?? '');
                    $staff_row_single['bw_staff_phone'] = BookWellAB_Input::decode_entities_for_display($staff_row['bw_staff_phone'] ?? '');
                    $staff_row_single['bw_staff_position'] = BookWellAB_Input::decode_entities_for_display($staff_row['bw_staff_position'] ?? '');
                    $staff_row_single['bw_staff_is_active'] = (int) ($staff_row['bw_staff_is_active'] ?? 1);
                    $staff_row_single['bw_zoom_enabled'] = (int) ($staff_row['bw_zoom_enabled'] ?? 0);
                    $staff_row_single['bw_zoom_user_id'] = (string) ($staff_row['bw_zoom_user_id'] ?? '');
                    $staff_row_single['delete_loader'] = '0';
                    $staff_row_single['status_loader'] = false;

                    // Get assigned services
                    $staff_row_single['bw_staff_services'] = $this->bwdb->get_staff_services($staff_row['bw_staff_id']);
                    if (!empty($staff_row_single['bw_staff_services']) && is_array($staff_row_single['bw_staff_services'])) {
                        foreach ($staff_row_single['bw_staff_services'] as $service_index => $assigned_service) {
                            if (isset($assigned_service['bw_service_name'])) {
                                $staff_row_single['bw_staff_services'][$service_index]['bw_service_name'] = BookWellAB_Input::decode_entities_for_display(
                                    $assigned_service['bw_service_name']
                                );
                            }
                        }
                    }

                    if (isset($staff_row_single['bw_staff_bio'])) {
                        $staff_row_single['bw_staff_bio'] = BookWellAB_Input::decode_entities_for_display($staff_row_single['bw_staff_bio']);
                    }

                    if (empty($staff_row_single['bw_staff_image'])) {
                        $staff_row_single['bw_staff_image'] = BookWellAB_Frontend::get_default_staff_image_url();
                    }

                    /**
                     * Filter a staff list/edit row before it is returned to admin Vue.
                     *
                     * @param array $staff_row_single Normalized staff row.
                     * @param object|array $staff_row Raw DB row.
                     */
                    $staff_row_single = apply_filters('bookwellab_staff_row_data', $staff_row_single, $staff_row);
                    if (!is_array($staff_row_single)) {
                        continue;
                    }

                    $staff_list[] = $staff_row_single;
                }
            }

            $response['type'] = 'success';
            $response['list_data'] = $staff_list;
            wp_send_json($response);
            exit;
        }

        public function bookwellab_search_wp_users_func()
        {
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Unauthorized', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Do not have permission to access this request.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $verified_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$verified_nonce) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking');
                wp_send_json($response);
                die;
            }

            $search = isset($_POST['search']) ? sanitize_text_field(wp_unslash($_POST['search'])) : '';
            $user_id = isset($_POST['user_id']) ? absint(wp_unslash($_POST['user_id'])) : 0;
            $users = array();

            if ($user_id > 0) {
                $user = get_userdata($user_id);
                if ($user instanceof \WP_User) {
                    $users[] = array(
                        'id' => (int) $user->ID,
                        'username' => sanitize_text_field($user->user_login),
                        'name' => sanitize_text_field($user->display_name),
                        'email' => sanitize_email($user->user_email),
                    );
                }
                wp_send_json(['success' => true, 'data' => $users]);
            }

            if (!empty($search)) {
                $args = array(
                    'search' => "*{$search}*",
                    'search_columns' => array('user_login', 'user_email', 'display_name'),
                    'number' => 10
                );

                $user_query = new \WP_User_Query($args);
                foreach ($user_query->get_results() as $user) {
                    $users[] = array(
                        'id' => (int) $user->ID,
                        'username' => sanitize_text_field($user->user_login),
                        'name' => sanitize_text_field($user->display_name),
                        'email' => sanitize_email($user->user_email),
                    );
                }
            }

            wp_send_json(['success' => true, 'data' => $users]);
        }

        public function bookwellab_get_staff_shifts_func()
        {
            global $wpdb;
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $staff_id = isset($_POST['staff_id']) ? intval(wp_unslash($_POST['staff_id'])) : 0;
            if ($staff_id <= 0) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Invalid Staff ID.', 'bookwellnow-appointment-booking')]);
            }

            $table_name = $this->bwdb->get_table_name('staff_working_hours');
            $shifts = $this->bwdb->select('results', '*', $table_name, 'bw_staff_id = %d', [$staff_id], 'bw_hour_id ASC');

            // Default structure if empty
            if (empty($shifts)) {
                $days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
                $shifts = [];
                foreach ($days as $day) {
                    $shifts[] = [
                        'bw_day_name' => $day,
                        'bw_start_time' => '09:00',
                        'bw_end_time' => '17:00',
                        'bw_is_off' => ($day == 'Saturday' || $day == 'Sunday') ? '1' : '0',
                        'bw_breaks' => json_encode([])
                    ];
                }
            } else {
                foreach ($shifts as &$shift) {
                    if (!empty($shift['bw_start_time'])) {
                        $shift['bw_start_time'] = $this->normalize_shift_time_hhmm($shift['bw_start_time']);
                    }
                    if (!empty($shift['bw_end_time'])) {
                        $shift['bw_end_time'] = $this->normalize_shift_time_hhmm($shift['bw_end_time']);
                    }

                    if (empty($shift['bw_breaks'])) {
                        $shift['bw_breaks'] = '[]';
                    } else {
                        $shift['bw_breaks'] = wp_json_encode(
                            $this->normalize_shift_breaks_for_response($shift['bw_breaks'])
                        );
                    }
                }
                unset($shift);
            }

            wp_send_json(['success' => true, 'data' => ['shifts' => $shifts]]);
        }

        public function bookwellab_save_staff_shifts_func()
        {
            global $wpdb;
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                wp_send_json_error(['msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_send_json_error(['msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $staff_id = isset($_POST['staff_id']) ? intval(wp_unslash($_POST['staff_id'])) : 0;
            // BookWellAB_Input: decoded JSON only; each shift field sanitized in loop below before DB upsert.
            $shifts_data = BookWellAB_Input::get_post_json('shifts_data');

            if ($staff_id <= 0 || empty($shifts_data)) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Invalid data.', 'bookwellnow-appointment-booking')]);
            }

            $table_name = $this->bwdb->get_table_name('staff_working_hours');

            foreach ($shifts_data as $shift) {
                $normalized_day_name = isset($shift['bw_day_name']) ? strtolower(trim(sanitize_text_field($shift['bw_day_name']))) : '';
                $normalized_day_name = ucfirst($normalized_day_name);
                if (empty($normalized_day_name)) {
                    continue;
                }

                // Check if exists
                $existing = $this->bwdb->select('row', 'bw_hour_id', $table_name, 'bw_staff_id = %d AND LOWER(bw_day_name) = %s', [$staff_id, strtolower($normalized_day_name)]);

                $start_time = $this->normalize_shift_time_hhmm(
                    isset($shift['bw_start_time']) ? sanitize_text_field($shift['bw_start_time']) : '09:00'
                );
                $end_time = $this->normalize_shift_time_hhmm(
                    isset($shift['bw_end_time']) ? sanitize_text_field($shift['bw_end_time']) : '17:00'
                );
                $is_off = isset($shift['bw_is_off']) ? intval($shift['bw_is_off']) : 0;

                if ($is_off === 0) {
                    $compare_start = $start_time !== '' ? $start_time : '09:00';
                    $compare_end = $end_time !== '' ? $end_time : '17:00';
                    if (strcmp($compare_start, $compare_end) >= 0) {
                        wp_send_json_error([
                            'msg' => esc_html__('Shift end time must be later than start time.', 'bookwellnow-appointment-booking'),
                        ]);
                    }
                }

                $data = [
                    'bw_staff_id' => $staff_id,
                    'bw_day_name' => $normalized_day_name,
                    'bw_start_time' => $start_time !== '' ? $start_time : '09:00',
                    'bw_end_time' => $end_time !== '' ? $end_time : '17:00',
                    'bw_is_off' => $is_off,
                    'bw_breaks' => $this->sanitize_bw_breaks_for_storage(isset($shift['bw_breaks']) ? $shift['bw_breaks'] : array())
                ];

                if ($existing) {
                    $this->bwdb->upsert($table_name, $data, ['bw_hour_id' => $existing['bw_hour_id']]);
                } else {
                    $this->bwdb->upsert($table_name, $data);
                }
            }

            \BookWellAB\BookWellAB_Frontend::clear_front_booking_cache();

            wp_send_json(['success' => true, 'data' => ['msg' => esc_html__('Shifts saved successfully.', 'bookwellnow-appointment-booking')]]);
        }

        public function bookwellab_get_staff_special_days_func()
        {
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $staff_id = isset($_POST['staff_id']) ? intval(wp_unslash($_POST['staff_id'])) : 0;
            if ($staff_id <= 0) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Invalid Staff ID.', 'bookwellnow-appointment-booking')]);
            }

            $table_name = $this->bwdb->get_table_name('staff_special_days');
            $special_days = $this->bwdb->select('results', '*', $table_name, 'bw_staff_id = %d', [$staff_id], 'bw_start_date ASC');

            if (empty($special_days)) {
                $special_days = [];
            } else {
                foreach ($special_days as &$sd) {
                    if (!empty($sd['bw_start_time'])) {
                        $sd['bw_start_time'] = $this->normalize_shift_time_hhmm($sd['bw_start_time']);
                    }
                    if (!empty($sd['bw_end_time'])) {
                        $sd['bw_end_time'] = $this->normalize_shift_time_hhmm($sd['bw_end_time']);
                    }
                    if (empty($sd['bw_breaks'])) {
                        $sd['bw_breaks'] = '[]';
                    } else {
                        $sd['bw_breaks'] = wp_json_encode(
                            $this->normalize_shift_breaks_for_response($sd['bw_breaks'])
                        );
                    }
                }
                unset($sd);
            }

            wp_send_json(['success' => true, 'data' => ['special_days' => $special_days]]);
        }

        public function bookwellab_save_staff_special_day_func()
        {
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                wp_send_json_error(['msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_send_json_error(['msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            // BookWellAB_Input: decoded JSON only; each field sanitized below before DB upsert.
            $special_day_data = BookWellAB_Input::get_post_json('special_day_data');

            if (empty($special_day_data) || !isset($special_day_data['bw_staff_id']) || intval($special_day_data['bw_staff_id']) <= 0) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Invalid data.', 'bookwellnow-appointment-booking')]);
            }

            $table_name = $this->bwdb->get_table_name('staff_special_days');

            $start_time = $this->normalize_shift_time_hhmm(
                sanitize_text_field($special_day_data['bw_start_time'] ?? '')
            );
            $end_time = $this->normalize_shift_time_hhmm(
                sanitize_text_field($special_day_data['bw_end_time'] ?? '')
            );

            $data = [
                'bw_staff_id' => intval($special_day_data['bw_staff_id']),
                'bw_start_date' => sanitize_text_field($special_day_data['bw_start_date']),
                'bw_end_date' => sanitize_text_field($special_day_data['bw_end_date']),
                'bw_start_time' => $start_time !== '' ? $start_time : '09:00',
                'bw_end_time' => $end_time !== '' ? $end_time : '17:00',
                'bw_is_off' => intval($special_day_data['bw_is_off']),
                'bw_breaks' => $this->sanitize_bw_breaks_for_storage(isset($special_day_data['bw_breaks']) ? $special_day_data['bw_breaks'] : array())
            ];

            if (!empty($special_day_data['bw_special_day_id'])) {
                $this->bwdb->upsert($table_name, $data, ['bw_special_day_id' => intval($special_day_data['bw_special_day_id'])]);
                $msg = esc_html__('Special Day updated successfully.', 'bookwellnow-appointment-booking');
            } else {
                $this->bwdb->upsert($table_name, $data);
                $msg = esc_html__('Special Day added successfully.', 'bookwellnow-appointment-booking');
            }

            wp_send_json(['success' => true, 'data' => ['msg' => $msg]]);
        }

        public function bookwellab_delete_staff_special_day_func()
        {
            // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            global $wpdb;
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $id = isset($_POST['id']) ? intval(wp_unslash($_POST['id'])) : 0;
            if ($id <= 0) {
                wp_send_json(['type' => 'error', 'msg' => esc_html__('Invalid ID.', 'bookwellnow-appointment-booking')]);
            }

            $table_name = $this->bwdb->get_table_name('staff_special_days');
            $wpdb->delete($table_name, ['bw_special_day_id' => $id]);

            wp_send_json(['success' => true, 'data' => ['msg' => esc_html__('Special Day deleted successfully.', 'bookwellnow-appointment-booking')]]);
            // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        }

        public function bookwellab_toggle_staff_status_func()
        {
            // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            global $wpdb;

            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_staff')) {
                $response = array(
                    'type' => 'error',
                    'title' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking'),
                    'msg' => esc_html__('Do not have permission to access this request.', 'bookwellnow-appointment-booking')
                );
                wp_send_json($response);
                die;
            }

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $check_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$check_nonce) {
                $response = array(
                    'type' => 'error',
                    'title' => esc_html__('Error', 'bookwellnow-appointment-booking'),
                    'msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')
                );
                wp_send_json($response);
                die;
            }

            $staff_id = isset($_POST['bw_staff_id']) ? intval(wp_unslash($_POST['bw_staff_id'])) : 0;
            $staff_status = isset($_POST['bw_staff_is_active']) ? intval(wp_unslash($_POST['bw_staff_is_active'])) : 0;

            if ($staff_id <= 0) {
                $response = array(
                    'type' => 'error',
                    'title' => esc_html__('Error', 'bookwellnow-appointment-booking'),
                    'msg' => esc_html__('Invalid staff ID.', 'bookwellnow-appointment-booking')
                );
                wp_send_json($response);
                die;
            }

            $staff_table = $this->bwdb->get_table_name('staffs');
            $updated = $wpdb->update(
                $staff_table,
                ['bw_staff_is_active' => $staff_status],
                ['bw_staff_id' => $staff_id]
            );

            if ($updated === false) {
                $response = array(
                    'type' => 'error',
                    'title' => esc_html__('Error', 'bookwellnow-appointment-booking'),
                    'msg' => esc_html__('Failed to update staff status.', 'bookwellnow-appointment-booking')
                );
                wp_send_json($response);
                die;
            }

            $response = array(
                'type' => 'success',
                'title' => esc_html__('Success', 'bookwellnow-appointment-booking'),
                'msg' => esc_html__('Staff status updated successfully.', 'bookwellnow-appointment-booking')
            );
            wp_send_json($response);
            // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            exit;
        }

        public function bookwellab_page_method_data_func($bookwellab_page_method_data, $bookwellpage)
        {
            if ($this->bookwell_page != $bookwellpage) {
                return $bookwellab_page_method_data;
            }

            $this->bw_wp_nonce = esc_html(wp_create_nonce('bw_wp_nonce'));

            $bookwellab_page_method_data .= '
                bwab_update_staff_order(oldIndex, newIndex) {
                    const vuedata = this;
                    const item = vuedata.list_data.splice(oldIndex, 1)[0];
                    vuedata.list_data.splice(newIndex, 0, item);
                    
                    var staff_order = [];
                    vuedata.list_data.forEach(function(item) {
                        staff_order.push(item.bw_staff_id);
                    });
                    
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var pass_data = {};
                    pass_data.staff_order = JSON.stringify(staff_order);
                    pass_data.action = "bookwellab_update_staff_orders";
                    pass_data._wpnonce = bookwellab_ajax_obj.bookwellab_nonce;
                    
                    axios.post(ajax_url, Qs.stringify(pass_data))
                    .then(function(response) {
                        vuedata.$notify({
                            title: response.data.title,
                            message: response.data.msg,
                            type: response.data.type,
                            customClass: response.data.type+"_notification_data",
                            duration: 2000,
                        });
                        if(response.data.type == "success"){
                            vuedata.bwab_getStaff(false);
                        }
                    });
                },
                
                bwab_uploadStaffImage() {
                    const vuedata = this;
                    var image_frame;
                    if(image_frame){
                        image_frame.open();
                    }
                    image_frame = wp.media({
                        title: \'Select Media\',
                        multiple : false,
                        library : {
                            type : \'image\',
                        }
                    });
                    image_frame.on(\'close\',function() {
                        var selection =  image_frame.state().get(\'selection\');
                        var gallery_ids = new Array();
                        var my_index = 0;
                        selection.each(function(attachment) {
                            vuedata.staff_form.bw_staff_image = attachment[\'attributes\'][\'url\'];
                        });
                    });
                    image_frame.on(\'open\',function() {
                        var selection =  image_frame.state().get(\'selection\');
                        var ids = vuedata.staff_form.bw_staff_image;
                        if(ids){
                            var attachment = wp.media.attachment(ids);
                            attachment.fetch();
                            selection.add( attachment ? [ attachment ] : [] );
                        }
                    });
                    image_frame.open();
                },

                bwab_removeStaffImage() {
                    this.staff_form.bw_staff_image = \'\';
                },

                bwab_resetFilter() {
                    const vuedata = this;
                    vuedata.filter = JSON.parse(JSON.stringify(vuedata.filter_reset));
                    vuedata.bwab_getStaff();
                },
                
                bwab_editRow(row) {
                    const vuedata = this;
                    vuedata.staff_form = JSON.parse(JSON.stringify(row));
                    vuedata.staff_form.bw_wp_user_id = parseInt(vuedata.staff_form.bw_wp_user_id, 10) || 0;
                    vuedata.staff_form.bw_zoom_enabled = parseInt(vuedata.staff_form.bw_zoom_enabled, 10) || 0;
                    vuedata.staff_form.bw_zoom_user_id = vuedata.staff_form.bw_zoom_user_id || "";
                    if (typeof vuedata.bwab_proPrepareStaffGoogleCalendar === "function") {
                        vuedata.bwab_proPrepareStaffGoogleCalendar(vuedata.staff_form);
                    }
                    
                    // Convert services array to format needed for select component
                    vuedata.staff_form.bw_staff_services = row.bw_staff_services.map(service => service.bw_service_id);
                    
                    vuedata.bw_add_edit_popup = true;
                    
                    // Preload linked WP user so select shows username instead of raw ID.
                    if (parseInt(row.bw_wp_user_id, 10) > 0) {
                        vuedata.fetchWPUsers("", parseInt(row.bw_wp_user_id, 10));
                    }
                    if (parseInt(vuedata.staff_form.bw_zoom_enabled, 10) === 1 && typeof vuedata.bwab_loadZoomUsers === "function") {
                        vuedata.bwab_loadZoomUsers();
                    }
                    if (typeof vuedata.bwab_proOnStaffGoogleCalendarOpen === "function") {
                        vuedata.bwab_proOnStaffGoogleCalendarOpen();
                    }
                },
                
                bwab_deleteRow(row, index) {
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    vuedata.list_data[index].delete_loader = "1";
                    
                    var pass_data = {};
                    pass_data.id = row.bw_staff_id;
                    pass_data.action = "bookwellab_delete_staff";
                    pass_data._wpnonce = bookwellab_ajax_obj.bookwellab_nonce;
                    
                    axios.post(ajax_url, Qs.stringify(pass_data))
                    .then(function(response) {
                        vuedata.$notify({
                            title: response.data.title,
                            message: response.data.msg,
                            type: response.data.type,
                            customClass: response.data.type+"_notification_data",
                            duration: 2000,
                        });
                        
                        let remove_ind = row.index;
                        vuedata.list_data[index].delete_loader = "0";
                        
                        if(response.data.type == "success") {
                            vuedata.list_data.splice(remove_ind, 1);
                            vuedata.bwab_getStaff();
                        }
                    }).catch(function(error) {
                        vuedata.delete_loader = false;
                        vuedata.bwab_error_handler();
                    });
                },
                
                bwab_closeDrawer() {
                    const vuedata = this;
                    vuedata.$refs["bw_staff_frm"].resetFields();
                    vuedata.staff_form = JSON.parse(JSON.stringify(vuedata.staff_form_reset));
                    vuedata.bw_add_edit_popup = false;
                    vuedata.insert_loader = false;
                },
                
                bwab_error_handler() {
                    const vuedata = this;
                    vuedata.$notify({
                        title: "Error",
                        message: "An error occurred while fetching data",
                        type: "error",
                        customClass: "error_notification",
                        duration: 2200,
                    });
                },
                
                bwab_storeStaffData() {
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    const vuedata = this;
                    
                    vuedata.$refs["bw_staff_frm"].validate((valid) => {
                        if(valid) {
                            vuedata.insert_loader = "1";
                            var pass_data = {};
                            
                            // Prepare services data
                            let services_data = [];
                            if(vuedata.staff_form.bw_staff_services && vuedata.staff_form.bw_staff_services.length > 0) {
                                services_data = vuedata.services_list.filter(service => 
                                    vuedata.staff_form.bw_staff_services.includes(service.bw_service_id)
                                ).map(service => ({
                                    id: service.bw_service_id,
                                    price: service.bw_service_price,
                                    duration_val: service.bw_service_duration_val,
                                    duration_unit: service.bw_service_duration_unit
                                }));
                            }
                            
                            vuedata.staff_form.bw_staff_services = services_data;
                            pass_data.staff_data = JSON.stringify(vuedata.staff_form);
                            pass_data.action = "bookwellab_add_staff";
                            pass_data._wpnonce = bookwellab_ajax_obj.bookwellab_nonce;
                            
                            axios.post(ajax_url, Qs.stringify(pass_data))
                            .then(function(response) {
                                if(response.data.type != "error") {
                                    vuedata.bwab_closeDrawer();
                                }
                                
                                vuedata.insert_loader = "0";
                                vuedata.$notify({
                                    title: response.data.title,
                                    message: response.data.msg,
                                    type: response.data.type,
                                    customClass: response.data.type+"_notification",
                                    duration: 2200,
                                });
                                
                                if(response.data.type == "success") {
                                    vuedata.bwab_getStaff();
                                }
                            }).catch(function(error) {
                                console.log(error);
                                vuedata.insert_loader = "0";
                                vuedata.bwab_error_handler();
                            });
                        }
                    });
                },
                
                async bwab_getStaff(show_loader = true) {
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    const vuedata = this;
                    
                    if(show_loader) {
                        vuedata.bw_list_loader = "1";
                    }
                    
                    var postData = {
                        action: "bookwellab_get_staff",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        filter: vuedata.filter
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        vuedata.bw_list_loader = "0";
                        vuedata.list_data = [];
                        setTimeout(function(){
                        vuedata.list_data = response.data.list_data;
                        }, 50);
                        setTimeout(function(){
                            vuedata.bwab_initSortable();
                        }, 200);
                    }).catch(function(error) {
                        console.log(error);
                        vuedata.bwab_error_handler();
                    });
                },
                
                fetchWPUsers(search = "", userId = 0) {
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    
                    var postData = {
                        action: "bookwellab_search_wp_users",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        search: search,
                        user_id: userId || 0
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        vuedata.wp_users = response.data.data || [];
                    }).catch(function(error) {
                        console.log(error);
                    });
                },
                
                handleUserSelect(user_id) {
                    const vuedata = this;
                    if(user_id > 0) {
                        const selectedUser = vuedata.wp_users.find(user => user.id == user_id);
                        if(selectedUser) {
                            vuedata.staff_form.bw_staff_name = selectedUser.name;
                            vuedata.staff_form.bw_staff_email = selectedUser.email;
                        }
                    }
                },
                
                fetchServices() {
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    
                    var postData = {
                        action: "bookwellab_get_services",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        vuedata.services_list = response.data.list_data || [];
                    }).catch(function(error) {
                        console.log(error);
                    });
                },

                bwab_initSortable(){
                    const vuedata = this;
                    const isMobile = window.innerWidth <= 782;
                    const mobileList = document.querySelector(".bw-staff-mobile-list");
                    const el = document.querySelector(".bw-staff-table .el-table__body-wrapper tbody");

                    if(vuedata.bw_staff_sortable){
                        vuedata.bw_staff_sortable.destroy();
                        vuedata.bw_staff_sortable = null;
                    }

                    let sortableEl = null;
                    let draggable = "tr.el-table__row";

                    if(isMobile && mobileList){
                        sortableEl = mobileList;
                        draggable = ".bw-staff-mobile-card";
                    } else if(el){
                        sortableEl = el;
                    }

                    if(!sortableEl){
                        return;
                    }

                    vuedata.bw_staff_sortable = Sortable.create(sortableEl, {
                        handle: ".bw-staff-drag-handle",
                        animation: 150,
                        draggable: draggable,
                        onEnd: function (evt) {
                            vuedata.bwab_update_staff_order(evt.oldIndex, evt.newIndex);
                        }
                    });
                },

                bwab_openShifts(row) {
                    const vuedata = this;
                    vuedata.shifts_drawer = true;
                    vuedata.shifts_loader = true;
                    vuedata.current_staff_id_shifts = row.bw_staff_id;
                    
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var postData = {
                        action: "bookwellab_get_staff_shifts",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        staff_id: row.bw_staff_id
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        vuedata.shifts_loader = false;
                        if(response.data.success) {
                            vuedata.current_staff_shifts = response.data.data.shifts.map(shift => {
                                let breaks = [];
                                // Ensure breaks is array
                                if (typeof shift.bw_breaks === \'string\') {
                                    try {
                                        breaks = JSON.parse(shift.bw_breaks);
                                    } catch(e) { breaks = []; }
                                } else if (Array.isArray(shift.bw_breaks)) {
                                    breaks = shift.bw_breaks;
                                }
                                
                                // Return a fresh object to ensure reactivity
                                return {
                                    bw_hour_id: shift.bw_hour_id,
                                    bw_staff_id: shift.bw_staff_id,
                                    bw_day_name: shift.bw_day_name,
                                    bw_start_time: shift.bw_start_time || \'09:00\',
                                    bw_end_time: shift.bw_end_time || \'17:00\',
                                    bw_is_off: String(shift.bw_is_off || \'0\'),
                                    bw_breaks: breaks
                                };
                            });
                        } else {
                            vuedata.$notify({ type: \'error\', message: response.data.data.msg });
                        }
                    }).catch(function(error) {
                        vuedata.shifts_loader = false;
                        console.log(error);
                    });
                },

                bwab_saveShifts() {
                    const vuedata = this;
                    const hasInvalidShift = vuedata.current_staff_shifts.some((shift) => {
                        if (String(shift.bw_is_off) === \'1\') {
                            return false;
                        }
                        return String(shift.bw_start_time || \'\') >= String(shift.bw_end_time || \'\');
                    });

                    if (hasInvalidShift) {
                        vuedata.$notify({ type: \'error\', message: \'' . esc_js(__('Shift end time must be later than start time.', 'bookwellnow-appointment-booking')) . '\' });
                        return;
                    }

                    const hasInvalidBreak = vuedata.current_staff_shifts.some((shift) => {
                        if (!Array.isArray(shift.bw_breaks)) {
                            return false;
                        }
                        return shift.bw_breaks.some((breakItem) => {
                            if (!breakItem) {
                                return false;
                            }
                            return String(breakItem.start || \'\') >= String(breakItem.end || \'\');
                        });
                    });

                    if (hasInvalidBreak) {
                        vuedata.$notify({ type: \'error\', message: \'Break start time must be earlier than break end time.\' });
                        return;
                    }

                    vuedata.shifts_loader = true;
                    
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var postData = {
                        action: "bookwellab_save_staff_shifts",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        staff_id: vuedata.current_staff_id_shifts,
                        shifts_data: JSON.stringify(vuedata.current_staff_shifts)
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        vuedata.shifts_loader = false;
                        if(response.data.success) {
                            vuedata.$notify({ type: \'success\', message: response.data.data.msg });
                            vuedata.shifts_drawer = false;
                        } else {
                            vuedata.$notify({ type: \'error\', message: response.data.data.msg });
                        }
                    }).catch(function(error) {
                        vuedata.shifts_loader = false;
                        console.log(error);
                    });
                },

                bwab_addBreak(dayIndex) {
                    this.active_break_day_index = dayIndex;
                    this.break_form = { start: \'12:00\', end: \'13:00\' };
                    this.break_dialog_visible = true;
                },

                bwab_saveBreak() {
                    if(this.active_break_day_index > -1) {
                        if(String(this.break_form.start || \'\') >= String(this.break_form.end || \'\')) {
                            this.$notify({ type: \'error\', message: \'Break start time must be earlier than break end time.\' });
                            return;
                        }
                        if(!this.current_staff_shifts[this.active_break_day_index].bw_breaks) {
                            this.current_staff_shifts[this.active_break_day_index].bw_breaks = [];
                        }
                        this.current_staff_shifts[this.active_break_day_index].bw_breaks.push({...this.break_form});
                        this.break_dialog_visible = false;
                    }
                },

                bwab_removeBreak(dayIndex, breakIndex) {
                    this.current_staff_shifts[dayIndex].bw_breaks.splice(breakIndex, 1);
                },

                bwab_openSpecialDays(row) {
                    const vuedata = this;
                    vuedata.special_days_drawer = true;
                    vuedata.special_days_loader = true;
                    vuedata.current_staff_id_special_days = row.bw_staff_id;
                    vuedata.bw_is_special_day_form_visible = false;
                    vuedata.bwab_getSpecialDays(row.bw_staff_id);
                },

                bwab_getSpecialDays(staff_id) {
                    const vuedata = this;
                    vuedata.special_days_loader = true;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var postData = {
                        action: "bookwellab_get_staff_special_days",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        staff_id: staff_id
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        vuedata.special_days_loader = false;
                        if(response.data.success) {
                            vuedata.current_staff_special_days = response.data.data.special_days.map(sd => {
                                let breaks = [];
                                if (typeof sd.bw_breaks === \'string\') {
                                    try { breaks = JSON.parse(sd.bw_breaks); } catch(e) { }
                                } else if (Array.isArray(sd.bw_breaks)) {
                                    breaks = sd.bw_breaks;
                                }
                                return {
                                    ...sd,
                                    bw_breaks: breaks,
                                    bw_is_off: String(sd.bw_is_off)
                                };
                            });
                        } else {
                            vuedata.$notify({ type: \'error\', message: response.data.data.msg });
                        }
                    }).catch(function(error) {
                        vuedata.special_days_loader = false;
                        console.log(error);
                    });
                },

                bwab_cancelSpecialDayForm() {
                    const vuedata = this;
                    vuedata.special_day_form = JSON.parse(JSON.stringify(vuedata.special_day_form_reset));
                    vuedata.special_day_form.bw_staff_id = vuedata.current_staff_id_special_days;
                    vuedata.bw_is_special_day_form_visible = false;
                },

                bwab_editSpecialDay(row) {
                    const vuedata = this;
                    vuedata.special_day_form = JSON.parse(JSON.stringify(row));
                    vuedata.bw_is_special_day_form_visible = true;
                },

                bwab_saveSpecialDay() {
                    const vuedata = this;
                    vuedata.special_day_save_loader = true;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    
                    if(!vuedata.special_day_form.bw_start_date || !vuedata.special_day_form.bw_end_date) {
                         vuedata.$notify({ type: \'error\', message: "Please select start and end dates." });
                         vuedata.special_day_save_loader = false;
                         return;
                    }

                    if(!vuedata.special_day_form.bw_staff_id) {
                         vuedata.special_day_form.bw_staff_id = vuedata.current_staff_id_special_days;
                    }

                    var postData = {
                        action: "bookwellab_save_staff_special_day",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        special_day_data: JSON.stringify(vuedata.special_day_form)
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        vuedata.special_day_save_loader = false;
                        if(response.data.success) {
                            vuedata.$notify({ type: \'success\', message: response.data.data.msg });
                            vuedata.bwab_cancelSpecialDayForm();
                            vuedata.bwab_getSpecialDays(vuedata.current_staff_id_special_days);
                        } else {
                            vuedata.$notify({ type: \'error\', message: response.data.data.msg });
                        }
                    }).catch(function(error) {
                        vuedata.special_day_save_loader = false;
                        console.log(error);
                    });
                },

                bwab_deleteSpecialDay(id, index) {
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var postData = {
                        action: "bookwellab_delete_staff_special_day",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        id: id
                    };
                    
                    axios.post(ajax_url, Qs.stringify(postData))
                    .then(function(response) {
                        if(response.data.success) {
                            vuedata.$notify({ type: \'success\', message: response.data.data.msg });
                            vuedata.current_staff_special_days.splice(index, 1);
                        } else {
                            vuedata.$notify({ type: \'error\', message: response.data.data.msg });
                        }
                    }).catch(function(error) {
                        console.log(error);
                    });
                },

                bwab_saveSpecialDayBreak() {
                    if(String(this.special_day_break_form.start || \'\') >= String(this.special_day_break_form.end || \'\')) {
                        this.$notify({ type: \'error\', message: \'Break start time must be earlier than break end time.\' });
                        return;
                    }
                    this.special_day_form.bw_breaks.push({...this.special_day_break_form});
                    this.special_day_break_dialog_visible = false;
                },

                bwab_removeSpecialDayBreak(breakIndex) {
                    this.special_day_form.bw_breaks.splice(breakIndex, 1);
                },

                bwab_toggleStaffStatus(row) {
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    row.status_loader = true;
                    
                    var pass_data = {
                        action: "bookwellab_toggle_staff_status",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        bw_staff_id: row.bw_staff_id,
                        bw_staff_is_active: row.bw_staff_is_active
                    };
                    
                    axios.post(ajax_url, Qs.stringify(pass_data))
                    .then(function(response) {
                        row.status_loader = false;
                        vuedata.$notify({
                            title: response.data.title,
                            message: response.data.msg,
                            type: response.data.type,
                            duration: 2000
                        });
                    }).catch(function(error) {
                        row.status_loader = false;
                        vuedata.bwab_error_handler();
                    });
                },
            ';

            return $bookwellab_page_method_data;
        }

        public function bookwellab_admin_script_vriable_data($bookwellab_admin_script_vriable_data, $bookwellpage)
        {
            if ($this->bookwell_page != $bookwellpage) {
                return $bookwellab_admin_script_vriable_data;
            }

            // Get all services for staff assignment
            $services_list = array();
            $services_table = $this->bwdb->get_table_name('services');
            $all_services = $this->bwdb->select('results', 'bw_service_id, bw_service_name, bw_service_price, bw_service_duration_val, bw_service_duration_unit', "{$services_table}", '', array(), 'bw_service_name ASC');

            foreach ($all_services as $service) {
                $services_list[] = array(
                    'bw_service_id' => $service['bw_service_id'],
                    'bw_service_name' => $service['bw_service_name'],
                    'bw_service_price' => $service['bw_service_price'],
                    'bw_service_duration_val' => $service['bw_service_duration_val'],
                    'bw_service_duration_unit' => $service['bw_service_duration_unit']
                );
            }

            $bookwellab_admin_script_vriable_data['services_list'] = $services_list;

            // Time Options for Shift Select
            $time_options = [];
            for ($i = 0; $i < 24; $i++) {
                $h = str_pad($i, 2, '0', STR_PAD_LEFT);
                $time_options[] = "$h:00";
                $time_options[] = "$h:30";
            }
            $bookwellab_admin_script_vriable_data['time_options'] = $time_options;
            $bookwellab_admin_script_vriable_data['wp_users'] = array();

            $bookwellab_admin_script_vriable_data['filter'] = array(
                'bw_staff_name' => '',
                'bw_staff_is_active' => ''
            );

            $bookwellab_admin_script_vriable_data['filter_reset'] = $bookwellab_admin_script_vriable_data['filter'];

            $bookwellab_admin_script_vriable_data['staff_form'] = array(
                'bw_staff_id' => '',
                'bw_staff_name' => '',
                'bw_staff_email' => '',
                'bw_staff_phone' => '',
                'bw_staff_position' => '',
                'bw_staff_bio' => '',
                'bw_staff_image' => '',
                'bw_wp_user_id' => 0,
                'bw_staff_is_active' => 1,
                'bw_zoom_enabled' => 0,
                'bw_zoom_user_id' => '',
                'bw_staff_services' => array()
            );

            /**
             * Filter default staff form fields for admin Vue.
             *
             * @param array $staff_form Default form map.
             */
            $bookwellab_admin_script_vriable_data['staff_form'] = apply_filters(
                'bookwellab_staff_form_defaults',
                $bookwellab_admin_script_vriable_data['staff_form']
            );
            if (!is_array($bookwellab_admin_script_vriable_data['staff_form'])) {
                $bookwellab_admin_script_vriable_data['staff_form'] = array();
            }

            $bookwellab_admin_script_vriable_data['staff_form_reset'] = $bookwellab_admin_script_vriable_data['staff_form'];

            $bookwellab_admin_script_vriable_data['form_rules'] = array(
                'bw_staff_name' => array(
                    array(
                        'required' => true,
                        'message' => esc_html__('Please enter staff name', 'bookwellnow-appointment-booking'),
                        'trigger' => 'blur'
                    )
                ),
                'bw_staff_email' => array(
                    array(
                        'required' => true,
                        'message' => esc_html__('Please enter email address', 'bookwellnow-appointment-booking'),
                        'trigger' => 'blur'
                    ),
                    array(
                        'type' => 'email',
                        'message' => esc_html__('Please enter valid email address', 'bookwellnow-appointment-booking'),
                        'trigger' => 'blur'
                    )
                )
            );

            $bookwellab_admin_script_vriable_data['insert_loader'] = false;
            $bookwellab_admin_script_vriable_data['delete_loader'] = false;
            $bookwellab_admin_script_vriable_data['list_data'] = array();

            // Shift Management Data
            $bookwellab_admin_script_vriable_data['shifts_drawer'] = false;
            $bookwellab_admin_script_vriable_data['shifts_loader'] = false;
            $bookwellab_admin_script_vriable_data['current_staff_shifts'] = array();
            $bookwellab_admin_script_vriable_data['current_staff_id_shifts'] = 0;
            $bookwellab_admin_script_vriable_data['break_dialog_visible'] = false;
            $bookwellab_admin_script_vriable_data['active_break_day_index'] = -1;
            $bookwellab_admin_script_vriable_data['break_form'] = array('start' => '12:00', 'end' => '13:00');

            // Special Days Data
            $bookwellab_admin_script_vriable_data['special_days_drawer'] = false;
            $bookwellab_admin_script_vriable_data['special_days_loader'] = false;
            $bookwellab_admin_script_vriable_data['current_staff_id_special_days'] = 0;
            $bookwellab_admin_script_vriable_data['current_staff_special_days'] = array();
            $bookwellab_admin_script_vriable_data['bw_is_special_day_form_visible'] = false;
            $bookwellab_admin_script_vriable_data['special_day_save_loader'] = false;

            $bookwellab_admin_script_vriable_data['special_day_form'] = array(
                'bw_special_day_id' => 0,
                'bw_staff_id' => 0,
                'bw_start_date' => '',
                'bw_end_date' => '',
                'bw_start_time' => '09:00',
                'bw_end_time' => '17:00',
                'bw_is_off' => '0',
                'bw_breaks' => array()
            );
            $bookwellab_admin_script_vriable_data['special_day_form_reset'] = $bookwellab_admin_script_vriable_data['special_day_form'];

            $bookwellab_admin_script_vriable_data['special_day_break_dialog_visible'] = false;
            $bookwellab_admin_script_vriable_data['special_day_break_form'] = array('start' => '12:00', 'end' => '13:00');

            return $bookwellab_admin_script_vriable_data;
        }

        public function bookwellab_staff_admin_html_func()
        {
            require BOOKWELLAB_HTML_PATH . 'staff_view.php';
        }

        /**
         * Normalize a wall-clock time to HH:MM without timezone conversion.
         *
         * @param mixed $time_raw Stored or submitted time value.
         * @return string Normalized HH:MM or empty string.
         */
        private function normalize_shift_time_hhmm($time_raw)
        {
            $time_raw = trim((string) $time_raw);
            if ($time_raw === '') {
                return '';
            }

            if (preg_match('/^(\d{1,2}):(\d{2})(?::\d{2})?$/', $time_raw, $matches)) {
                $hour = (int) $matches[1];
                $minute = (int) $matches[2];
                if ($hour >= 0 && $hour <= 23 && $minute >= 0 && $minute <= 59) {
                    return sprintf('%02d:%02d', $hour, $minute);
                }
            }

            return sanitize_text_field($time_raw);
        }

        /**
         * Normalize break start/end times for API responses.
         *
         * @param mixed $breaks JSON string or array of breaks.
         * @return array<int, array{start: string, end: string}>
         */
        private function normalize_shift_breaks_for_response($breaks)
        {
            $breaks_list = BookWellAB_Input::sanitize_break_hours_list($breaks);

            foreach ($breaks_list as &$break_item) {
                $break_item['start'] = $this->normalize_shift_time_hhmm($break_item['start']);
                $break_item['end'] = $this->normalize_shift_time_hhmm($break_item['end']);
            }
            unset($break_item);

            return $breaks_list;
        }

        /**
         * Sanitize break hours [{start, end}, ...] for database storage.
         *
         * @param mixed $breaks Raw breaks from request.
         * @return string JSON-encoded sanitized breaks.
         */
        private function sanitize_bw_breaks_for_storage($breaks)
        {
            $sanitized = BookWellAB_Input::sanitize_break_hours_list($breaks);

            foreach ($sanitized as &$break_item) {
                $break_item['start'] = $this->normalize_shift_time_hhmm($break_item['start']);
                $break_item['end'] = $this->normalize_shift_time_hhmm($break_item['end']);
            }
            unset($break_item);

            return wp_json_encode($sanitized);
        }

        /**
         * Sanitize staff-service assignment payload before database insert.
         *
         * @param mixed $services Raw services from request.
         * @return array Sanitized services array.
         */
        private function sanitize_staff_services($services)
        {
            $sanitized = array();

            if (!is_array($services)) {
                return $sanitized;
            }

            foreach ($services as $service) {
                if (!is_array($service)) {
                    continue;
                }

                $service_id = isset($service['id']) ? absint($service['id']) : 0;
                if ($service_id <= 0) {
                    continue;
                }

                $sanitized[] = array(
                    'id' => $service_id,
                    'price' => isset($service['price']) ? sanitize_text_field((string) $service['price']) : null,
                    'duration_val' => isset($service['duration_val']) ? absint($service['duration_val']) : null,
                    'duration_unit' => isset($service['duration_unit']) ? sanitize_text_field($service['duration_unit']) : null,
                    'is_primary' => isset($service['is_primary']) ? intval($service['is_primary']) : 0,
                );
            }

            return $sanitized;
        }
    }
}