<?php
/**
 * Availability REST Controller
 *
 * @package WahraRentalsBookings
 */

defined('ABSPATH') || exit;

/**
 * Availability controller class
 */
class WAHRB_Availability_Controller extends WAHRB_REST_Controller {

    /**
     * Route base
     *
     * @var string
     */
    protected $rest_base = 'availability';

    /**
     * Register routes
     */
    public function register_routes(): void {
        // Public endpoint: Check product availability.
        // Uses __return_true because customers need to check availability without authentication
        // before making a booking request. This only exposes public product availability data.
        register_rest_route($this->namespace, '/' . $this->rest_base, [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'check_availability'],
                'permission_callback' => '__return_true',
                'args' => $this->get_availability_args(),
            ],
        ]);

        // Public endpoint: Get calendar data for a product.
        // Uses __return_true because calendar data is displayed on public product pages
        // to show customers which dates are available for booking.
        register_rest_route($this->namespace, '/product/(?P<id>\d+)/calendar', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_calendar'],
                'permission_callback' => '__return_true',
                'args' => [
                    'id' => [
                        'description' => __('Product ID', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'month' => [
                        'description' => __('Month (1-12)', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'default' => (int) current_time('n'),
                    ],
                    'year' => [
                        'description' => __('Year', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'default' => (int) current_time('Y'),
                    ],
                ],
            ],
        ]);

        // Public endpoint: Create temporary hold on dates.
        // Uses __return_true to allow unauthenticated customers to temporarily reserve dates
        // during the booking process. Hold is session-based and expires automatically.
        register_rest_route($this->namespace, '/hold', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_hold'],
                'permission_callback' => '__return_true',
                'args' => $this->get_hold_args(),
            ],
        ]);

        // Public endpoint: Release a temporary hold.
        // Uses __return_true but requires valid session_token for authorization.
        // Only the session that created the hold can release it.
        register_rest_route($this->namespace, '/hold/(?P<id>\d+)', [
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'release_hold'],
                'permission_callback' => '__return_true',
                'args' => [
                    'id' => [
                        'description' => __('Hold ID', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'session_token' => [
                        'description' => __('Session token', 'wahra-rentals-bookings'),
                        'type' => 'string',
                        'required' => true,
                    ],
                ],
            ],
        ]);

        // Public endpoint: Revalidate hold before checkout.
        // Uses __return_true but requires valid session_token for authorization.
        // Extends the hold during the checkout process.
        register_rest_route($this->namespace, '/revalidate', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'revalidate'],
                'permission_callback' => '__return_true',
                'args' => [
                    'hold_id' => [
                        'description' => __('Hold ID', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'session_token' => [
                        'description' => __('Session token', 'wahra-rentals-bookings'),
                        'type' => 'string',
                        'required' => true,
                    ],
                ],
            ],
        ]);

        // Public endpoint: Get blocked dates for calendar UI.
        // Uses __return_true because this data is needed for the public booking calendar
        // to disable unavailable dates in the date picker.
        register_rest_route($this->namespace, '/blocked-dates', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_blocked_dates'],
                'permission_callback' => '__return_true',
                'args' => [
                    'product_id' => [
                        'description' => __('Product ID', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'months' => [
                        'description' => __('Number of months to check', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'default' => 6,
                    ],
                ],
            ],
        ]);

        // Vendor: Get blackout dates
        register_rest_route($this->namespace, '/vendor/blackouts', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_blackouts'],
                'permission_callback' => [$this, 'check_vendor_permission'],
                'args' => [
                    'product_id' => [
                        'description' => __('Product ID', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                ],
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'add_blackout'],
                'permission_callback' => [$this, 'check_vendor_permission'],
                'args' => $this->get_blackout_args(),
            ],
        ]);

        // Vendor: Delete blackout
        register_rest_route($this->namespace, '/vendor/blackouts/(?P<id>\d+)', [
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_blackout'],
                'permission_callback' => [$this, 'check_vendor_permission'],
            ],
        ]);

        // Public endpoint: Get available time slots for a specific date.
        // Uses __return_true because customers need to see available time slots
        // when booking slot-based or service-based products.
        register_rest_route($this->namespace, '/time-slots', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_time_slots'],
                'permission_callback' => '__return_true',
                'args' => [
                    'product_id' => [
                        'description' => __('Product ID', 'wahra-rentals-bookings'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'date' => [
                        'description' => __('Date (Y-m-d)', 'wahra-rentals-bookings'),
                        'type' => 'string',
                        'required' => true,
                    ],
                    'mode' => [
                        'description' => __('Booking mode (slot or service)', 'wahra-rentals-bookings'),
                        'type' => 'string',
                        'default' => 'slot',
                    ],
                ],
            ],
        ]);
    }

    /**
     * Get availability check arguments
     *
     * @return array
     */
    private function get_availability_args(): array {
        return [
            'product_id' => [
                'description' => __('Product ID', 'wahra-rentals-bookings'),
                'type' => 'integer',
                'required' => true,
            ],
            'variation_id' => [
                'description' => __('Variation ID', 'wahra-rentals-bookings'),
                'type' => 'integer',
                'default' => 0,
            ],
            'start_date' => [
                'description' => __('Start date (Y-m-d)', 'wahra-rentals-bookings'),
                'type' => 'string',
                'required' => true,
            ],
            'end_date' => [
                'description' => __('End date (Y-m-d)', 'wahra-rentals-bookings'),
                'type' => 'string',
                'required' => true,
            ],
            'qty' => [
                'description' => __('Quantity', 'wahra-rentals-bookings'),
                'type' => 'integer',
                'default' => 1,
            ],
        ];
    }

    /**
     * Get hold arguments
     *
     * @return array
     */
    private function get_hold_args(): array {
        return [
            'product_id' => [
                'description' => __('Product ID', 'wahra-rentals-bookings'),
                'type' => 'integer',
                'required' => true,
            ],
            'variation_id' => [
                'description' => __('Variation ID', 'wahra-rentals-bookings'),
                'type' => 'integer',
                'default' => 0,
            ],
            'start_date' => [
                'description' => __('Start date (Y-m-d)', 'wahra-rentals-bookings'),
                'type' => 'string',
                'required' => true,
            ],
            'end_date' => [
                'description' => __('End date (Y-m-d)', 'wahra-rentals-bookings'),
                'type' => 'string',
                'required' => true,
            ],
            'qty' => [
                'description' => __('Quantity', 'wahra-rentals-bookings'),
                'type' => 'integer',
                'default' => 1,
            ],
        ];
    }

    /**
     * Get blackout arguments
     *
     * @return array
     */
    private function get_blackout_args(): array {
        return [
            'product_id' => [
                'description' => __('Product ID', 'wahra-rentals-bookings'),
                'type' => 'integer',
                'required' => true,
            ],
            'start_date' => [
                'description' => __('Start date (Y-m-d)', 'wahra-rentals-bookings'),
                'type' => 'string',
                'required' => true,
            ],
            'end_date' => [
                'description' => __('End date (Y-m-d)', 'wahra-rentals-bookings'),
                'type' => 'string',
                'required' => true,
            ],
            'reason' => [
                'description' => __('Reason', 'wahra-rentals-bookings'),
                'type' => 'string',
                'default' => '',
            ],
        ];
    }

    /**
     * Check availability
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function check_availability($request) {
        $product_id = absint($request->get_param('product_id'));
        $variation_id = absint($request->get_param('variation_id'));
        $start_date = $this->sanitize_date($request->get_param('start_date'));
        $end_date = $this->sanitize_date($request->get_param('end_date'));
        $qty = absint($request->get_param('qty')) ?: 1;

        if (!$start_date || !$end_date) {
            return $this->error('invalid_dates', __('Invalid dates.', 'wahra-rentals-bookings'));
        }

        $result = WAHRB_Availability::check($product_id, $start_date, $end_date, $variation_id ?: null, $qty);

        return $this->success($result);
    }

    /**
     * Get calendar data
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function get_calendar($request) {
        $product_id = absint($request->get_param('id'));
        $month = absint($request->get_param('month'));
        $year = absint($request->get_param('year'));

        $product = wc_get_product($product_id);
        if (!$product) {
            return $this->error('product_not_found', __('Product not found.', 'wahra-rentals-bookings'), 404);
        }

        $start_date = sprintf('%04d-%02d-01', $year, $month);
        $calendar_data = WAHRB_Availability::get_calendar_data($product_id, $start_date, 1);

        return $this->success($calendar_data);
    }

    /**
     * Create hold
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function create_hold($request) {
        $product_id = absint($request->get_param('product_id'));
        $variation_id = absint($request->get_param('variation_id'));
        $start_date = $this->sanitize_date($request->get_param('start_date'));
        $end_date = $this->sanitize_date($request->get_param('end_date'));
        $qty = absint($request->get_param('qty')) ?: 1;

        if (!$start_date || !$end_date) {
            return $this->error('invalid_dates', __('Invalid dates.', 'wahra-rentals-bookings'));
        }

        // Rate limiting: max 10 hold attempts per minute per IP
        $ip = sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'] ?? ''));
        $rate_key = 'wahrb_hold_rate_' . md5($ip);
        $attempts = (int) get_transient($rate_key);
        if ($attempts >= 10) {
            return $this->error('rate_limited', __('Too many requests. Please try again later.', 'wahra-rentals-bookings'), 429);
        }
        set_transient($rate_key, $attempts + 1, 60);

        // Check availability first
        $check = WAHRB_Availability::check($product_id, $start_date, $end_date, $variation_id ?: null, $qty);

        if (!$check['available']) {
            return $this->error('not_available', $check['reason'] ?? __('Not available.', 'wahra-rentals-bookings'));
        }

        // Create hold
        $hold = WAHRB_Hold::create([
            'product_id' => $product_id,
            'variation_id' => $variation_id,
            'start_dt' => $start_date,
            'end_dt' => $end_date,
            'qty' => $qty,
            'user_id' => get_current_user_id() ?: null,
        ]);

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

        return $this->success([
            'hold_id' => $hold->get_id(),
            'session_token' => $hold->get('session_token'),
            'expires_at' => $hold->get('expires_at'),
            'message' => __('Dates temporarily reserved.', 'wahra-rentals-bookings'),
        ], 201);
    }

    /**
     * Release hold
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function release_hold($request) {
        $hold_id = absint($request->get_param('id'));
        $session_token = sanitize_text_field($request->get_param('session_token'));

        $hold = new WAHRB_Hold($hold_id);

        if (!$hold->exists()) {
            return $this->error('hold_not_found', __('Temporary reservation not found.', 'wahra-rentals-bookings'), 404);
        }

        if ($hold->get('session_token') !== $session_token) {
            return $this->error('invalid_token', __('Invalid session token.', 'wahra-rentals-bookings'), 403);
        }

        $hold->release();

        return $this->success([
            'message' => __('Temporary reservation cancelled.', 'wahra-rentals-bookings'),
        ]);
    }

    /**
     * Revalidate hold before checkout
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function revalidate($request) {
        $hold_id = absint($request->get_param('hold_id'));
        $session_token = sanitize_text_field($request->get_param('session_token'));

        $hold = new WAHRB_Hold($hold_id);

        if (!$hold->exists()) {
            return $this->error('hold_not_found', __('Temporary reservation not found.', 'wahra-rentals-bookings'), 404);
        }

        if ($hold->get('session_token') !== $session_token) {
            return $this->error('invalid_token', __('Invalid session token.', 'wahra-rentals-bookings'), 403);
        }

        if (!$hold->is_valid()) {
            return $this->error('hold_expired', __('Your temporary reservation has expired. Please try again.', 'wahra-rentals-bookings'));
        }

        // Extend hold for checkout
        $hold->extend(15); // 15 more minutes for checkout

        return $this->success([
            'valid' => true,
            'expires_at' => $hold->get('expires_at'),
            'message' => __('Temporary reservation validated.', 'wahra-rentals-bookings'),
        ]);
    }

    /**
     * Get blackout dates
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function get_blackouts($request) {
        $product_id = absint($request->get_param('product_id'));
        $vendor_id = $this->get_vendor_id();

        // Check vendor owns product
        $product = wc_get_product($product_id);
        if (!$product) {
            return $this->error('product_not_found', __('Product not found.', 'wahra-rentals-bookings'), 404);
        }

        if (!$this->is_admin() && (int) get_post_field('post_author', $product_id) !== $vendor_id) {
            return $this->error('forbidden', __('You cannot access this product.', 'wahra-rentals-bookings'), 403);
        }

        global $wpdb;
        $db = WAHRB_Database::instance();

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom blackout_dates table
        $blackouts = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$db->table('blackout_dates')} WHERE product_id = %d AND deleted_at IS NULL ORDER BY date_start ASC", $product_id), ARRAY_A);

        return $this->success($blackouts ?: []);
    }

    /**
     * Add blackout date
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function add_blackout($request) {
        $product_id = absint($request->get_param('product_id'));
        $start_date = $this->sanitize_date($request->get_param('start_date'));
        $end_date = $this->sanitize_date($request->get_param('end_date'));
        $reason = sanitize_text_field($request->get_param('reason'));
        $vendor_id = $this->get_vendor_id();

        if (!$start_date || !$end_date) {
            return $this->error('invalid_dates', __('Invalid dates.', 'wahra-rentals-bookings'));
        }

        // Check vendor owns product
        $product = wc_get_product($product_id);
        if (!$product) {
            return $this->error('product_not_found', __('Product not found.', 'wahra-rentals-bookings'), 404);
        }

        if (!$this->is_admin() && (int) get_post_field('post_author', $product_id) !== $vendor_id) {
            return $this->error('forbidden', __('You cannot modify this product.', 'wahra-rentals-bookings'), 403);
        }

        global $wpdb;
        $db = WAHRB_Database::instance();

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom blackout_dates table insert
        $wpdb->insert(
            $db->table('blackout_dates'),
            [
                'vendor_id' => $vendor_id,
                'product_id' => $product_id,
                'date_start' => $start_date,
                'date_end' => $end_date,
                'reason' => $reason,
                'created_at' => current_time('mysql'),
            ],
            ['%d', '%d', '%s', '%s', '%s', '%s']
        );

        $blackout_id = $wpdb->insert_id;

        if (!$blackout_id) {
            return $this->error('insert_failed', __('Error adding blocked dates.', 'wahra-rentals-bookings'), 500);
        }

        return $this->success([
            'id' => $blackout_id,
            'message' => __('Dates blocked successfully.', 'wahra-rentals-bookings'),
        ], 201);
    }

    /**
     * Delete blackout date
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function delete_blackout($request) {
        $blackout_id = absint($request->get_param('id'));
        $vendor_id = $this->get_vendor_id();

        global $wpdb;
        $db = WAHRB_Database::instance();

        // Get blackout
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom blackout_dates table
        $blackout = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$db->table('blackout_dates')} WHERE id = %d AND deleted_at IS NULL", $blackout_id));

        if (!$blackout) {
            return $this->error('not_found', __('Blocked date not found.', 'wahra-rentals-bookings'), 404);
        }

        if (!$this->is_admin() && (int) $blackout->vendor_id !== $vendor_id) {
            return $this->error('forbidden', __('You cannot delete this blocked date.', 'wahra-rentals-bookings'), 403);
        }

        // Soft delete
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table soft delete
        $wpdb->update(
            $db->table('blackout_dates'),
            ['deleted_at' => current_time('mysql')],
            ['id' => $blackout_id]
        );

        return $this->success([
            'message' => __('Date unblocked successfully.', 'wahra-rentals-bookings'),
        ]);
    }

    /**
     * Get all blocked dates for a product (for Flatpickr)
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function get_blocked_dates($request) {
        global $wpdb;
        $db = WAHRB_Database::instance();

        $product_id = (int) $request->get_param('product_id');
        $months = (int) $request->get_param('months') ?: 6;

        $start_date = current_time('Y-m-d');
        $end_date = gmdate('Y-m-d', strtotime("+{$months} months"));

        $blocked_dates = [];

        // 1. Get blackout dates
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom blackout_dates table
        $blackouts = $wpdb->get_results($wpdb->prepare("SELECT date_start, date_end FROM {$db->table('blackout_dates')} WHERE product_id = %d AND deleted_at IS NULL AND date_end >= %s AND date_start <= %s", $product_id, $start_date, $end_date));

        foreach ($blackouts as $blackout) {
            $current = new DateTime($blackout->date_start);
            $end = new DateTime($blackout->date_end);
            while ($current <= $end) {
                $blocked_dates[$current->format('Y-m-d')] = true;
                $current->modify('+1 day');
            }
        }

        // 2. Get fully booked dates from daily_availability (if index exists)
        $total_units = (int) get_post_meta($product_id, '_wahrb_total_units', true) ?: 1;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom daily_availability table
        $fully_booked = $wpdb->get_col($wpdb->prepare("SELECT availability_date FROM {$db->table('daily_availability')} WHERE product_id = %d AND availability_date BETWEEN %s AND %s AND (available_units = 0 OR is_blackout = 1)", $product_id, $start_date, $end_date));

        foreach ($fully_booked as $date) {
            $blocked_dates[$date] = true;
        }

        // 3. Get dates from bookings directly (fallback if daily index not populated)
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom bookings table
        $bookings = $wpdb->get_results($wpdb->prepare("SELECT DATE(start_dt) as start_date, DATE(end_dt) as end_date, COALESCE(SUM(qty), 0) as booked_qty FROM {$db->table('bookings')} WHERE product_id = %d AND status IN ('confirmed', 'checked_in', 'checked_out', 'completed') AND deleted_at IS NULL AND DATE(end_dt) >= %s AND DATE(start_dt) <= %s GROUP BY DATE(start_dt), DATE(end_dt)", $product_id, $start_date, $end_date));

        // For bookings that take all units, block the full range
        foreach ($bookings as $booking) {
            if ((int) $booking->booked_qty >= $total_units) {
                $current = new DateTime($booking->start_date);
                $end = new DateTime($booking->end_date);
                while ($current < $end) {
                    $blocked_dates[$current->format('Y-m-d')] = true;
                    $current->modify('+1 day');
                }
            }
        }

        // Return unique sorted dates
        $dates = array_keys($blocked_dates);
        sort($dates);

        return $this->success([
            'blocked_dates' => $dates,
            'product_id' => $product_id,
            'range' => [
                'start' => $start_date,
                'end' => $end_date,
            ],
        ]);
    }

    /**
     * Get available time slots for a specific date (slot/service mode)
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function get_time_slots($request) {
        $product_id = absint($request->get_param('product_id'));
        $date = $this->sanitize_date($request->get_param('date'));
        $mode = sanitize_text_field($request->get_param('mode')) ?: 'slot';

        if (!$date) {
            return $this->error('invalid_date', __('Invalid date.', 'wahra-rentals-bookings'));
        }

        $product = wc_get_product($product_id);
        if (!$product) {
            return $this->error('product_not_found', __('Product not found.', 'wahra-rentals-bookings'), 404);
        }

        // Get product settings
        $business_hours_start = get_post_meta($product_id, '_wahrb_business_hours_start', true) ?: '09:00';
        $business_hours_end = get_post_meta($product_id, '_wahrb_business_hours_end', true) ?: '18:00';
        
        if ($mode === 'slot') {
            $slot_duration = (int) get_post_meta($product_id, '_wahrb_slot_duration_minutes', true) ?: 60;
        } else {
            $slot_duration = (int) get_post_meta($product_id, '_wahrb_service_duration_minutes', true) ?: 60;
        }
        
        $capacity = (int) get_post_meta($product_id, '_wahrb_capacity_per_slot', true) ?: 1;
        $total_units = (int) get_post_meta($product_id, '_wahrb_total_units', true) ?: 1;

        // Generate time slots
        $slots = [];
        $current = new DateTime($date . ' ' . $business_hours_start);
        $end_of_day = new DateTime($date . ' ' . $business_hours_end);
        $now = new DateTime();

        while ($current < $end_of_day) {
            $slot_end = clone $current;
            $slot_end->modify("+{$slot_duration} minutes");

            if ($slot_end > $end_of_day) {
                break; // Slot would exceed business hours
            }

            $start_time = $current->format('H:i');
            $end_time = $slot_end->format('H:i');
            $slot_datetime = new DateTime($date . ' ' . $start_time);

            // Check if slot is in the past
            $is_past = $slot_datetime < $now;

            // Check availability for this slot
            $is_available = !$is_past && $this->is_slot_available(
                $product_id,
                $date,
                $start_time,
                $end_time,
                $mode,
                $capacity,
                $total_units
            );

            if ($mode === 'slot') {
                $slots[] = [
                    'start' => $start_time,
                    'end' => $end_time,
                    'available' => $is_available,
                    'price' => (float) get_post_meta($product_id, '_wahrb_price_per_day', true) ?: 0,
                ];
            } else {
                // Service mode - show spots left
                $spots_left = $is_available ? $this->get_spots_left($product_id, $date, $start_time, $capacity) : 0;
                $slots[] = [
                    'time' => $start_time,
                    'duration' => $slot_duration,
                    'available' => $is_available,
                    'spots_left' => $spots_left,
                ];
            }

            $current->modify("+{$slot_duration} minutes");
        }

        return $this->success([
            'slots' => $slots,
            'date' => $date,
            'mode' => $mode,
            'business_hours' => [
                'start' => $business_hours_start,
                'end' => $business_hours_end,
            ],
        ]);
    }

    /**
     * Check if a specific time slot is available
     *
     * @param int $product_id Product ID
     * @param string $date Date (Y-m-d)
     * @param string $start_time Start time (H:i)
     * @param string $end_time End time (H:i)
     * @param string $mode Booking mode
     * @param int $capacity Capacity per slot
     * @param int $total_units Total units
     * @return bool
     */
    private function is_slot_available($product_id, $date, $start_time, $end_time, $mode, $capacity, $total_units) {
        global $wpdb;
        $db = WAHRB_Database::instance();

        $start_dt = $date . ' ' . $start_time . ':00';
        $end_dt = $date . ' ' . $end_time . ':00';

        // Count overlapping bookings
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
        $booked_count = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COALESCE(SUM(qty), 0) FROM {$db->table('bookings')} 
            WHERE product_id = %d 
            AND status IN ('confirmed', 'checked_in', 'checked_out', 'completed') 
            AND deleted_at IS NULL
            AND start_dt < %s 
            AND end_dt > %s",
            $product_id,
            $end_dt,
            $start_dt
        ));

        if ($mode === 'service') {
            return $booked_count < $capacity;
        }

        return $booked_count < $total_units;
    }

    /**
     * Get remaining spots for a service slot
     *
     * @param int $product_id Product ID
     * @param string $date Date
     * @param string $start_time Start time
     * @param int $capacity Total capacity
     * @return int
     */
    private function get_spots_left($product_id, $date, $start_time, $capacity) {
        global $wpdb;
        $db = WAHRB_Database::instance();

        $slot_duration = (int) get_post_meta($product_id, '_wahrb_service_duration_minutes', true) ?: 60;
        $start_dt = $date . ' ' . $start_time . ':00';
        $end_time = new DateTime($date . ' ' . $start_time);
        $end_time->modify("+{$slot_duration} minutes");
        $end_dt = $end_time->format('Y-m-d H:i:s');

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
        $booked_count = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COALESCE(SUM(qty), 0) FROM {$db->table('bookings')} 
            WHERE product_id = %d 
            AND status IN ('confirmed', 'checked_in', 'checked_out', 'completed') 
            AND deleted_at IS NULL
            AND start_dt < %s 
            AND end_dt > %s",
            $product_id,
            $end_dt,
            $start_dt
        ));

        return max(0, $capacity - $booked_count);
    }
}
