<?php

namespace BookWellAB;

defined('ABSPATH') || exit;
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

if (!class_exists('BookWellAB_Services')) {

    class BookWellAB_Services
    {

        private static $instance = null;
        private $bookwell_page = 'services';
        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_services_admin_html', [$this, 'bookwellab_services_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_filter('bookwellab_admin_script_define_data',[$this,'bookwellab_admin_script_define_data']);  
            }

            /* Get Services List */
            add_action('wp_ajax_bookwellab_get_services', [$this, 'bookwellab_get_services_func']);
            add_action('wp_ajax_bookwellab_add_service', [$this, 'bookwellab_add_service_func']);
            add_action('wp_ajax_bookwellab_delete_service', [$this, 'bookwellab_delete_service_func']);
            add_action('wp_ajax_bookwellab_service_position', [$this, 'bookwellab_service_position_func']);
            add_action('wp_ajax_bookwellab_update_services_order', [$this, 'bookwellab_update_services_order_func']);
            add_action('wp_ajax_bookwellab_toggle_service_status', [$this, 'bookwellab_toggle_service_status_func']);

            /* Category Management */
            add_action('wp_ajax_bookwellab_get_categories', [$this, 'bookwellab_get_categories_func']);
            add_action('wp_ajax_bookwellab_add_category', [$this, 'bookwellab_add_category_func']);
            add_action('wp_ajax_bookwellab_delete_category', [$this, 'bookwellab_delete_category_func']);
            add_action('wp_ajax_bookwellab_update_categories_order', [$this, 'bookwellab_update_categories_order_func']);

            /* Service image (custom 350x300 medium size) */
            $this->register_service_image_size();
            add_action('init', [$this, 'register_service_image_size'], 5);
            add_action('wp_ajax_bookwellab_get_service_image_medium', [$this, 'ajax_get_service_image_medium']);
        }

        /**
         * Custom service image size used in admin + frontend cards.
         */
        public const SERVICE_IMAGE_SIZE = 'bookwellab_service_medium';
        public const SERVICE_IMAGE_WIDTH = 300;
        public const SERVICE_IMAGE_HEIGHT = 300;

        /**
         * Register BookWell service medium crop (350x300).
         *
         * @return void
         */
        public function register_service_image_size()
        {
            add_image_size(self::SERVICE_IMAGE_SIZE, self::SERVICE_IMAGE_WIDTH, self::SERVICE_IMAGE_HEIGHT, true);
        }

        /**
         * Resolve an attachment URL for the custom 350x300 service image size.
         * Generates the intermediate size when missing.
         *
         * @param int $attachment_id Attachment ID.
         * @return string
         */
        public static function get_service_medium_image_url($attachment_id)
        {
            $attachment_id = absint($attachment_id);
            if ($attachment_id <= 0 || !wp_attachment_is_image($attachment_id)) {
                return '';
            }

            $meta = wp_get_attachment_metadata($attachment_id);
            $size_meta = (is_array($meta) && isset($meta['sizes'][self::SERVICE_IMAGE_SIZE]) && is_array($meta['sizes'][self::SERVICE_IMAGE_SIZE]))
                ? $meta['sizes'][self::SERVICE_IMAGE_SIZE]
                : [];
            $has_size = !empty($size_meta);
            $has_expected_dimensions = $has_size
                && isset($size_meta['width'], $size_meta['height'])
                && (int) $size_meta['width'] === self::SERVICE_IMAGE_WIDTH
                && (int) $size_meta['height'] === self::SERVICE_IMAGE_HEIGHT;

            if (!$has_expected_dimensions) {
                require_once ABSPATH . 'wp-admin/includes/image.php';
                $file = get_attached_file($attachment_id);
                if (is_string($file) && $file !== '' && file_exists($file)) {
                    $generated = wp_generate_attachment_metadata($attachment_id, $file);
                    if (is_array($generated) && !empty($generated)) {
                        wp_update_attachment_metadata($attachment_id, $generated);
                    }
                }
            }

            $url = wp_get_attachment_image_url($attachment_id, self::SERVICE_IMAGE_SIZE);
            if (!is_string($url) || $url === '') {
                $url = wp_get_attachment_url($attachment_id);
            }

            return is_string($url) ? esc_url_raw($url) : '';
        }

        /**
         * Prefer stored custom medium URL when the given image URL maps to an attachment.
         *
         * @param string $image_url Image URL from request/media library.
         * @return string
         */
        public static function resolve_service_image_url($image_url)
        {
            $resolved = self::resolve_service_image_data($image_url, 0);
            return isset($resolved['url']) ? (string) $resolved['url'] : '';
        }

        /**
         * Resolve service image URL + attachment ID (creates 350x300 medium when needed).
         *
         * @param string $image_url     Image URL from request/media library.
         * @param int    $attachment_id Optional known attachment ID.
         * @return array{url:string,attachment_id:int}
         */
        public static function resolve_service_image_data($image_url, $attachment_id = 0)
        {
            $image_url = esc_url_raw((string) $image_url);
            $attachment_id = absint($attachment_id);

            if ($attachment_id <= 0 && $image_url !== '') {
                $attachment_id = attachment_url_to_postid($image_url);
                if ($attachment_id <= 0) {
                    $normalized = preg_replace('/-\d+x\d+(?=\.[a-zA-Z0-9]+(?:\?|$))/', '', $image_url);
                    if (is_string($normalized) && $normalized !== $image_url) {
                        $attachment_id = attachment_url_to_postid($normalized);
                    }
                }
            }

            if ($attachment_id > 0) {
                $medium = self::get_service_medium_image_url($attachment_id);
                if ($medium !== '') {
                    return [
                        'url' => $medium,
                        'attachment_id' => $attachment_id,
                    ];
                }
            }

            return [
                'url' => $image_url,
                'attachment_id' => $attachment_id,
            ];
        }

        /**
         * AJAX: return custom 350x300 service image URL for a media library attachment.
         *
         * @return void
         */
        public function ajax_get_service_image_medium()
        {
            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_services')) {
                wp_send_json_error(['msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            if (!wp_verify_nonce($nonce, 'bw_wp_nonce')) {
                wp_send_json_error(['msg' => esc_html__('Security token is miss match.', 'bookwellnow-appointment-booking')]);
            }

            $attachment_id = isset($_POST['attachment_id']) ? absint(wp_unslash($_POST['attachment_id'])) : 0;
            $resolved = self::resolve_service_image_data('', $attachment_id);
            if ($resolved['url'] === '') {
                wp_send_json_error(['msg' => esc_html__('Unable to create service image.', 'bookwellnow-appointment-booking')]);
            }

            wp_send_json_success([
                'url' => $resolved['url'],
                'attachment_id' => (int) $resolved['attachment_id'],
                'size' => self::SERVICE_IMAGE_SIZE,
            ]);
        }

        function bookwellab_service_position_func()
        {

            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $varified_none_field = wp_verify_nonce($nonce, 'bw_wp_nonce'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            if (!$varified_none_field) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token is miss match.', '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');


            // BookWellAB_Input: decoded JSON only; each field sanitized below before DB update.
            $service_data = BookWellAB_Input::get_post_json('row');
            $direction = isset($service_data['direction']) ? sanitize_text_field($service_data['direction']) : '';
            $bw_service_position = isset($service_data['bw_service_position']) ? absint($service_data['bw_service_position']) : 0;
            $bw_service_id = isset($service_data['bw_service_id']) ? absint($service_data['bw_service_id']) : 0;

            $service_res = array();
            if ($direction == "down") {

                $services_table = $this->bwdb->get_table_name('services');
                $where = "service.bw_service_position > %d";
                $where_args = array($bw_service_position);
                $service_res = $this->bwdb->select('row', 'service.*,service.bw_service_id, service.bw_service_position ', "{$services_table} service", $where, $where_args, 'bw_service_position ASC');

            } else {
                $services_table = $this->bwdb->get_table_name('services');
                $where = "service.bw_service_position < %d";
                $where_args = array($bw_service_position);
                $service_res = $this->bwdb->select('row', 'service.*,service.bw_service_id, service.bw_service_position ', "{$services_table} service", $where, $where_args, 'bw_service_position DESC');
            }

            $update_service_id = (isset($service_res['bw_service_id'])) ? $service_res['bw_service_id'] : '';
            $update_bw_service_position = (isset($service_res['bw_service_position'])) ? $service_res['bw_service_position'] : '';

            if (!empty($update_service_id)) {
                $args = array('bw_service_position' => $update_bw_service_position);
                $this->bwdb->upsert($services_table, $args, array('bw_service_id' => $bw_service_id));
                $args = array('bw_service_position' => $bw_service_position);
                $this->bwdb->upsert($services_table, $args, array('bw_service_id' => $update_service_id));
                \BookWellAB\BookWellAB_Frontend::clear_front_booking_cache();
            }

            $response['type'] = 'success';
            $response['title'] = esc_html__('Success', 'bookwellnow-appointment-booking');
            $response['msg'] = esc_html__('Service position succesfully updated.', 'bookwellnow-appointment-booking');
            wp_send_json($response);
            exit;

        }

        public function bookwellab_update_services_order_func()
        {
            global $wpdb;

            if (!\BookWellAB\BookWellAB_Permissions::has_permission('bookwellab_manage_services')) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Unauthorized', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Do not have a 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;
            }

            // BookWellAB_Input: decoded JSON only; IDs cast to absint before DB update.
            $services_order = array_map('absint', BookWellAB_Input::get_post_json('services_order'));
            $services_table = $this->bwdb->get_table_name('services');

            if (!empty($services_order)) {
                foreach ($services_order as $index => $service_id) {
                    $this->bwdb->upsert($services_table, ['bw_service_position' => $index + 1], ['bw_service_id' => $service_id]);
                }
                \BookWellAB\BookWellAB_Frontend::clear_front_booking_cache();
            }

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

        public function bookwellab_get_categories_func()
        {
            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $check_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$check_nonce) {
                wp_send_json_error(['msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $categories_table = $this->bwdb->get_table_name('categories');
            $categories = $this->bwdb->select('results', '*', $categories_table, '', [], 'bw_category_position ASC');

            // Format categories if needed
            $formatted_categories = [];
            if (!empty($categories)) {
                foreach ($categories as $category) {
                    $cat_data = (array) $category;
                    $cat_data['bw_category_name'] = BookWellAB_Input::decode_entities_for_display($cat_data['bw_category_name'] ?? '');
                    $formatted_categories[] = $cat_data;
                }
            }

            $response = [
                'type' => 'success',
                'list_data' => $formatted_categories
            ];
            wp_send_json($response);
            exit;
        }

        public function bookwellab_add_category_func()
        {
            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $check_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$check_nonce) {
                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.
            $category_data = BookWellAB_Input::get_post_json('category_data');
            $bw_category_name = isset($category_data['bw_category_name']) ? sanitize_text_field($category_data['bw_category_name']) : '';
            $bw_category_id = isset($category_data['bw_category_id']) ? intval($category_data['bw_category_id']) : 0;

            if (empty($bw_category_name)) {
                $response = [
                    'type' => 'error',
                    'title' => esc_html__('Error', 'bookwellnow-appointment-booking'),
                    'msg' => esc_html__('Please enter category name.', 'bookwellnow-appointment-booking')
                ];
                wp_send_json($response);
            }

            $categories_table = $this->bwdb->get_table_name('categories');
            $args = ['bw_category_name' => $bw_category_name];

            if ($bw_category_id == 0) {
                // New category
                $max_pos = $this->bwdb->select('var', 'bw_category_position', $categories_table, '', [], 'bw_category_position DESC');
                $args['bw_category_position'] = intval($max_pos) + 1;
                $this->bwdb->upsert($categories_table, $args);
                $msg = esc_html__('Category added successfully.', 'bookwellnow-appointment-booking');
            } else {
                // Update category
                $this->bwdb->upsert($categories_table, $args, ['bw_category_id' => $bw_category_id]);
                $msg = esc_html__('Category updated successfully.', 'bookwellnow-appointment-booking');
            }

            wp_send_json([
                'type' => 'success',
                'title' => esc_html__('Success', 'bookwellnow-appointment-booking'),
                'msg' => $msg
            ]);
            exit;
        }

        public function bookwellab_delete_category_func()
        {
            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $check_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$check_nonce) {
                wp_send_json_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',
                    'title' => esc_html__('Error', 'bookwellnow-appointment-booking'),
                    'msg' => esc_html__('Invalid category ID.', 'bookwellnow-appointment-booking')
                ]);
            }

            $categories_table = $this->bwdb->get_table_name('categories');
            $services_table = $this->bwdb->get_table_name('services');

            // Unlink services first
            $wpdb->update($services_table, ['bw_category_id' => 0], ['bw_category_id' => $id]);

            // Delete category
            $wpdb->delete($categories_table, ['bw_category_id' => $id]);

            wp_send_json([
                'type' => 'success',
                'title' => esc_html__('Success', 'bookwellnow-appointment-booking'),
                'msg' => esc_html__('Category deleted successfully.', 'bookwellnow-appointment-booking')
            ]);
            exit;
        }

        public function bookwellab_update_categories_order_func()
        {
            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $check_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$check_nonce) {
                wp_send_json_error(['msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            // BookWellAB_Input: decoded JSON only; IDs cast to absint before DB update.
            $categories_order = array_map('absint', BookWellAB_Input::get_post_json('categories_order'));
            $categories_table = $this->bwdb->get_table_name('categories');

            if (!empty($categories_order)) {
                foreach ($categories_order as $index => $cat_id) {
                    $this->bwdb->upsert($categories_table, ['bw_category_position' => $index + 1], ['bw_category_id' => $cat_id]);
                }
                \BookWellAB\BookWellAB_Frontend::clear_front_booking_cache();
            }

            wp_send_json([
                'type' => 'success',
                'title' => esc_html__('Success', 'bookwellnow-appointment-booking'),
                'msg' => esc_html__('Categories order updated successfully.', 'bookwellnow-appointment-booking')
            ]);
            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_getServices();
            ';

            return $bookwellab_page_mounted_data;

        }

        public function bookwellab_delete_service_func()
        {

            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $varified_none_field = wp_verify_nonce($nonce, 'bw_wp_nonce'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            if (!$varified_none_field) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token is miss match.', '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;
            }

            $services_table = $this->bwdb->get_table_name('services');
            $appointments_table = esc_sql($this->bwdb->get_table_name('appointments'));

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

            if ($appointment_count > 0) {
                // Deactivate the service instead of deleting
                $wpdb->update($services_table, ['bw_service_status' => 0], ['bw_service_id' => $id]);
                
                $response['type'] = 'success';
                $response['title'] = esc_html__('Success', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('This service 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
            $staff_services_table = $this->bwdb->get_table_name('staff_services');
            $wpdb->delete($staff_services_table, array('bw_service_id' => $id), array('%d'));
            $wpdb->delete($services_table, array('bw_service_id' => $id));

            $response['type'] = 'success';
            $response['title'] = esc_html__('Success', 'bookwellnow-appointment-booking');
            $response['msg'] = esc_html__('Service successfully deleted.', 'bookwellnow-appointment-booking');
            wp_send_json($response);
            exit;


        }

        public function bookwellab_add_service_func()
        {

            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $varified_none_field = wp_verify_nonce($nonce, 'bw_wp_nonce'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            if (!$varified_none_field) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token is miss match.', '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.
            $service_data = BookWellAB_Input::get_post_json('service_data');

            $bw_service_name = isset($service_data['bw_service_name']) ? sanitize_text_field($service_data['bw_service_name']) : ''; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_price = isset($service_data['bw_service_price']) ? sanitize_text_field($service_data['bw_service_price']) : ''; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_category_id = isset($service_data['bw_category_id']) ? intval($service_data['bw_category_id']) : ''; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_duration_val = isset($service_data['bw_service_duration_val']) ? intval($service_data['bw_service_duration_val']) : ''; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_duration_unit = isset($service_data['bw_service_duration_unit']) ? sanitize_text_field($service_data['bw_service_duration_unit']) : 'm'; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_description = isset($service_data['bw_service_description']) ? sanitize_text_field($service_data['bw_service_description']) : 'm'; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_image = isset($service_data['bw_service_image']) ? esc_url_raw($service_data['bw_service_image']) : ''; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_image_id = isset($service_data['bw_service_image_id']) ? absint($service_data['bw_service_image_id']) : 0; // phpcs:ignore WordPress.Security.NonceVerification
            $resolved_image = self::resolve_service_image_data($bw_service_image, $bw_service_image_id);
            $bw_service_image = $resolved_image['url'];
            $bw_service_image_id = (int) $resolved_image['attachment_id'];
            if ($bw_service_image === '') {
                $bw_service_image_id = 0;
            }
            $bw_service_position = isset($service_data['bw_service_position']) ? intval($service_data['bw_service_position']) : 1; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_status = isset($service_data['bw_service_status']) ? intval($service_data['bw_service_status']) : 1; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_id = isset($service_data['bw_service_id']) ? intval($service_data['bw_service_id']) : 0; // phpcs:ignore WordPress.Security.NonceVerification
            $bw_service_staff_raw = isset($service_data['bw_service_staff']) ? $service_data['bw_service_staff'] : array();
            $bw_service_staff = $this->sanitize_service_staff($bw_service_staff_raw);
            $bw_zoom_enabled = !empty($service_data['bw_zoom_enabled']) ? 1 : 0;

            $args = array(
                'bw_service_name' => $bw_service_name,
                'bw_service_price' => $bw_service_price,
                'bw_category_id' => $bw_category_id,
                'bw_service_duration_val' => $bw_service_duration_val,
                'bw_service_duration_unit' => $bw_service_duration_unit,
                'bw_service_description' => $bw_service_description,
                'bw_service_image' => $bw_service_image,
                'bw_service_image_id' => $bw_service_image_id,
                'bw_service_status' => $bw_service_status,
                'bw_zoom_enabled' => $bw_zoom_enabled,
                //'bw_service_position' => $bw_service_position,
            );

            $last_insert_id = '';
            $services_table = $this->bwdb->get_table_name('services');
            if ($bw_service_id == 0) {
                $bw_service_position = intval($this->bwdb->select('var', 'bw_service_position', "{$services_table}", '', array(), 'bw_service_position DESC'));
                $bw_service_position = $bw_service_position + 1;
                $args['bw_service_position'] = $bw_service_position;
                $last_insert_id = $this->bwdb->upsert($services_table, $args);
                $msg = esc_html__('Service has been added successfully.', 'bookwellnow-appointment-booking');
            } else {
                $this->bwdb->upsert($services_table, $args, array('bw_service_id' => $bw_service_id));
                $last_insert_id = $bw_service_id;
                $msg = esc_html__('Service has been updated successfully.', 'bookwellnow-appointment-booking');
            }

            if ($last_insert_id) {
                $this->bwdb->update_service_staff($last_insert_id, $bw_service_staff);
            }

            $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;


        }

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

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

            foreach ($staff_list as $staff) {
                if (!is_array($staff)) {
                    continue;
                }

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

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

            return $sanitized;
        }

        public function bookwellab_toggle_service_status_func()
        {
            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $check_nonce = wp_verify_nonce($nonce, 'bw_wp_nonce');
            if (!$check_nonce) {
                wp_send_json_error(['msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $service_id = isset($_POST['bw_service_id']) ? intval(wp_unslash($_POST['bw_service_id'])) : 0;
            $service_status = isset($_POST['bw_service_status']) ? intval(wp_unslash($_POST['bw_service_status'])) : 0;

            if ($service_id <= 0) {
                wp_send_json_error(['msg' => esc_html__('Invalid service ID.', 'bookwellnow-appointment-booking')]);
            }

            $services_table = $this->bwdb->get_table_name('services');
            $updated = $wpdb->update(
                $services_table,
                ['bw_service_status' => $service_status],
                ['bw_service_id' => $service_id]
            );

            if ($updated === false) {
                wp_send_json_error(['msg' => esc_html__('Failed to update service status.', 'bookwellnow-appointment-booking')]);
            }

            wp_send_json_success([
                'title' => esc_html__('Success', 'bookwellnow-appointment-booking'),
                'msg' => esc_html__('Service status updated successfully.', 'bookwellnow-appointment-booking'),
                'type' => 'success'
            ]);
            exit;
        }

        public function bookwellab_get_services_func()
        {

            global $wpdb;

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

            $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
            $varified_none_field = wp_verify_nonce($nonce, 'bw_wp_nonce'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            if (!$varified_none_field) {
                $response = array();
                $response['type'] = 'error';
                $response['title'] = esc_html__('Error', 'bookwellnow-appointment-booking');
                $response['msg'] = esc_html__('Security token is miss match.', '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');


            $services_table = $this->bwdb->get_table_name('services');
            $categories_table = $this->bwdb->get_table_name('categories');

            $where = '';
            $where_args = array();

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

            if (isset($filter['bw_service_name']) && !empty($filter['bw_service_name'])) {
                $search_service_name = $filter['bw_service_name'];
                $where .= " service.bw_service_name LIKE %s ";
                $where_args[] = "%{$search_service_name}%";
            }
            if (isset($filter['bw_category_id']) && $filter['bw_category_id'] !== '') {
                $bw_category_id = intval($filter['bw_category_id']);
                if ($bw_category_id >= 0) {
                    if (empty($where)) {
                        $where = 'service.bw_category_id = %d';
                    } else {
                        $where .= " AND service.bw_category_id = %d ";
                    }
                    $where_args[] = $bw_category_id;
                }
            }

            $service_res = $this->bwdb->select('results', 'service.*, category.bw_category_name', "{$services_table} service Left Join {$categories_table} as category ON service.bw_category_id = category.bw_category_id", $where, $where_args, 'bw_service_position ASC');

            $services = array();
            if (!empty($service_res)) {
                foreach ($service_res as $service_row) {

                    $service_row_single = (array) $service_row;
                    $service_row_single['bw_category_name'] = BookWellAB_Input::decode_entities_for_display($service_row['bw_category_name'] ?? '');
                    $service_row_single['bw_category_id'] = (string) intval($service_row['bw_category_id'] ?? 0);
                    $service_row_single['bw_service_name'] = BookWellAB_Input::decode_entities_for_display($service_row['bw_service_name'] ?? '');
                    $service_row_single['bw_service_image_id'] = absint($service_row['bw_service_image_id'] ?? 0);
                    $service_row_single['bw_service_image'] = esc_url_raw($service_row['bw_service_image'] ?? '');
                    if ($service_row_single['bw_service_image_id'] > 0) {
                        $resolved_service_image = self::get_service_medium_image_url($service_row_single['bw_service_image_id']);
                        if ($resolved_service_image !== '') {
                            $service_row_single['bw_service_image'] = $resolved_service_image;
                        }
                    }
                    $service_row_single['bw_service_price'] = (string) ($service_row['bw_service_price'] ?? '');
                    $service_row_single['bw_service_price_formated'] = \BookWellAB\BookWellAB_Settings::get_formatted_price((float) ($service_row['bw_service_price'] ?? 0));
                    $service_row_single['bw_service_duration_val'] = (string) ($service_row['bw_service_duration_val'] ?? '');
                    $service_row_single['bw_service_duration_unit'] = (string) ($service_row['bw_service_duration_unit'] ?? '');
                    $service_row_single['bw_service_status'] = intval($service_row['bw_service_status'] ?? 1);
                    $service_row_single['bw_zoom_enabled'] = (int) ($service_row['bw_zoom_enabled'] ?? 0);
                    $service_row_single['status_loader'] = false;

                    $duration_unit = esc_html__('Mins', 'bookwellnow-appointment-booking');
                    if ($service_row_single['bw_service_duration_unit'] == 'h') {
                        $duration_unit = esc_html__('Hour', 'bookwellnow-appointment-booking');
                        if ($service_row_single['bw_service_duration_val'] > 1) {
                            $duration_unit = esc_html__('Hours', 'bookwellnow-appointment-booking');
                        }
                    }

                    $service_row_single['bw_service_duration'] = $service_row_single['bw_service_duration_val'] . ' ' . $duration_unit;

                    $service_row_single['bw_service_description'] = BookWellAB_Input::decode_entities_for_display($service_row['bw_service_description'] ?? '');
                    $service_row_single['bw_service_status'] = (int) ($service_row['bw_service_status'] ?? 1);
                    $service_row_single['delete_loader'] = '0';
                    $service_row_single['status_loader'] = false;

                    $assigned_staff = $this->bwdb->get_service_staff((int) ($service_row['bw_service_id'] ?? 0));
                    $service_row_single['bw_service_staff'] = array();
                    if (!empty($assigned_staff) && is_array($assigned_staff)) {
                        foreach ($assigned_staff as $staff_row) {
                            $service_row_single['bw_service_staff'][] = array(
                                'bw_staff_id' => (int) ($staff_row['bw_staff_id'] ?? 0),
                                'bw_staff_name' => BookWellAB_Input::decode_entities_for_display($staff_row['bw_staff_name'] ?? ''),
                                'bw_is_primary' => (int) ($staff_row['bw_is_primary'] ?? 0),
                            );
                        }
                    }

                    $services[] = $service_row_single;

                }
            }

            $response['type'] = 'success';
            $response['list_data'] = $services;
            $response['booknow_service_position'] = '';

            wp_send_json($response);
            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('bn_wp_nonce'));

            $bookwellab_page_method_data .= '
                bwab_openCategoryManager(){
                    const vuedata = this;
                    vuedata.category_form = JSON.parse(JSON.stringify(vuedata.category_form_reset));
                    vuedata.bw_manage_categories_popup = true;
                    vuedata.bwab_getCategories();
                },
                bwab_getCategories(){
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var pass_data = { action: "bookwellab_get_categories", _wpnonce: bookwellab_ajax_obj.bookwellab_nonce };
                    
                    axios.post( ajax_url, Qs.stringify( pass_data ) )
                    .then(function(response){
                        if(response.data.type == "success"){
                            vuedata.category_list = response.data.list_data;
                            
                            // Update all_category_list for dropdowns
                            var new_options = [{ label: "Uncategorized", value: "0" }];
                            if(response.data.list_data && response.data.list_data.length > 0){
                                response.data.list_data.forEach(function(cat){
                                    new_options.push({ label: cat.bw_category_name, value: cat.bw_category_id });
                                });
                            }
                            vuedata.all_category_list = new_options;

                            setTimeout(function(){
                                var isMobile = window.innerWidth <= 782;
                                var mobileList = document.querySelector(".bw-categories-mobile-list");
                                var el = document.querySelector(".bw-categories-table .el-table__body-wrapper tbody");
                                var sortableOptions = {
                                    handle: ".bw-category-drag-handle",
                                    animation: 150,
                                    onEnd: function (evt) {
                                        vuedata.bwab_update_category_order(evt.oldIndex, evt.newIndex);
                                    }
                                };

                                if(isMobile && mobileList){
                                    Sortable.create(mobileList, Object.assign({}, sortableOptions, {
                                        draggable: ".bw-cat-mobile-card"
                                    }));
                                } else if(el){
                                    Sortable.create(el, sortableOptions);
                                }
                            }, 500);
                        }
                    });
                },
                bwab_addCategory(){
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    
                    vuedata.$refs["bw_category_frm"].validate((valid) => {
                        if(valid){
                            var pass_data = {};
                            pass_data.category_data = JSON.stringify(vuedata.category_form);
                            pass_data.action = "bookwellab_add_category";
                            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,
                                    duration: 2000
                                });
                                if(response.data.type == "success"){
                                    vuedata.category_form = JSON.parse(JSON.stringify(vuedata.category_form_reset));
                                    vuedata.bwab_getCategories();
                                }
                            });
                        }
                    });
                },
                bwab_deleteCategory(row){
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    
                    vuedata.$confirm( "Are you sure you want to delete this category?", "Warning", {
                        confirmButtonText: "OK",
                        cancelButtonText: "Cancel",
                        type: "warning"
                    }).then(() => {
                        var pass_data = { id: row.bw_category_id, action: "bookwellab_delete_category", _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,
                                duration: 2000
                            });
                            if(response.data.type == "success"){
                                vuedata.bwab_getCategories();
                            }
                        });
                    });
                },
                bwab_update_category_order(oldIndex, newIndex){
                    const vuedata = this;
                    const item = vuedata.category_list.splice(oldIndex, 1)[0];
                    vuedata.category_list.splice(newIndex, 0, item);
                    
                    var categories_order = [];
                    vuedata.category_list.forEach(function(item){
                        categories_order.push(item.bw_category_id);
                    });
                    
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var pass_data = {};
                    pass_data.categories_order = JSON.stringify(categories_order);
                    pass_data.action = "bookwellab_update_categories_order";
                    pass_data._wpnonce = bookwellab_ajax_obj.bookwellab_nonce;
                    
                    axios.post( ajax_url, Qs.stringify( pass_data ) )
                    .then(function(response){ });
                },
                bwab_update_service_order(oldIndex, newIndex){
                    const vuedata = this;
                    // Move the item in the local array
                    const item = vuedata.list_data.splice(oldIndex, 1)[0];
                    vuedata.list_data.splice(newIndex, 0, item);
                    
                    var services_order = [];
                    vuedata.list_data.forEach(function(item){
                        services_order.push(item.bw_service_id);
                    });
                    
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    var pass_data = {};
                    pass_data.services_order = JSON.stringify(services_order);
                    pass_data.action = "bookwellab_update_services_order";
                    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,
                        });
                    });
                },
                bwab_initServiceSortable(){
                    const vuedata = this;
                    const isMobile = window.innerWidth <= 782;
                    const mobileList = document.querySelector(".bw-services-mobile-list");
                    const tableBody = document.querySelector(".bw-services-table .el-table__body tbody")
                        || document.querySelector(".bw-services-table .el-table__body-wrapper tbody");

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

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

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

                    if(!sortableEl){
                        return;
                    }

                    vuedata.bw_services_sortable = Sortable.create(sortableEl, {
                        handle: ".bw-drag-handle",
                        animation: 150,
                        draggable: draggable,
                        onEnd: function (evt) {
                            if(evt.oldIndex === evt.newIndex){
                                return;
                            }
                            vuedata.bwab_update_service_order(evt.oldIndex, evt.newIndex);
                        }
                    });
                },
                bwab_position_change(row,index,position_direction){
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;

                    //vuedata.list_data[index].delete_loader = "1";
                    var pass_data = {};
                    row.direction = position_direction;
                    pass_data.row      = JSON.stringify(row);
                    pass_data.action   = "bookwellab_service_position";
                    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.bwab_getServices(false);
                        }
                    }).catch(function(error){
                        vuedata.delete_loader = false;
                        vuedata.bwab_error_handler();
                    });                    

                },
                bwab_resetFilter(){
                    const vuedata = this;
                    vuedata.filter = JSON.parse(JSON.stringify(vuedata.filter_reset));
                    vuedata.bwab_getServices();
                },
                bwab_editRow(row){
                    const vuedata = this;
                    vuedata.service_form = JSON.parse(JSON.stringify(row));
                    vuedata.service_form.bw_zoom_enabled = parseInt(row.bw_zoom_enabled, 10) || 0;
                    if (row.bw_service_staff && row.bw_service_staff.length > 0) {
                        vuedata.service_form.bw_service_staff = row.bw_service_staff.map(staff => staff.bw_staff_id);
                    } else {
                        vuedata.service_form.bw_service_staff = [];
                    }
                    vuedata.bw_add_edit_popup = true;
                },
                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_service_id;
                    pass_data.action   = "bookwellab_delete_service";
                    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_getServices();
                        }
                    }).catch(function(error){
                        vuedata.delete_loader = false;
                        vuedata.bwab_error_handler();
                    });                        
                    
                },                
                bwab_uploadServiceImage() {
                    const vuedata = this;
                    var image_frame;
                    if(image_frame){
                        image_frame.open();
                    }
                    image_frame = wp.media({
                        title: \'Select Service Image\',
                        multiple : false,
                        library : {
                            type : \'image\',
                        }
                    });
                    image_frame.on(\'select\', function() {
                        var selection = image_frame.state().get(\'selection\');
                        var attachment = selection.first();
                        if (!attachment) {
                            return;
                        }
                        var attachment_id = attachment.get(\'id\');
                        var fallback_url = attachment.get(\'url\') || \'\';

                        // Prefer custom 350x300 BookWell medium size for service images.
                        axios.post(bookwellab_ajax_obj.ajax_url, Qs.stringify({
                            action: \'bookwellab_get_service_image_medium\',
                            attachment_id: attachment_id,
                            _wpnonce: bookwellab_ajax_obj.nonce
                        })).then(function(response) {
                            if (response.data && response.data.success && response.data.data && response.data.data.url) {
                                vuedata.service_form.bw_service_image = response.data.data.url;
                                vuedata.service_form.bw_service_image_id = response.data.data.attachment_id || attachment_id || 0;
                            } else {
                                var sizes = attachment.get(\'sizes\') || {};
                                if (sizes.bookwellab_service_medium && sizes.bookwellab_service_medium.url) {
                                    vuedata.service_form.bw_service_image = sizes.bookwellab_service_medium.url;
                                } else if (sizes.medium && sizes.medium.url) {
                                    vuedata.service_form.bw_service_image = sizes.medium.url;
                                } else {
                                    vuedata.service_form.bw_service_image = fallback_url;
                                }
                                vuedata.service_form.bw_service_image_id = attachment_id || 0;
                            }
                        }).catch(function() {
                            vuedata.service_form.bw_service_image = fallback_url;
                            vuedata.service_form.bw_service_image_id = attachment_id || 0;
                        });
                    });
                    image_frame.on(\'open\',function() {
                        var selection = image_frame.state().get(\'selection\');
                        var attachment_id = parseInt(vuedata.service_form.bw_service_image_id || 0, 10);
                        if(attachment_id > 0){
                            var attachment = wp.media.attachment(attachment_id);
                            attachment.fetch();
                            selection.add( attachment ? [ attachment ] : [] );
                        }
                    });
                    image_frame.open();
                },
                bwab_removeServiceImage() {
                    this.service_form.bw_service_image = \'\';
                    this.service_form.bw_service_image_id = 0;
                },                
                bwab_closeDrawer(){
                    const vuedata = this;
                    vuedata.$refs["bw_service_frm"].resetFields();
                    vuedata.service_form = JSON.parse(JSON.stringify(vuedata.service_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_storeServiceData(){

                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    const vuedata = this;
                    vuedata.$refs["bw_service_frm"].validate((valid) => {
                        if(valid){
                            vuedata.insert_loader = "1";
                            var pass_data = {};

                            let staff_data = [];
                            if (vuedata.service_form.bw_service_staff && vuedata.service_form.bw_service_staff.length > 0) {
                                staff_data = vuedata.staff_list.filter(staff =>
                                    vuedata.service_form.bw_service_staff.includes(staff.bw_staff_id)
                                ).map(staff => ({
                                    id: staff.bw_staff_id,
                                    price: vuedata.service_form.bw_service_price,
                                    duration_val: vuedata.service_form.bw_service_duration_val,
                                    duration_unit: vuedata.service_form.bw_service_duration_unit
                                }));
                            }

                            const service_payload = JSON.parse(JSON.stringify(vuedata.service_form));
                            service_payload.bw_service_staff = staff_data;
                            pass_data.service_data = JSON.stringify(service_payload);
                            pass_data.action = "bookwellab_add_service";
                            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_getServices();
                                }
                            }).catch(function(error){
                                console.log(error);
                                vuedata.insert_loader = "0";
                                vuedata.bwab_error_handler();
                            });                        
                        }
                        
                    });                    


                },
                async bwab_getServices(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_services",_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 = response.data.list_data;
                        Vue.nextTick(function(){
                            vuedata.bwab_initServiceSortable();
                        });
                        
                    }.bind(this) )
                    .catch( function (error) {                    
                        console.log(error);
                        vm.bwab_error_handler();
                    });                
                    

                },
                bwab_toggleServiceStatus(row) {
                    const vuedata = this;
                    var ajax_url = bookwellab_ajax_obj.ajax_url;
                    row.status_loader = true;
                    
                    var pass_data = {
                        action: "bookwellab_toggle_service_status",
                        _wpnonce: bookwellab_ajax_obj.bookwellab_nonce,
                        bw_service_id: row.bw_service_id,
                        bw_service_status: row.bw_service_status
                    };
                    
                    axios.post(ajax_url, Qs.stringify(pass_data))
                    .then(function(response) {
                        row.status_loader = false;
                        var result = response.data.data ? response.data.data : response.data;
                        vuedata.$notify({
                            title: result.title,
                            message: result.msg,
                            type: result.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;
            }

            $all_category_list = array();
            $categories_table = $this->bwdb->get_table_name('categories');

            $where = '';
            $where_args = array();
            $all_categories = $this->bwdb->select('results', 'bw_category_name, bw_category_id ', "{$categories_table}", $where, $where_args, 'bw_category_name ASC');

            $all_category_list[] = array(
                'label' => esc_html__('All', 'bookwellnow-appointment-booking'),
                'value' => "",
            );
            $all_category_list[] = array(
                'label' => esc_html__('Uncategorized', 'bookwellnow-appointment-booking'),
                'value' => "0",
            );
            foreach ($all_categories as $category) {
                $all_category_list[] = array(
                    'label' => $category['bw_category_name'],
                    'value' => $category['bw_category_id'],
                );
            }
            $bookwellab_admin_script_vriable_data['all_category_list'] = $all_category_list;

            $staff_list = array();
            $staffs_table = $this->bwdb->get_table_name('staffs');
            $all_staff = $this->bwdb->select(
                'results',
                'bw_staff_id, bw_staff_name, bw_staff_is_active',
                "{$staffs_table}",
                'bw_staff_is_active = 1',
                array(),
                'bw_staff_name ASC'
            );

            foreach ($all_staff as $staff) {
                $staff_list[] = array(
                    'bw_staff_id' => (int) ($staff['bw_staff_id'] ?? 0),
                    'bw_staff_name' => BookWellAB_Input::decode_entities_for_display($staff['bw_staff_name'] ?? ''),
                );
            }

            $bookwellab_admin_script_vriable_data['staff_list'] = $staff_list;

            $bookwellab_admin_script_vriable_data['filter'] = array(
                'bw_service_name' => '',
                'bw_category_id' => '',
            );

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


            $bookwellab_admin_script_vriable_data['service_form'] = array(
                'bw_service_id' => '',
                'bw_service_name' => '',
                'bw_service_price' => '',
                'bw_service_image' => '',
                'bw_service_image_id' => 0,
                'bw_category_id' => '0',
                'bw_service_duration_val' => '30',
                'bw_service_duration_unit' => 'm',
                'bw_service_description' => '',
                'bw_service_status' => 1,
                'bw_service_position' => 1,
                'bw_zoom_enabled' => 0,
                'bw_service_staff' => array(),
            );
            $bookwellab_admin_script_vriable_data['service_form_reset'] = $bookwellab_admin_script_vriable_data['service_form'];

            $bookwellab_admin_script_vriable_data['form_rules'] = array(
                'bw_service_name' => array(
                    array(
                        'required' => true,
                        'message' => esc_html__('Please enter service name', 'bookwellnow-appointment-booking'),
                        'trigger' => 'blur',
                    ),
                ),
                'bw_service_duration_val' => array(
                    array(
                        'required' => true,
                        'message' => esc_html__('Please enter duration', 'bookwellnow-appointment-booking'),
                        'trigger' => 'blur',
                    ),
                ),
                'bw_service_price' => array(
                    array(
                        'required' => true,
                        'message' => esc_html__('Please enter price', 'bookwellnow-appointment-booking'),
                        'trigger' => 'blur',
                    ),
                ),
                'bw_category_name' => array(
                    array(
                        'required' => true,
                        'message' => esc_html__('Please enter category name', 'bookwellnow-appointment-booking'),
                        'trigger' => 'blur',
                    ),
                )
            );

            $bookwellab_admin_script_vriable_data['bw_manage_categories_popup'] = false;
            $bookwellab_admin_script_vriable_data['category_list'] = array();
            $bookwellab_admin_script_vriable_data['category_form'] = array(
                'bw_category_id' => '',
                'bw_category_name' => '',
            );
            $bookwellab_admin_script_vriable_data['category_form_reset'] = $bookwellab_admin_script_vriable_data['category_form'];

            $bookwellab_admin_script_vriable_data['insert_loader'] = false;
            $bookwellab_admin_script_vriable_data['delete_loader'] = false;

            $bookwellab_admin_script_vriable_data['list_data'] = array();
            $bookwellab_admin_script_vriable_data['price_number_of_decimals'] = 1;

            return $bookwellab_admin_script_vriable_data;
        }

        public function bookwellab_services_admin_html_func()
        {
            require BOOKWELLAB_HTML_PATH . 'services_view.php';
        }


    }


}
// phpcs:enable

