<?php

namespace Booktics\Order\Controllers;

use Booktics\Abstracts\Base_Rest_Controller;
use Booktics\Abstracts\Booktics_Database;
use Booktics\Abstracts\User_Model;
use Booktics\Cart\Cart_Manager;
use Booktics\Customer\Handlers\Customer_Event_Handler;
use Booktics\Models\Appointment_Model;
use Booktics\Models\Customer_Model;
use Booktics\Models\Guest_Model;
use Booktics\Models\Order_Model;
use Booktics\Models\Payment_Model;
use Booktics\Models\Service_Model;
use Booktics\Models\Team_Member_Model;
use Booktics\Order\Order_Validator;
use Booktics\Payment\Payment_Method_Manager;
use Booktics\Payment\Payment_Status;
use Booktics\Services\Time_Zone_Handler;
use DateTime;
use Exception;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Request;
use WP_REST_Server;

/**
 * Order controller
 *
 * @package Booktics/Order
 */
class Order_Controller extends Base_Rest_Controller {

    /**
     * Endpoint namespace
     *
     * @var string
     */
    protected $namespace = 'booktics/v1';

    /**
     * Route name
     *
     * @var string
     */
    protected $base = 'orders';

    /**
     * Register REST API routes for orders.
     *
     * @return void
     */
    public function register_routes(): void {
        register_rest_route(
            $this->namespace, $this->base, array(
                array(
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => array( $this, 'create_item' ),
                    'permission_callback' => array(
                        $this,
                        'create_order_permission',
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_items' ),
                    'permission_callback' => array(
                        $this,
                        'get_items_permission',
                    ),
                    'args'                => array(
                        'page'     => array( 'default' => 1 ),
                        'per_page' => array( 'default' => 10 ),
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => array(
                        $this,
                        'bulk_delete_items',
                    ),
                    'permission_callback' => array(
                        $this,
                        'bulk_delete_items_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, $this->base . '/(?P<id>[\d]+)', array(
                array(
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => array( $this, 'update_item' ),
                    'permission_callback' => array(
                        $this,
                        'update_item_permission',
                    ),
                    'args'                => array(
                        'id'     => array( 'required' => true ),
                        'status' => array( 'type' => 'string' ),
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_item' ),
                    'permission_callback' => array(
                        $this,
                        'get_item_permission',
                    ),
                    'args'                => array(
                        'id'         => array( 'required' => true ),
                        'order_key'  => array(
                            'description' => __( 'Secret key that proves the client may view this order without logging in (returned when the order is created).', 'booktics' ),
                            'type'        => 'string',
                            'required'    => false,
                        ),
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => array( $this, 'delete_item' ),
                    'permission_callback' => array(
                        $this,
                        'delete_item_permission',
                    ),
                    'args'                => array(
                        'id' => array( 'required' => true ),
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, $this->base . '/(?P<id>[\d]+)/payment', array(
                array(
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => array(
                        $this,
                        'update_payment_status',
                    ),
                    'permission_callback' => array(
                        $this,
                        'update_payment_status_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, $this->base . '/(?P<id>[\d]+)/refund', array(
                array(
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => array( $this, 'refund_payment' ),
                    'permission_callback' => array(
                        $this,
                        'update_payment_status_permission',
                    ),
                ),
            )
        );
    }

    /**
     * Check if the current user has permission to create orders.
     *
     * @param WP_REST_Request $request
     *
     * @return bool
     */
    public function create_order_permission( $request ) {
        return true;
    }

    /**
     * Create a new order with the provided data
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     * @throws Exception
     */
    public function create_item( $request ) {
        $input_data = json_decode( $request->get_body(), true );

        // Single comprehensive validation and preparation
        $validation_result = $this->validate_and_prepare_order_data( $input_data );
        if ( is_wp_error( $validation_result ) ) {
            $validation_error_msg = $validation_result->get_error_message() ?? __( 'Order validation failed', 'booktics' );

            return $this->error(
                $validation_error_msg,
                422,
                'Validation'
            );
        }

        $db = new Booktics_Database();

        // Resolve customer ID
        $customer_id_result = $this->resolve_customer_id( $input_data );
        if ( is_wp_error( $customer_id_result ) ) {
            return $this->error( $customer_id_result->get_error_message() );
        }
        $input_data['customer_id'] = $customer_id_result;

        try {
            $db->begin_transaction();
            list( $order_model, $appointments ) = $this->create_order_and_appointments( $input_data, $db );
            $payment_intent = $this->handle_payment_if_needed( $input_data, $order_model, $db );
            $db->commit();
        } catch ( Exception $e ) {
            $db->roll_back();
            return $this->error( $e->getMessage() );
        }

        $this->send_notifications( $db, $order_model, $input_data['customer_id'], $appointments );
        $response = $this->format_order_response( $db, $order_model, $input_data, $payment_intent );
        do_action('booktics_after_order_created', $order_model, $appointments);
        
        $response_message = 'Order created successfully';
        if ( isset( $input_data['pay_now']) && $input_data['pay_now'] ) {
            $response_message = 'Order pending, please proceed payment';
        }

        return $this->response(
            $response,
            sprintf(
                /* translators: %s: dynamic response message */
                __( 'Response %s', 'booktics' ),
                $response_message
            ),
            201
        );
    }

    /**
     * Comprehensive validation and preparation of order data
     *
     * @param array $input_data
     *
     * @return array|WP_Error
     */
    private function validate_and_prepare_order_data( &$input_data ) {
        // Step 1: Basic validation using Order_Validator
        $validator = new Order_Validator();
        $validation_result = $validator->validate( $input_data );
        if ( is_wp_error( $validation_result ) ) {
            return $validation_result;
        }

        // Step 2: Preserve original timezone for appointment creation, then validate and setup working timezone
        $input_data['original_timezone'] = $input_data['timezone'] ?? wp_timezone_string();
        if ( ! isset( $input_data['timezone'] ) || ! Time_Zone_Handler::validate_time_zone( $input_data['timezone'] ) ) {
            $input_data['timezone'] = 'UTC';
        }

        // Step 3: Payment validation if pay_now is true
        if ( isset( $input_data['pay_now'] ) && $input_data['pay_now'] ) {
            $validate = booktics_validate(
                $input_data, array(
                    'payment_method' => array( 'required' ),
                )
            );
            if ( is_wp_error( $validate ) ) {
                return $validate;
            }
        }

        // Step 4: Process and validate each item
        if ( ! is_array( $input_data['items'] ) || count( $input_data['items'] ) == 0 ) {
            return new WP_Error( 'items', __( 'Items are required', 'booktics' ) );
        }

        foreach ( $input_data['items'] as $key => $item ) {
            // Validate required fields for each item
            $item_validate = booktics_validate(
                $item, array(
                    'date'           => array( 'required' ),
                    'start_time'     => array( 'required' ),
                    'end_time'       => array( 'required' ),
                    'team_member_id' => array( 'required' ),
                    'service_id'     => array( 'required' ),
                )
            );

            if ( is_wp_error( $item_validate ) ) {
                return $item_validate;
            }

            // Convert times to UTC
            $converted_start_time = Time_Zone_Handler::convert_to_utc(
                $item['start_time'],
                $item['date'],
                $input_data['timezone']
            );
            $converted_end_time = Time_Zone_Handler::convert_to_utc(
                $item['end_time'],
                $item['date'],
                $input_data['timezone']
            );

            // Validate time range
            $start_time = strtotime( $converted_start_time['time'] );
            $end_time = strtotime( $converted_end_time['time'] );

            if ( ! $start_time || ! $end_time || $start_time >= $end_time ) {
                return new WP_Error( 'time_invalid', __( 'Invalid time range provided', 'booktics' ) );
            }

            // In validate_and_prepare_order_data(), after converting times:
            $item_with_converted_times = array_merge($item, [
                'start_time' => $converted_start_time['time'],
                'end_time' => $converted_end_time['time'],
                'date' => gmdate('Y-m-d', strtotime($converted_start_time['date']))
            ]);

            $item_timet_slot_validate = $this->validate_appointment_time_is_free($item_with_converted_times);

            if ( is_wp_error( $item_timet_slot_validate ) ) {
                return $item_timet_slot_validate;
            }

            // Sanitize and prepare item data
            $input_data['items'][ $key ] = array(
                'start_time'             => $converted_start_time['time'],
                'end_time'               => $converted_end_time['time'],
                'date'                   => gmdate( 'Y-m-d', strtotime( $item['date'] ) ),
                'customer_id'            => isset( $item['customer_id'] ) ? absint( $item['customer_id'] ) : null,
                'team_member_id'         => absint( $item['team_member_id'] ),
                'service_id'             => absint( $item['service_id'] ),
                'additional_duration_id' => $item['additional_duration_id'] ?? '',
                'price'                  => floatval( $item['price'] ?? 0 ),
                'subtotal'               => floatval( $item['subtotal'] ?? 0 ),
                'quantity'               => absint( $item['quantity'] ?? 1 ),
                'total_attendees'        => absint( $item['total_attendees'] ?? 1 ),
            );
            $input_data['items'][$key] = apply_filters('booktics_pro_appointment_validate', $input_data['items'][$key], $item);
        }

        $input_data = apply_filters( 'booktics_pro_order_validate', $input_data );

        return $input_data;
    }

    /**
     * Check if the requested time slot has available capacity for the given team member
     * Works by checking all appointments for this team member on this date and calculating remaining capacity
     * 
     * @param  array $item Appointment item data
     * @return bool|WP_Error True if time slot has capacity, WP_Error if not
     */
    private function validate_appointment_time_is_free( $item ) {
        try {
            // Convert times to DateTime objects
            $requested_start = new \DateTime( $item['date'] . ' ' . $item['start_time'] );
            $requested_end = new \DateTime( $item['date'] . ' ' . $item['end_time'] );
            
            // Get the requested attendee count (default to 1 for backward compatibility)
            $requested_attendees = isset( $item['total_attendees'] ) ? (int) $item['total_attendees'] : 1;
            
            // Get service to check if group booking is enabled and get max capacity
            $service = Service_Model::find($item['service_id']);
            if ( ! $service ) {
                return new \WP_Error( 'service_not_found', __( 'Service not found', 'booktics' ) );
            }
            
            $max_group_size = $this->get_service_max_group_size( $service->id );

            
            $args = [
                'team_member_id' => $item['team_member_id'],
                'date'           => $item['date'],
                'post_status'    => booktics_get_option('appointment_statuses_for_blocked_slot', []),
            ];

            // Get all non-cancelled appointments for this team member on this date
            $appointments = Appointment_Model::all( $args );

            if ( ! is_array( $appointments ) || empty( $appointments ) ) {
                // No existing appointments, check if requested attendees fit within max capacity
                if ( $requested_attendees > $max_group_size ) {
                    /* translators: %1$d = requested attendees, %2$d = maximum group size */
                    return new \WP_Error( 'exceeds_max_capacity', sprintf( __( 'Requested attendees (%1$d) exceeds maximum group size (%2$d) for this service.', 'booktics' ), $requested_attendees, $max_group_size ) );
                }

                return true;
            }

            // Calculate total attendees for overlapping time slots
            $overlapping_attendees = 0;

            foreach ( $appointments as $appointment ) {
                // Skip if this is the same appointment (for updates)
                if ( isset($item['id']) && $appointment->ID == $item['id'] ) {
                    continue;
                }
                
                $appt_start = new \DateTime( $appointment->date . ' ' . $appointment->start_time );
                $appt_end = new \DateTime( $appointment->date . ' ' . $appointment->end_time );

                // Check for time overlap
                if ( ! ( $requested_end <= $appt_start || $requested_start >= $appt_end ) ) {
                    // Times overlap, add this appointment's attendees to the count
                    $appointment_attendees = $appointment->total_attendees ?? 1;
                    $overlapping_attendees += $appointment_attendees;
                }
            }
            
            // Check if adding the requested attendees would exceed capacity
            $total_after_booking = $overlapping_attendees + $requested_attendees;
            
            if ($total_after_booking > $max_group_size) {
                return new \WP_Error( 'insufficient_capacity', __( 'Insufficient capacity' , 'booktics' ) );
            }

            return true;
        } catch ( \Throwable $e ) {
            return new \WP_Error(
                'time_validation_error',
                __( 'Error validating appointment time. Please try another slot.', 'booktics' )
            );
        }
    }

    /**
     * Get max group size for a specific service
     * 
     * @param int $service_id Service ID
     * 
     * @return int Max group size
     */
    private function get_service_max_group_size( $service_id ) {
        $default_max_size = 1;
        $group_booking_meta = get_post_meta( $service_id, 'group_booking', true );
        
        if ( ! $this->is_group_booking_enabled( $group_booking_meta ) ) {
            return $default_max_size;
        }

        return $this->extract_max_booking_size( $group_booking_meta, $default_max_size );
    }
    
    /**
     * Checks if group booking is enabled for a services group booking meta data
     * 
     * @param array $group_booking_meta Group booking meta data
     * 
     * @return bool True if group booking is enabled, false otherwise
     */
    private function is_group_booking_enabled( $group_booking_meta ) {
        return !empty( $group_booking_meta )  && is_array( $group_booking_meta ) && !empty( $group_booking_meta['enable_group_booking'] );
    }
    
    /**
     * Extracts the max booking size from the group booking meta data of a service
     * 
     * @param array $group_booking_meta Group booking meta data
     * @param int $default_size Default max booking size
     * 
     * @return int Max booking size
     */
    private function extract_max_booking_size( $group_booking_meta, $default_size ) {
        if ( isset( $group_booking_meta['max_booking'] ) && is_numeric( $group_booking_meta['max_booking'] ) ) {
            return (int) $group_booking_meta['max_booking'];
        }
        
        return $default_size;
    }

    /**
     * Resolve the customer ID from input data, creating or finding the customer as needed
     *
     * @param array $input_data
     *
     * @return int|WP_Error
     */
    private function resolve_customer_id( &$input_data ) {
        if ( isset( $input_data['customer_id'] ) ) {
            return $input_data['customer_id'];
        }
        if ( ! isset( $input_data['customer'] ) ) {
            return new WP_Error( 'missing_customer', __( 'Customer information is required', 'booktics' ) );
        }

        return $this->find_or_create_customer( $input_data['customer'] );
    }

    /**
     * Create the order and its associated appointments
     *
     * @param array $input_data
     * @param Booktics_Database $db
     *
     * @return array [Order_Model $order_model, array $appointments]
     */
    private function create_order_and_appointments( $input_data, $db ) {
        $input_data['currency'] = ! empty( $input_data['currency'] ) ? strtolower( $input_data['currency'] ) : strtolower( booktics_get_option( 'currency', 'USD' ) );
        $order_model            = new Order_Model( $db );

        $input_timezone_string  = $input_data['timezone'] ?? 'UTC';
        $input_timezone         = Time_Zone_Handler::get_timezone_from_string( $input_timezone_string );
        $original_timezone      = $input_data['original_timezone'] ?? $input_timezone_string;

        $order_data = array(
            'customer_id'     => $input_data['customer_id'],
            'payment_status'  => Payment_Status::FAILED,
            'price_breakdown' => $input_data['price_breakdown'] ?? array(),
            'status'          => 'open',
            'tax_total'       => $input_data['tax_total'] ?? 0,
            'coupon_code'     => $input_data['coupon_code'] ?? '',
            'coupon_discount' => $input_data['coupon_discount'] ?? 0,
            'subtotal'        => $input_data['subtotal'],
            'timezone'        => $input_timezone,
            'total'           => $input_data['total'],
            'currency'        => $input_data['currency'],
            'payment_method'  => $input_data['payment_method'] ?? null,
        );

        // This is to only allow setting payment status when creating order from admin panel as admin
        if ( isset( $input_data['payment_status'] ) && current_user_can( 'manage_options' ) ) {
            $order_data['payment_status'] = $input_data['payment_status'];
        }

        $order_data   = apply_filters( 'booktics_pro_order_columns', $order_data, $input_data );
        $order_model  = $order_model->create( $order_data );
        $appointments = array();
        foreach ( $input_data['items'] as $item ) {
            if ( isset( $item['uuid'] ) ) {
                ( new Cart_Manager() )->remove_cart_item( $item['uuid'] );
            }
            $total_duration_min = $this->calculate_total_duration( $item );

            $appointment_data   = array(
                'start_time'             => wp_date( get_option( 'time_format' ), strtotime( $item['start_time'] ), $input_timezone ) ?? '',
                'end_time'               => wp_date( get_option( 'time_format' ), strtotime( $item['end_time'] ), $input_timezone ) ?? '',
                'date'                   => wp_date( 'Y-m-d', strtotime( $item['date'] ), $input_timezone ),
                'customer_id'            => $input_data['customer_id'] ?? '',
                'team_member_id'         => $item['team_member_id'] ?? '',
                'service_id'             => $item['service_id'] ?? '',
                'additional_duration_id' => $item['additional_duration_id'] ?? '',
                'duration'               => $total_duration_min,
                'order_id'               => $order_model->id,
                'quantity'               => $item['quantity'] ?? 1,
                'price'                  => $item['price'] ?? 0,
                'subtotal'               => $item['subtotal'] ?? 0,
                'total'                  => $item['total'] ?? $item['subtotal'] ?? 0,
                'status'                 => booktics_get_option( 'appointment_status', 'pending' ),
                'total_attendees'        => $input_data['total_attendees'] ?? 1,
                'timezone'               => $original_timezone,
            );
            
          
            
            $appointment_data   = apply_filters( 'booktics_appointment_pro_format', $appointment_data, $item );
            
            $appointment = Appointment_Model::create( $appointment_data );
            if ( is_wp_error( $appointment ) ) {
                throw new Exception( esc_html( $appointment->get_error_message() ) );
            }

            do_action( 'booktics_after_appointment_created', $appointment );
            $appointments[] = $appointment;
        }

        return array( $order_model, $appointments );
    }


    /**
     * Process payment for an order if immediate payment is requested
     *
     * @param array $input_data Order input data containing payment details
     * @param Order_Model $order_model Order model instance
     * @param Booktics_Database $db Database instance
     *
     * @return array
     * @throws Exception
     */
    private function handle_payment_if_needed( $input_data, $order_model, $db ) {
        if ( ! isset( $input_data['pay_now'] ) || ! $input_data['pay_now'] ) {
            return array();
        }

        $payment_method = ( new Payment_Method_Manager() )->get_payment_method( $input_data['payment_method'] );
        if ( ! $payment_method ) {
            throw new Exception( esc_html__( 'Payment method cannot be found!', 'booktics' ) );
        }

        $payment_intent = $payment_method->process_payment( $order_model );

        if ( $payment_intent['status'] === 'error' ) {
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception message is not directly output; escaping is handled at the display layer
            throw new Exception( $payment_intent['message'] );
        }

        if ( $payment_intent['status'] === 'success' ) {
            $this->update_payment_records( $db, $order_model, $input_data, $payment_intent );
            // Do NOT set fully_paid here — PaymentIntent created ≠ payment collected.
            // Status is updated after client-side confirmation via update_payment_status endpoint.
        }

        return $payment_intent;
    }


    /**
     * Update payment records after successful payment processing
     *
     * @param Booktics_Database $db Database instance
     * @param Order_Model $order_model Order model instance
     * @param array $input_data Order input data containing payment details
     * @param array $payment_intent Payment intent data from payment processor
     *
     * @return void
     */
    private function update_payment_records( $db, $order_model, $input_data, $payment_intent ) {
        ( new Order_Model( $db ) )->update(
            $order_model->id,
            array( 'payment_intent_id' => $payment_intent['payment_intent'] )
        );

        ( new Payment_Model( $db ) )->create(
            array(
				'order_id'       => $order_model->id,
				'customer_id'    => $input_data['customer_id'],
				'amount'         => $input_data['total'] * 100,
				'currency'       => $input_data['currency'] ?? booktics_get_option( 'currency', 'USD' ),
				'intent_id'      => $payment_intent['payment_intent'],
				'transaction_id' => null,
				'payment_method' => $input_data['payment_method'],
				'status'         => $payment_intent['status'] == 'success' ? 'completed' : 'pending',
				'date'           => gmdate( 'Y-m-d' ),
				'date_time'      => gmdate( 'Y-m-d H:i:s' ),
            )
        );
    }

    /**
     * Helper: Format a single appointment for API output
     *
     * @param Appointment_Model|array $appointment
     *
     * @return array
     */
    private function format_appointment( $appointment ): array {
        if ( is_object( $appointment ) && method_exists( $appointment, 'to_array' ) ) {
            $appointment = $appointment->to_array();
        }
        $team_member           = ( new Team_Member_Model() )->find( $appointment['team_member_id'] );
        $service               = ( new Service_Model() )->find( $appointment['service_id'] );
        $service               = $service ? $service->to_array() : array();
        $additional_durations  = $service['additional_durations'] ?? array();
        $additional_durations  = ! empty( $additional_durations )
            ? array_filter(
                $additional_durations, function ( $duration ) use ( $appointment ) {
					return $duration['id'] === $appointment['additional_duration_id'];
				}
            ) : array();
        $additional_duration   = ! empty( $additional_durations ) ? reset( $additional_durations ) : array();
        $formatted_appointment = array(
            'id'                     => $appointment['id'] ?? 0,
            'date'                   => $appointment['date'] ?? '',
            'start_time'             => $appointment['start_time'] ?? '',
            'end_time'               => $appointment['end_time'] ?? '',
            'additional_duration_id' => $appointment['additional_duration_id'] ?? '',
            'team'                   => array(
                'id'           => $team_member->id ?? null,
                'display_name' => $team_member->display_name ?? '',
                'image'        => $team_member->image ?? '',
                'user_email'        => $team_member->user_email ?? '',
            ),
            'service'                => array(
                'id'                  => $appointment['service_id'] ?? '',
                'title'               => $service['title'] ?? '',
                'additional_duration' => $additional_duration,
                'enable_google_meet'  => $service['enable_google_meet'] ?? false,
            ),
            'duration'               => $appointment['duration'] ?? null,
            'quantity'               => $appointment['quantity'] ?? 1,
            'price'                  => $appointment['price'] ?? 0,
            'subtotal'               => $appointment['subtotal'] ?? 0,
            'total'                  => $appointment['total'] ?? 0,
            'status'                 => $appointment['status'] ?? '',
            'total_attendees'        => $appointment['total_attendees'] ?? 1,
            'appointment_token'      => $appointment['security_token'] ?? '',
            'cancel_note'            => $appointment['cancel_note'] ?? '', 
        );
        $formatted_appointment = apply_filters( 'booktics_appointment_pro_format', $formatted_appointment, $appointment );
        return $formatted_appointment;
    }

    /**
     * Helper: Format order for API output
     *
     * @param Order_Model $order_model
     * @param array $input_data
     * @param array $payment_intent
     *
     * @return array
     */
    private function format_order_response( $db, $order_model, $input_data, $payment_intent ): array {
        $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $input_data['customer_id'] ) );
        // Ensure $order_model is an object before accessing properties
        $order_id            = is_object( $order_model ) ? $order_model->id : ( is_array( $order_model ) ? $order_model['id'] : null );
        $appointments        = ( new Appointment_Model() )->where( 'order_id', $order_id );
        $appointment_details = array();
        foreach ( $appointments as $appointment ) {
            $appointment_details[] = $this->format_appointment( $appointment );
        }

        $order_no_for_key = is_object( $order_model ) ? $order_model->order_no : ( $order_model['order_no'] ?? '' );

        $order_data = array(
            'id'                => $order_id,
            'order_no'          => is_object( $order_model ) ? $order_model->order_no : ( $order_model['order_no'] ?? null ),
            'order_key'         => $this->get_order_view_key( (int) $order_id, (string) $order_no_for_key ),
            'customer'          => array(
                'id'           => $customer->id ?? null,
                'name'         => $customer->name ?? '',
                'phone'        => $customer->phone ?? '',
                'email'        => $customer->email ?? '',
            ),
            'status'            => is_object( $order_model ) ? $order_model->status : ( $order_model['status'] ?? null ),
            'subtotal'          => is_object( $order_model ) ? $order_model->subtotal : ( $order_model['subtotal'] ?? null ),
            'total'             => is_object( $order_model ) ? $order_model->total : ( $order_model['total'] ?? null ),
            'payment_status'    => is_object( $order_model ) ? $order_model->payment_status : ( $order_model['payment_status'] ?? null ),
            'payment_method'    => is_object( $order_model ) ? $order_model->payment_method : $order_model['payment_method'],
            'items'             => $appointment_details,
            'payment_intent_id' => $payment_intent['payment_intent'] ?? '',
            'client_secret'     => $payment_intent['client_secret'] ?? '',
            'total_services'    => $input_data['total_services'] ?? 1,
        );

        $order_data = apply_filters( 'booktics_pro_order_format', $order_data, $order_model );

        return $order_data;
    }

    /**
     * Helper: Send notifications for order and appointment creation
     *
     * @param Order_Model $order_model
     * @param int $customer_id
     * @param array $appointments
     *
     * @return void
     */
    private function send_notifications( $db, $order_model, $customer_id, $appointments ): void {
        $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $customer_id ) );
        foreach ( $appointments as $appointment ) {
            $formatted_appointment = $this->format_appointment( $appointment );
            $appointment_time      = $formatted_appointment['date'] . ' ' . $formatted_appointment['start_time'];
            $dt                    = new DateTime( $appointment_time, wp_timezone() );
            $timestamp             = $dt->getTimestamp();
            $appointment_datetime  = wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp, wp_timezone() );
            $notification_flow_data_for_appointment = array(
                'appointment_service'     => $formatted_appointment['service']['title'] ?? '',
                'appointment_team_member' => $formatted_appointment['team']['display_name'] ?? '',
                'start_time'              => $formatted_appointment['start_time'],
                'appointment_date'        => $formatted_appointment['date'],
                'appointment_time'        => $appointment_datetime,
                'attendee_count'          => $formatted_appointment['attendee_count'] ?? null,
                'customer_name'           => $customer->name ?? '',
                'user_email'              => $customer->email ?? '',
                'appointment_team_member_email' => $formatted_appointment['team']['user_email'] ?? '',
            );
            $notification_flow_data_for_appointment = apply_filters( 'booktics_appointment_scheduled_notification_flow_data', $notification_flow_data_for_appointment, $formatted_appointment );

            do_action( 'booktics_gln_hook', 'appointment_scheduled', $notification_flow_data_for_appointment );
        }

        $appointment = $appointments[0]; // As currently only one appointment is created for each user
        $formatted_appointment = $this->format_appointment( $appointment );
        $notification_flow_data_for_order = [
            'order_no'      => $order_model->order_no ?? '',
            'order_date'    => $order_model->created_at ?? '',
            'order_total'   => $order_model->total ?? 0,
            'customer_name' => $customer->name ?? '',
            'user_email'    => $customer->email ?? '',
            'order_status'  => $order_model->status ?? '',
            'service_name'  => $formatted_appointment['service']['title'] ?? '',
            'team_name'     => $formatted_appointment['team']['display_name'] ?? '',
            'start_time'    => $formatted_appointment['start_time'] ?? '',
            'end_time'      => $formatted_appointment['end_time'] ?? '',
            'appointment_date' => $formatted_appointment['date'] ?? '',
            'location_address' => $formatted_appointment['location']['address'] ?? '',
            'appointment_status' => $formatted_appointment['status'] ?? '',
            'appointment_cancel_url' => $appointment->get_cancel_link(),
            'appointment_reschedule_url' => $appointment->get_reschedule_link(),
        ];

        $notification_flow_data_for_order = apply_filters( 'booktics_order_created_notification_flow_data', $notification_flow_data_for_order, $formatted_appointment, $order_model );

        do_action( 'booktics_gln_hook', 'order_created', $notification_flow_data_for_order );
    }

    /**
     * Find existing customer by email or create new customer
     *
     * @param array $customer
     *
     * @return int|WP_Error
     */
    private function find_or_create_customer( array $customer ) {
        $db = new Booktics_Database();
        if ( ! isset( $customer['email'] ) ) {
            return new WP_Error( 'invalid_email', __( 'Customer email is required', 'booktics' ) );
        }
        $user_login          = User_Model::generate_username( $customer['email'] );
        $allow_customer_as_wp_user = booktics_get_option( 'allow_customer_as_wp_user', false );
        $wp_user_id          = null;
        if ( $allow_customer_as_wp_user ) {
            $wp_user_id = $this->get_or_create_wp_user( $customer, $user_login );
        }
        $guest = $this->find_and_update_guest( $db, $customer, $wp_user_id );
        if ( $guest ) {
            return $guest->id;
        }
        $guest = $this->create_guest( $db, $customer, $user_login, $wp_user_id );

        return $guest->id;
    }

    /**
     * Get or create a WordPress user for the customer
     *
     * @param array $customer
     * @param string $user_login
     *
     * @return int WP user ID
     */
    private function get_or_create_wp_user( $customer, $user_login ) {
        $wp_user = get_user_by( 'email', $customer['email'] );
        if ( $wp_user ) {
            return $wp_user->ID;
        }
        $userdata       = array(
            'user_login'   => $user_login,
            'user_email'   => $customer['email'],
            'first_name'   => $customer['name'] ?? '',
            'display_name' => $customer['name'] ?? '',
            'phone'        => $customer['phone'] ?? '',
        );
        $customer_model = new Customer_Model();
        $customer_id    = $customer_model->save( $userdata );
        ( new Customer_Event_Handler() )->on_customer_create( $customer_id );

        return $customer_id;
    }

    /**
     * Find and update a guest record by email
     *
     * @param Booktics_Database $db
     * @param array $customer
     *
     * @return object|null Guest_Model instance or null
     */
    private function find_and_update_guest( $db, $customer, $wp_user_id = null ) {
        $guest = ( new Guest_Model( $db ) )->find( array( 'email' => $customer['email'] ) );
        if ( $guest ) {
            ( new Guest_Model( $db ) )->update(
                $guest->id, array(
					'name'       => $customer['name'] ?? '',
					'phone'      => $customer['phone'] ?? '',
					'wp_user_id' => $wp_user_id,
                )
            );
        }

        return $guest;
    }

    /**
     * Create a new guest record
     *
     * @param Booktics_Database $db
     * @param array $customer
     * @param string $user_login
     * @param int|null $wp_user_id
     *
     * @return object Guest_Model instance
     */
    private function create_guest( $db, $customer, $user_login, $wp_user_id ) {
        return ( new Guest_Model( $db ) )->create(
            array(
				'wp_user_id' => $wp_user_id,
				'email'      => $customer['email'],
				'name'       => $customer['name'] ?? '',
				'user_login' => $user_login,
				'phone'      => $customer['phone'] ?? '',
            )
        );
    }

    /**
     * Check if user has permission to list orders.
     *
     * Anonymous access is denied: the collection handler scopes to the current customer or team member;
     * unauthenticated callers would otherwise receive unscoped results. Single-order GET is allowed for
     * guests when {@see get_item_permission()} validates a matching order_key.
     *
     * @param WP_REST_Request $request
     *
     * @return bool|WP_Error
     */
    public function get_items_permission( $request ) {
        if ( is_user_logged_in() ) {
            return true;
        }

        return new WP_Error(
            'booktics_forbidden',
            __( 'You are not allowed to view orders.', 'booktics' ),
            array( 'status' => 403 )
        );
    }

    /**
     * Get paginated list of orders.
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     */
    public function get_items( $request ) {
        $page           = (int) $request->get_param( 'paged' ) === 0 ? 1 : absint( $request->get_param( 'paged' ) );
        $per_page       = (int) $request->get_param( 'per_page' ) === 0 ? 10 : absint( $request->get_param( 'per_page' ) );
        $payment_status = sanitize_text_field( $request->get_param( 'payment_status' ) );
        $payment_method = sanitize_text_field( $request->get_param( 'payment_method' ) );
        $order_date     = sanitize_text_field( $request->get_param( 'order_date' ) );
        $team_member    = absint( $request->get_param( 'team_member' ) );
        $service        = absint( $request->get_param( 'service' ) );
        $search         = sanitize_text_field( $request->get_param( 'search' ) );

        $db          = new Booktics_Database();
        $order_model = new Order_Model( $db );
        $conditions  = array();

        if ( $payment_status ) {
            $conditions['payment_status'] = $payment_status;
        }

        if ( $payment_method ) {
            $conditions['payment_method'] = $payment_method;
        }

        if ( $order_date ) {
            $conditions['DATE(created_at)'] = array( '=', $order_date );
        }

        if ( $search ) {
            $conditions['order_no'] = array( 'LIKE', '%' . $search . '%' );
        }

        $conditions = apply_filters( 'booktics_order_filters', $conditions );

        // Scope to customer's own orders when not admin/team-member
        if ( ! booktics_is_admin_or_team_member() && is_user_logged_in() ) {
            $guest = ( new Guest_Model( $db ) )->find( array( 'wp_user_id' => get_current_user_id() ) );
            if ( ! $guest ) {
                return $this->response(
                    array( 'items' => array(), 'total' => 0, 'total_pages' => 0 ),
                    __( 'Orders fetched successfully', 'booktics' )
                );
            }
            $conditions['customer_id'] = $guest->id;
        }

        // Scope to team member's assigned orders - only return orders that contain
        // at least one appointment where team_member_id equals the current user's team member ID
        $is_team_member_scope = false;
        $team_member_filter_id = null;
        if ( booktics_is_team_member() && ! current_user_can( 'manage_options' ) ) {
            $current_user_id = get_current_user_id();
            $team_member_model = new Team_Member_Model();
            $team_member = $team_member_model->find( array( 'wp_user_id' => $current_user_id ) );
            if ( $team_member ) {
                $is_team_member_scope = true;
                $team_member_filter_id = $team_member->id;
            }
        }

        $orders = $order_model->get(
            $conditions, $page, $per_page, array(
                'created_at',
                'DESC',
            )
        );

        if ( $team_member || $service ) {
            foreach ( $orders['items'] as $key => $order ) {
                $appointments = ( new Appointment_Model() )->where( 'order_id', $order->id );
                $keep_order   = false;

                foreach ( $appointments as $appointment ) {
                    if ( $team_member && $appointment->team_member_id == $team_member ) {
                        $keep_order = true;
                    }
                    if ( $service && $appointment->service_id == $service ) {
                        $keep_order = true;
                    }
                }

                if ( ! $keep_order && ( $team_member || $service ) ) {
                    unset( $orders['items'][ $key ] );
                    --$orders['total'];
                }
            }

            $orders['items']       = array_values( $orders['items'] );
            $orders['total_pages'] = ceil( $orders['total'] / $per_page );
        }

        foreach ( $orders['items'] as &$order ) {
            $customer              = ( new Guest_Model( $db ) )->find( array( 'id' => $order->customer_id ) );
            $customer_data_for_order = [
                'id'           => $customer ? $customer->id : null,
                'name'         => $customer ? $customer->name : '',
                'email'        => '',
                'phone'        => '',
            ];
            // Admins and team members see email and phone
            if ( booktics_is_admin_or_team_member() ) {
                $customer_data_for_order['email'] = $customer ? $customer->email : '';
                $customer_data_for_order['phone'] = $customer ? $customer->phone : '';
            }
            $order->customer       = $customer_data_for_order;
            $order->total_services = ( new Appointment_Model() )->get_appointments_count_by_order_id( $order->id );
            
            // Hide payment intent fields from team members (not admins)
            if ( booktics_is_team_member() && ! current_user_can( 'manage_options' ) ) {
                $order->payment_intent_id = '';
                $order->client_secret = '';
            }
        }

        return $this->response( $orders, __( 'Orders fetched successfully', 'booktics' ) );
    }

    /**
     * Check if user has permission to update order.
     *
     * @param WP_REST_Request $request
     *
     * @return bool
     */
    public function update_item_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Update an existing order.
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     */
    public function update_item( $request ) {
        $order_id    = absint( $request->get_param( 'id' ) );
        $data        = json_decode( $request->get_body(), true );
        $db          = new Booktics_Database();
        $order_model = new Order_Model( $db );
        $order       = $order_model->find( array( 'id' => $order_id ) );

        if ( ! $order ) {
            return $this->error( __( 'Order not found', 'booktics' ) );
        }

        $status         = isset( $data['status'] ) ? sanitize_text_field( $data['status'] ) : $order->status;
        $payment_status = isset( $data['payment_status'] ) ? sanitize_text_field( $data['payment_status'] ) : $order->payment_status;

        $order_model->update(
            $order_id, array(
                'status'         => $status,
                'payment_status' => $payment_status,
            )
        );

        if ( isset( $data['customer'] ) && isset( $data['customer']['id'] ) ) {
            $customer_id = absint( $data['customer']['id'] );
            $customer_data = $data['customer'];
            $customer_data['id'] = $customer_id;
            if ( isset( $customer_data['name'] ) ) {
                $customer_data['name'] = sanitize_text_field( $customer_data['name'] );
            }
            if ( isset( $customer_data['email'] ) ) {
                $customer_data['email'] = sanitize_email( $customer_data['email'] );
            }
            if ( isset( $customer_data['phone'] ) ) {
                $customer_data['phone'] = sanitize_text_field( $customer_data['phone'] );
            }
            ( new Guest_Model( $db ) )->update( $customer_id, $customer_data );
        }

        $order_model = $order_model->find( array( 'id' => $order_id ) );
        $data['customer_id'] = $order_model->customer_id;

        $response = $this->format_order_response($db, $order_model, $data, null);

        // Hide payment intent fields from team members (not admins)
        if ( booktics_is_team_member() && ! current_user_can( 'manage_options' ) ) {
            $response['payment_intent_id'] = '';
            $response['client_secret'] = '';
        }

        return $this->response( $response, __( 'Order updated successfully', 'booktics' ) );
    }

    /**
     * Check if user has permission to get order.
     *
     * Staff always allowed. Logged-in customers allowed when the order belongs to their guest record.
     * Anonymous users allowed only when the request includes a valid order_key matching the order (same
     * value returned when the order was created).
     *
     * @param WP_REST_Request $request
     *
     * @return bool|WP_Error
     */
    public function get_item_permission( $request ) {
        if ( booktics_is_admin_or_team_member() ) {
            return true;
        }

        $order_id = absint( $request->get_param( 'id' ) );
        $db       = new Booktics_Database();
        $order    = ( new Order_Model( $db ) )->find( array( 'id' => $order_id ) );

        if ( ! $order ) {
            return true;
        }

        if ( is_user_logged_in() ) {
            $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $order->customer_id ) );

            if ( $customer && intval( $customer->wp_user_id ) === get_current_user_id() ) {
                return true;
            }
        }

        $order_key = sanitize_text_field( (string) $request->get_param( 'order_key' ) );
        if ( $order_key && $this->verify_order_view_key( $order_id, (string) $order->order_no, $order_key ) ) {
            return true;
        }

        return new WP_Error(
            'booktics_forbidden',
            __( 'You are not allowed to view this order.', 'booktics' ),
            array( 'status' => 403 )
        );
    }

    /**
     * Non-secret, unguessable key for viewing an order without logging in (returned on create and with GET responses).
     *
     * @param int    $order_id
     * @param string $order_no
     *
     * @return string
     */
    private function get_order_view_key( $order_id, $order_no ) {
        return hash_hmac( 'sha256', (string) $order_id . '|' . $order_no, wp_salt( 'booktics_order_view' ) );
    }

    /**
     * @param int    $order_id
     * @param string $order_no
     * @param string $provided_key
     *
     * @return bool
     */
    private function verify_order_view_key( $order_id, $order_no, $provided_key ) {
        if ( '' === $provided_key || strlen( $provided_key ) > 128 ) {
            return false;
        }

        $expected = $this->get_order_view_key( $order_id, $order_no );

        return hash_equals( $expected, $provided_key );
    }

    /**
     * Get single order.
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     */
    public function get_item( $request ) {
        $order_id    = absint( $request->get_param( 'id' ) );
        $db          = new Booktics_Database();
        $order_model = new Order_Model( $db );
        $order       = $order_model->find( array( 'id' => $order_id ) );

        if ( ! $order ) {
            return $this->error( __( 'Order not found', 'booktics' ) );
        }

        // Use the format_order_response helper for consistency
        $input_data = array( 'customer_id' => $order->customer_id );
        $response   = $this->format_order_response( $db, $order, $input_data, array() );
        // Add extra fields specific to GET if needed
        $response['status']          = $order->status;
        $response['created_at']      = $this->format_datetime_to_dayjs_format( $order->created_at );
        $response['payment_gateway'] = $order->payment_method;

        // Hide payment intent fields from team members (not admins)
        if ( booktics_is_team_member() && ! current_user_can( 'manage_options' ) ) {
            $response['payment_intent_id'] = '';
            $response['client_secret'] = '';
        }

        return $this->response( $response, __( 'Order fetched successfully', 'booktics' ) );
    }

    /**
     * Check if user has permission to update payment status.
     *
     * @param WP_REST_Request $request
     *
     * @return bool
     */
    public function update_payment_status_permission( $request ) {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        // Allow order owner with a valid order_key (guest checkout support).
        $order_id  = absint( $request->get_param( 'id' ) );
        $order_key = sanitize_text_field( (string) $request->get_param( 'order_key' ) );

        if ( ! $order_id || ! $order_key ) {
            return false;
        }

        $db    = new Booktics_Database();
        $order = ( new Order_Model( $db ) )->find( array( 'id' => $order_id ) );

        if ( ! $order ) {
            return false;
        }

        return $this->verify_order_view_key( $order_id, (string) $order->order_no, $order_key );
    }

    /**
     * Update payment status for an order.
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     */
    public function update_payment_status( $request ) {
        $data           = json_decode( $request->get_body(), true );
        $order_id       = ! empty( $data['order_id'] ) ? intval( $data['order_id'] ) : 0;
        $payment_status = ! empty( $data['payment_status'] ) ? $data['payment_status'] : 0;
        $payment_method = ! empty( $data['payment_method'] ) ? $data['payment_method'] : null;

        $db          = new Booktics_Database();
        $order_model = new Order_Model( $db );
        $order       = $order_model->find( array( 'id' => $order_id ) );

        if ( ! $order ) {
            return $this->error( __( 'Order not found', 'booktics' ) );
        }

        if ( ! array_key_exists( $payment_method, ( new Payment_Method_Manager() )->get_payment_methods() ) ) {
            return $this->error( __( 'Unauthorized payment method', 'booktics' ) );
        }

        try {
            $payment_handler = ( new Payment_Method_Manager() )->get_payment_method( $payment_method );
            if ( ! $payment_handler ) {
                return $this->error( __( 'Payment method not found', 'booktics' ) );
            }

            $status = $payment_handler->get_payment_status( $order );
            if ( is_wp_error( $status ) ) {
                return $this->error( $status->get_error_message() );
            }

            $order_model->update(
                $order_id, array(
                    'payment_status' => $status,
                    'transaction_id' => $data['transaction_id'] ?? null,
                )
            );

            // Payment model status and transaction id update
            return $this->response(
                array(
                    'payment_status' => $status,
                ), __( 'Payment status updated successfully', 'booktics' )
            );
        } catch ( \Exception $e ) {
            
            if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
                error_log( 'An error occurred while deleting orders:' . $e->getMessage() );
            }
            return $this->error( __( 'An error occurred while deleting orders.', 'booktics' ) );
        }
    }

    /**
     * Check if user has permission to delete order.
     *
     * @param WP_REST_Request $request
     *
     * @return bool
     */
    public function delete_item_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Delete an order.
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     */
    public function delete_item( $request ) {
        $order_id    = absint( $request->get_param( 'id' ) );
        $db          = new Booktics_Database();
        $order_model = new Order_Model( $db );
        $order       = $order_model->find( array( 'id' => $order_id ) );

        if ( ! $order ) {
            return $this->error( __( 'Order not found', 'booktics' ) );
        }

        try {
            $db->begin_transaction();

            // Delete related appointments
            $appointment_model = new Appointment_Model();
            $appointments      = $appointment_model->where( 'order_id', $order_id );
            foreach ( $appointments as $appointment ) {
                $appointment->delete();
            }

            // Delete related payments
            $payment_model = new Payment_Model( $db );
            $payments      = $payment_model->get( array( 'order_id' => $order_id ) );
            foreach ( $payments['items'] as $payment ) {
                ( new Payment_Model( $db ) )->delete( $payment->id );
            }
            // Delete the order
            $order_model->delete( $order_id );

            $db->commit();

            return $this->response( array(), __( 'Order deleted successfully', 'booktics' ) );
        } catch ( Exception $e ) {
            $db->roll_back();

            return $this->error( $e->getMessage() );
        }
    }

    /**
     * Check if user has permission to bulk delete orders.
     *
     * @param WP_REST_Request $request
     *
     * @return bool
     */
    public function bulk_delete_items_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Bulk delete orders.
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     */
    public function bulk_delete_items( $request ) {
        $ids_param = $request->get_param( 'ids' );
        $order_ids = is_array( $ids_param ) ? array_map( 'absint', $ids_param ) : array();
        $db        = new Booktics_Database();

        try {
            $db->begin_transaction();

            foreach ( $order_ids as $order_id ) {
                $order_model = new Order_Model( $db );
                $order       = $order_model->find( array( 'id' => $order_id ) );

                if ( ! $order ) {
                    continue;
                }

                // Delete related appointments
                $appointment_model = new Appointment_Model();
                $appointments      = $appointment_model->where( 'order_id', $order_id );
                foreach ( $appointments as $appointment ) {
                    $appointment->delete();
                }

                // Delete related payments
                $payment_model = new Payment_Model( $db );
                $payments      = $payment_model->get( array( 'order_id' => $order_id ) );
                foreach ( $payments['items'] as $payment ) {
                    ( new Payment_Model( $db ) )->delete( $payment->id );
                }

                // Delete the order
                $order_model->delete( $order_id );
            }

            $db->commit();

            return $this->response( array(), __( 'Orders deleted successfully', 'booktics' ) );
        } catch ( Exception $e ) {
            $db->roll_back();

            return $this->error( $e->getMessage() );
        }
    }

    /**
     * Process payment refund for an order.
     *
     * @param WP_REST_Request $request
     *
     * @return WP_Error|WP_HTTP_Response
     */
    public function refund_payment( $request ) {
        $order_id    = absint( $request->get_param( 'id' ) );
        $db          = new Booktics_Database();
        $order_model = new Order_Model( $db );
        $order       = $order_model->find( array( 'id' => $order_id ) );

        $payment_method = ( new Payment_Method_Manager() )->get_payment_method( $order->payment_method );
        if ( ! $payment_method ) {
            return $this->error( __( 'Invalid payment method!', 'booktics' ) );
        }
        $response = $payment_method->process_refund( $order );
        if ( $response['status'] === 'error' ) {
            return $this->error( $response['message'] );
        }

        $order_model->update(
            $order_id, array(
                'payment_status' => Payment_Status::REFUNDED,
            )
        );

        return $this->response(
            $order_model->find( array( 'id' => $order_id ) ),
            __( 'Refund successful', 'booktics' )
        );
    }

    /**
     * Calculate total duration in minutes for a service appointment.
     *
     * @param array $item
     *
     * @return int
     */
    private function calculate_total_duration( $item ) {
        $service_model = Service_Model::find( $item['service_id'] );
        if ( ! $service_model ) {
            return 0;
        }
        $service_duration = array_filter(
            $service_model->additional_durations, function ( $duration ) use ( $item ) {
				return $item['additional_duration_id'] === $duration['id'];
			}
        );
        if ( empty( $service_duration ) ) {
            return 0;
        }
        $service_duration   = reset( $service_duration );
        $total_duration_min = $service_duration['duration_unit'] === 'min'
            ? $service_duration['duration_time']
            : ( $service_duration['duration_time'] * 60 );
        if ( ! empty( $item['extra_services'] ) ) {
            $total_duration_min = apply_filters( 'booktics_extra_service_duration', $total_duration_min, $item['extra_services'] );
        }

        $total_duration_min = apply_filters( 'booktics_calculate_total_duration', $total_duration_min, $item );

        return $total_duration_min;
    }

        /**
     * Converts a datetime string to ISO 8601 format with WordPress timezone for dayjs compatibility
     *
     * @param string $datetime Datetime string from database (e.g., "2024-01-15 14:00:00")
     *
     * @return string ISO 8601 formatted datetime with timezone offset (e.g., "2024-01-15T14:00:00-05:00")
     */
    private function format_datetime_to_dayjs_format( $datetime ) {
        $dt = new DateTime( $datetime );
        $dt->setTimezone( wp_timezone() );
        return $dt->format( 'Y-m-d H:i:s' );
    }
}
