<template>
  <div id="bookaro-booking-app" class="bookaro-scope">
    <div v-show="visible" class="bookaro-booking-modal" @click.self="closeModal">
      <div
        class="bookaro-booking-wrapper"
        :class="{ 'is-compact': isCompact, 'is-expanded': !isCompact }"
        ref="bookingWrapper"
      >
          <div class="mb-4" v-if="showPrice">
            <strong>{{ i18n.price_label }}:</strong> {{ formattedPrice }}
          </div>
          <div v-if="showServiceSelect" class="form-group">
            <label for="bookaro-service-select">{{ i18n.service_name }}</label>
            <select
                id="bookaro-service-select"
                v-model="selected_service_id"
                @change="onServiceChange"
                class="w-full px-4 py-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
            >
              <option value="">{{ i18n.none_option }}</option>
              <option v-for="service in services" :key="service.id" :value="service.id">
                {{ service.title }}
              </option>
            </select>
            <div v-if="servicesLoading" class="text-sm text-gray-500">{{ i18n.loading }}</div>
            <div v-else-if="servicesError" class="error">{{ servicesError }}</div>
            <div v-else-if="!services.length" class="text-sm text-gray-500">
              {{ i18n.no_services_msg }}
            </div>
          </div>
      <!-- Overlay spinner when any loading flag is true -->
      <div v-if="loading || availLoading" class="modal-loader">
        <div class="spinner"></div>
      </div>

      <!-- Close button -->
      <button class="modal-close-button" @click="closeModal">&times;</button>

      <!-- STEP CONTENT: render the correct component based on `step` -->
      <component
          :is="currentStepComponent"
          v-bind="currentStepProps"
          @advance="handleAdvance"
          @back="handleBack"
          @submit-booking="submitBooking"
      >
        <template v-if="showPolicyNotice" #after-calendar>
          <div class="mb-4 p-3 rounded-md border border-gray-200 bg-white text-xs text-gray-600">
            <div class="font-semibold text-gray-700 mb-1">{{ policyTitle }}</div>
            <ul class="list-disc ml-4 space-y-1">
              <li v-for="(line, idx) in policyLines" :key="`policy-${idx}`">
                {{ line }}
              </li>
            </ul>
          </div>
        </template>
      </component>
    </div>
    </div>
  </div>
</template>

<script>
import $ from 'jquery';
import { restRequest } from '@bookaro-free/js/rest';

import LoginPrompt    from '@bookaro-free/components/myListingBookingPopUp/LoginPrompt.vue';
import DateTimeStep   from '@bookaro-free/components/myListingBookingPopUp/DateTimeStep.vue';
import UserInfoStep   from '@bookaro-free/components/myListingBookingPopUp/UserInfoStep.vue';
import SuccessStep    from '@bookaro-free/components/myListingBookingPopUp/SuccessStep.vue';
import FailureStep    from '@bookaro-free/components/myListingBookingPopUp/FailureStep.vue';

export default {
  name: 'BookingApp',
  components: {
    LoginPrompt,
    DateTimeStep,
    UserInfoStep,
    SuccessStep,
    FailureStep
  },
  data() {
    return {
      visible: false,
      step: 0, // 0=login, 1=date-time, 2=user-info, 3=success, 4=failure

      // Context
      listing_id:   BookaroData.listing_id || null,
      provider_id:  BookaroData.provider_id || null,
      listing_price: BookaroData.listing_price || 0,
      listing_price_formatted: BookaroData.listing_price_formatted || '',
      service_label: BookaroData.service_label || BookaroData.listing_title || '',
      service_id: 0,
      service_token: '',
      service_duration: BookaroData.service_duration || 0,
      services: Array.isArray(BookaroData.services) ? BookaroData.services : [],
      selected_service_id: '',
      servicesLoading: false,
      servicesError: '',

      // Combined availability from REST
      availability: {
        weekly:     {},
        timeslots:  [],
        exceptions: {},
        bookedSlots:{},
        available:  {},
        slots:      1,
      },
      availLoading: false,
      availError:   '',

      // Date/time selection
      currentMonth: window.dayjs().startOf('month'),
      selected_date:     '',
      selected_date_to:  '',
      available_times:   [],
      selected_time:     '',


      // User info (step 2)
      user_name:  BookaroData.user?.name  || '',
      user_email: BookaroData.user?.email || '',
      user_phone: BookaroData.user?.phone || '',
      hasTimeslots: !!BookaroData.has_timeslots,

      // UI state
      loading:   false,
      error:     '',
      i18n:      BookaroData.i18n  || {},
      currency:  BookaroData.currency || {},
      login_url: BookaroData.login_url,
      isLoggedIn: !!BookaroData.is_logged_in,
      // Bug 54: surface the server's require_login setting so the modal
      // skips the login-gate step when the admin has flipped it off.
      // Default true (require login) to stay safe when the field is
      // missing — older BookaroData payloads from cached HTML pre-Bug-54
      // shouldn't suddenly open the anonymous path.
      requireLogin: BookaroData.require_login === undefined
        ? true
        : !!BookaroData.require_login,
      dashboard_url: BookaroData.dashboard_url || '',
      booking_id: 0,
      booking_status: '',
      booking_notes: '',
      hasPickedDate: false,
      booking_form_started_at: 0,
      booking_honeypot: ''
    };
  },
  computed: {
    isCompact() {
      return !this.hasPickedDate || !this.selected_date;
    },
    usesBookaroServices() {
      const source = BookaroData?.services_source || '';
      const hasLocalServices = Array.isArray(BookaroData?.services) && BookaroData.services.length > 0;
      return source === 'bookaro' || hasLocalServices;
    },
    showServiceSelect() {
      return this.step === 1 && this.usesBookaroServices;
    },
    showPrice() {
      const price = Number(this.listing_price);
      if (Number.isNaN(price)) {
        return false;
      }
      if (this.usesBookaroServices && !this.selected_service_id) {
        return false;
      }
      return true;
    },
    policiesAvailable() {
      return BookaroData && Object.prototype.hasOwnProperty.call(BookaroData, 'policies');
    },
    policies() {
      return BookaroData?.policies || {};
    },
    cancelWindowHours() {
      const hours = Number(this.policies.cancel_window_hours ?? 0);
      return Number.isFinite(hours) ? hours : 0;
    },
    rescheduleWindowHours() {
      const hours = Number(this.policies.reschedule_window_hours ?? 0);
      return Number.isFinite(hours) ? hours : 0;
    },
    policyTitle() {
      return this.i18n.policy_title;
    },
    cancelPolicyText() {
      const custom = typeof this.policies?.cancel_text === 'string' ? this.policies.cancel_text.trim() : '';
      if (custom) {
        return this.applyPolicyTemplate(custom, this.cancelWindowHours);
      }
      if (this.cancelWindowHours > 0) {
        const template = this.i18n.policy_cancel_window;
        return template.replace('%d', String(this.cancelWindowHours));
      }
      return this.i18n.policy_cancel_anytime;
    },
    reschedulePolicyText() {
      const custom = typeof this.policies?.reschedule_text === 'string' ? this.policies.reschedule_text.trim() : '';
      if (custom) {
        return this.applyPolicyTemplate(custom, this.rescheduleWindowHours);
      }
      if (this.rescheduleWindowHours > 0) {
        const template = this.i18n.policy_reschedule_window;
        return template.replace('%d', String(this.rescheduleWindowHours));
      }
      return this.i18n.policy_reschedule_anytime;
    },
    policyLines() {
      if (!this.policiesAvailable) {
        return [];
      }
      return [this.cancelPolicyText, this.reschedulePolicyText];
    },
    showPolicyNotice() {
      return this.policyLines.length > 0 && this.step < 3;
    },
    phoneRequired() {
      if (typeof BookaroData?.phone_required === 'undefined') {
        return true;
      }
      return !!BookaroData.phone_required;
    },
    userInfoComplete() {
      const nameOk = String(this.user_name || '').trim().length > 0;
      const emailOk = String(this.user_email || '').trim().length > 0;
      const phoneOk = !this.phoneRequired || String(this.user_phone || '').trim().length > 0;
      return nameOk && emailOk && phoneOk;
    },
    manageBookingUrl() {
      return this.dashboard_url || '';
    },
    bookingStatusLabel() {
      const status = String(this.booking_status || '').toLowerCase();
      if (!status) {
        return '';
      }
      const map = {
        pending: this.i18n.status_pending,
        approved: this.i18n.status_approved,
        rejected: this.i18n.status_rejected,
        cancelled: this.i18n.status_cancelled,
        completed: this.i18n.status_completed,
        no_show: this.i18n.status_no_show
      };
      return map[status] || this.i18n.status_unknown || '';
    },
    bookingDateTimeLabel() {
      if (!this.selected_date) {
        return '';
      }
      const start = window.dayjs(`${this.selected_date} ${this.selected_time || '00:00'}`.trim());
      if (!start.isValid()) {
        return '';
      }
      const format = `${this.i18n.date_format || 'DD/MM/YY'} ${this.i18n.time_format || 'HH:mm'}`;
      let label = start.format(format);
      if (this.selected_date_to) {
        const end = window.dayjs(this.selected_date_to);
        if (end.isValid()) {
          label = `${label} -> ${end.format(format)}`;
        }
      }
      return label;
    },
    bookingDurationLabel() {
      const duration = Number(this.service_duration || 0);
      if (!Number.isFinite(duration) || duration <= 0) {
        return '';
      }
      if (duration >= 1440 && duration % 1440 === 0) {
        const days = duration / 1440;
        const unit = days === 1 ? this.i18n.day_unit : this.i18n.days_unit;
        return unit ? `${days} ${unit}` : String(days);
      }
      const hours = Math.floor(duration / 60);
      const mins = duration % 60;
      const hourLabel = this.i18n.hour_short || '';
      const minuteLabel = this.i18n.minute_short || '';
      if (hours > 0 && mins > 0) {
        return `${hours}${hourLabel} ${mins}${minuteLabel}`.trim();
      }
      if (hours > 0) {
        return `${hours}${hourLabel}`.trim();
      }
      return `${mins}${minuteLabel}`.trim();
    },
    bookingDetails() {
      const details = [];
      const listingTitle = BookaroData?.listing_title || '';
      const listingUrl = this.withBookingAnchor(BookaroData?.listing_url || '');
      const provider = BookaroData?.provider || {};
      const location = BookaroData?.listing_location || '';

      if (this.bookingStatusLabel) {
        details.push({ label: this.i18n.booking_detail_status, value: this.bookingStatusLabel });
      }
      if (this.service_label) {
        details.push({ label: this.i18n.booking_detail_service, value: this.service_label });
      }
      if (this.bookingDateTimeLabel) {
        details.push({ label: this.i18n.booking_detail_date_time, value: this.bookingDateTimeLabel });
      }
      if (this.bookingDurationLabel) {
        details.push({ label: this.i18n.booking_detail_duration, value: this.bookingDurationLabel });
      }
      if (listingTitle) {
        details.push({ label: this.i18n.booking_detail_listing, value: listingTitle, url: listingUrl });
      }
      if (location) {
        details.push({ label: this.i18n.booking_detail_location, value: location });
      }
      if (provider?.name) {
        details.push({ label: this.i18n.booking_detail_provider, value: provider.name });
      }
      if (provider?.email) {
        details.push({ label: this.i18n.booking_detail_provider_email, value: provider.email });
      }
      if (provider?.phone) {
        details.push({ label: this.i18n.booking_detail_provider_phone, value: provider.phone });
      }
      if (this.booking_notes) {
        details.push({ label: this.i18n.booking_detail_notes, value: this.booking_notes });
      }
      return details;
    },
    formattedPrice() {
      if (this.listing_price_formatted) {
        return this.listing_price_formatted;
      }
      return this.formatPrice(this.listing_price);
    },
    displayServiceLabel() {
      if (this.service_label) {
        return this.service_label;
      }
      return BookaroData.listing_title || '';
    },
    monthLabel() {
      const idx = this.currentMonth.month();
      const fullMonth = Array.isArray(this.i18n.months) ? this.i18n.months[idx] : '';
      const shortMonth = Array.isArray(this.i18n.months_short) ? this.i18n.months_short[idx] : '';
      const year = this.currentMonth.format('YYYY');
      const format = this.i18n.month_year_format || 'MMMM YYYY';
      let label = format;
      if (format.includes('MMMM')) {
        label = label.replace('MMMM', fullMonth || '');
      } else if (format.includes('MMM')) {
        label = label.replace('MMM', shortMonth || '');
      }
      label = label.replace('YYYY', year);
      return label.trim();
    },
    monthDays() {
      const start  = this.currentMonth.startOf('month').startOf('week');
      const end    = this.currentMonth.endOf('month').endOf('week');
      let cursor   = start.clone();
      const days   = [];
      while (cursor.isBefore(end) || cursor.isSame(end, 'day')) {
        days.push(cursor.clone());
        cursor = cursor.add(1, 'day');
      }
      return days;
    },
    currentStepComponent() {
      switch (this.step) {
        case 0: return 'LoginPrompt';
        case 1: return 'DateTimeStep';
        case 2: return 'UserInfoStep';
        case 3: return 'SuccessStep';
        case 4: return 'FailureStep';
        default: return 'div';
      }
    },
    currentStepProps() {
      // Props to pass down based on `step`
      switch (this.step) {
        case 0:
          return {
            i18n: this.i18n,
            login_url: this.login_url,
            rebook_payload: this.getRebookPayload()
          };
        case 1:
          return {
            i18n:            this.i18n,
            service_label:   this.displayServiceLabel,
            currentMonth:    this.currentMonth,
            monthDays:       this.monthDays,
            availability:    this.availability,
            availLoading:    this.availLoading,
            selected_date:   this.selected_date,
            selected_date_to: this.selected_date_to,
            selected_time:   this.selected_time,
            available_times: this.available_times,
            service_duration: this.service_duration,
            error:           this.error,
            compact:         this.isCompact
          };
        case 2:
          return {
            i18n:       this.i18n,
            user_name:  this.user_name,
            user_email: this.user_email,
            user_phone: this.user_phone,
            honeypot_value: this.booking_honeypot,
            phone_required: this.phoneRequired,
            submit_label: this.i18n.confirm_booking,
            error:      this.error
          };
        case 3:
          return {
            i18n:           this.i18n,
            service_label:  this.service_label,
            selected_date:  this.selected_date,
            selected_time:  this.selected_time,
            dashboard_url:  this.dashboard_url,
            manage_booking_url: this.manageBookingUrl,
            policy_lines:   this.policyLines,
            booking_details: this.bookingDetails,
            booking_status: this.booking_status,
            auto_approve:   !!(window.BookaroData?.auto_approve_bookings)
          };
        case 4:
          return {
            i18n:           this.i18n,
            error:          this.error,
            listing_id:     this.listing_id,
            service_label:  this.service_label,
            provider_id:    this.provider_id
          };
        default:
          return {};
      }
    }
  },
  methods: {
    withBookingAnchor(url) {
      const raw = String(url || '').trim();
      if (!raw) {
        return '';
      }
      if (raw.includes('#')) {
        return raw;
      }
      return `${raw}#bookaro-booking-btn`;
    },
    isMobileViewport() {
      return !!(window.matchMedia && window.matchMedia('(max-width: 768px)').matches);
    },
    scrollModalToBottom() {
      if (!this.isMobileViewport()) {
        return;
      }
      const wrapper = this.$refs.bookingWrapper;
      if (!wrapper) {
        return;
      }
      requestAnimationFrame(() => {
        const target = wrapper.scrollHeight || 0;
        try {
          wrapper.scrollTo({ top: target, behavior: 'smooth' });
        } catch (e) {
          wrapper.scrollTop = target;
        }
      });
    },
    timeToMinutes(t) {
      const [h, m] = t.split(':').map(Number);
      return h * 60 + m;
    },
    minutesToTime(m) {
      const h  = Math.floor(m / 60);
      const mm = m % 60;
      return (h < 10 ? '0' + h : h) + ':' + (mm < 10 ? '0' + mm : mm);
    },
    getRebookPayload() {
      return {
        listing_id: this.listing_id || 0,
        service_label: this.service_label || this.displayServiceLabel || '',
        provider_id: this.provider_id || 0,
        listing_price: this.listing_price || 0,
        service_id: this.service_id || 0,
        service_duration: this.service_duration || 0,
        service_token: this.service_token || ''
      };
    },
    bookingFormElapsedSeconds() {
      const started = Number(this.booking_form_started_at || 0);
      if (!Number.isFinite(started) || started <= 0) {
        return 0;
      }
      const now = Math.floor(Date.now() / 1000);
      if (now <= started) {
        return 0;
      }
      return now - started;
    },
    fetchAvailability() {
      this.availLoading = true;
      this.availError   = '';
      if (!this.provider_id) {
        this.availLoading = false;
        this.availError = this.i18n.avail_load_error;
        return;
      }
      const from = window.dayjs().format('YYYY-MM-DD');
      const duration = Number(this.service_duration) || 0;
      let toDate = window.dayjs().add(3, 'month');
      if (duration > 0) {
        const extraDays = Math.max(0, Math.ceil(duration / 1440) - 1);
        if (extraDays > 0) {
          toDate = toDate.add(extraDays, 'day');
        }
      }
      const to   = toDate.format('YYYY-MM-DD');
      const data = { from, to };
      if (this.listing_id) {
        data.listing_id = this.listing_id;
      }
      if (duration > 0) {
        data.duration = duration;
      }

      $.ajax({
        url: `${BookaroData.rest_url}bookaro/v1/provider/${this.provider_id}/availability`,
        method: 'GET',
        data,
        beforeSend: (xhr) => {
          xhr.setRequestHeader('X-WP-Nonce', BookaroData.rest_nonce);
        }
      })
          .done((resp) => {
            this.availability.weekly      = resp.weekly;
            this.availability.timeslots   = resp.timeslots;
            this.availability.exceptions  = resp.exceptions;
            this.availability.bookedSlots = resp.bookedSlots;
            this.availability.available   = resp.available;
            this.availability.slots       = resp.slots ? parseInt(resp.slots, 10) : 1;
          })
          .fail(() => {
            this.availError = this.i18n.avail_load_error;
          })
          .always(() => {
          this.availLoading = false;
        });
    },
    fetchServices() {
      if (!this.listing_id) {
        const localServices = Array.isArray(BookaroData.services) ? BookaroData.services : [];
        this.services = localServices;
        this.servicesLoading = false;
        this.applySelectedService();
        if ((this.isLoggedIn || ! this.requireLogin) && this.service_id) {
          this.fetchAvailability();
        }
        return;
      }

      const localServices = Array.isArray(BookaroData.services) ? BookaroData.services : [];
      if (localServices.length && this.listing_id === BookaroData.listing_id) {
        this.services = localServices;
        this.servicesLoading = false;
        this.applySelectedService();
        if ((this.isLoggedIn || ! this.requireLogin) && this.service_id) {
          this.fetchAvailability();
        }
        return;
      }

      this.servicesLoading = true;
      this.servicesError = '';

      const endpoint = BookaroData.services_endpoint ||
        `${BookaroData.rest_url}bookaro/v1/listings/${this.listing_id}/services`;

      $.ajax({
        url: endpoint,
        method: 'GET',
        beforeSend: (xhr) => {
          xhr.setRequestHeader('X-WP-Nonce', BookaroData.rest_nonce);
        }
      })
        .done((res) => {
          this.services = Array.isArray(res) ? res : [];
          this.applySelectedService();
          if ((this.isLoggedIn || ! this.requireLogin) && this.service_id) {
            this.fetchAvailability();
          }
        })
        .fail(() => {
          this.servicesError = this.i18n.services_load_error;
        })
        .always(() => {
          this.servicesLoading = false;
        });
    },
    applySelectedService() {
      if (!this.services.length) {
        this.selected_service_id = '';
        this.service_id = 0;
        this.service_token = '';
        this.service_label = '';
        this.listing_price = 0;
        this.listing_price_formatted = '';
        this.service_duration = 0;
        return;
      }

      let selected = null;
      if (this.selected_service_id) {
        const selectedId = Number(this.selected_service_id);
        selected = this.services.find(service => Number(service.id) === selectedId) || null;
      }

      if (!selected && this.services.length === 1) {
        selected = this.services[0];
        this.selected_service_id = String(selected.id);
      }

      if (!selected) {
        this.service_id = 0;
        this.service_token = '';
        this.service_label = '';
        this.listing_price = 0;
        this.listing_price_formatted = '';
        this.service_duration = 0;
        return;
      }

      this.service_id = Number(selected.id);
      this.service_token = selected.token ? String(selected.token) : '';
      this.service_label = selected.title || '';
      this.listing_price = Number(selected.price) || 0;
      this.listing_price_formatted = selected.price_formatted || this.formatPrice(this.listing_price);
      this.service_duration = Number(selected.duration) || 0;
    },
    onServiceChange() {
      this.error = '';
      this.applySelectedService();
      this.selected_date = '';
      this.selected_date_to = '';
      this.selected_time = '';
      this.available_times = [];
      if (this.isLoggedIn || ! this.requireLogin) {
        this.fetchAvailability();
      }
    },
    isDateDisabled(day) {
      const dStr = day.format('YYYY-MM-DD');
      // a) past
      if (day.isBefore(window.dayjs(), 'day')) return true;
      // b) still loading
      if (this.availLoading) return true;
      // c) exception flagged closed
      if (Number(this.availability.exceptions?.[dStr]) === 0) return true;
      // d) weekday not open
      if (Number(this.availability.weekly?.[day.day()]) === 0) return true;

      // e) no time‐blocks at all
      const blocks = this.availability.timeslots.filter(t => Number(t.weekday) === day.day());
      if (!blocks.length) return true;

      // f) fully booked
      const booked = this.availability.bookedSlots?.[dStr] || [];
      const capacity = Number(this.availability.slots) || 1;
      let total = 0;
      blocks.forEach(t => {
        const start = this.timeToMinutes(t.start_time);
        const end   = this.timeToMinutes(t.end_time);
        const step = Number(t.interval_m);
        const diff = end - start;
        total += (step >= diff ? 1 : Math.floor(diff / step)) * capacity;
      });
      return booked.length >= total;
    },
    getAvailableTimes(dateStr) {
      const wd = window.dayjs(dateStr).day();
      let all = [];
      this.availability.timeslots.forEach(t => {
        if (Number(t.weekday) !== wd) return;
        const start   = this.timeToMinutes(t.start_time);
        const end     = this.timeToMinutes(t.end_time);
        const rawStep = Number(t.interval_m);
        const step    = rawStep > 0 ? rawStep : this.availability.interval;
        if (step <= 0 || end <= start) return;
        if (step >= end - start) {
          all.push(this.minutesToTime(start));
        } else {
          for (let m = start; m + step <= end; m += step) {
            all.push(this.minutesToTime(m));
          }
        }
      });
      const booked = this.availability.bookedSlots?.[dateStr] || [];
      const counts = {};
      booked.forEach(s => { counts[s] = (counts[s] || 0) + 1; });
      const capacity = Number(this.availability.slots) || 1;
      return all
        .filter(s => (counts[s] || 0) < capacity)
        .map(s => ({ time: s, remaining: capacity - (counts[s] || 0) }));
    },
    prevMonth() {
      this.currentMonth = this.currentMonth.subtract(1, 'month');
    },
    nextMonth() {
      this.currentMonth = this.currentMonth.add(1, 'month');
    },
    showModal(listing_id, service_label, provider_id, price, service_id = 0, service_duration = 0, service_token = '') {
      this.listing_id    = listing_id;
      this.provider_id   = provider_id;
      this.service_id    = service_id ? Number(service_id) : 0;
      this.service_token = service_token ? String(service_token) : '';
      this.selected_service_id = service_id ? String(service_id) : '';
      this.service_label = service_label || BookaroData.listing_title || '';
      this.listing_price = Number(price) || 0;
      this.listing_price_formatted = this.formatPrice(this.listing_price);
      this.service_duration = Number(service_duration) || 0;
      if (!this.service_duration && BookaroData.service_durations) {
        const key = String(this.service_label).trim().toLowerCase();
        const mapped = BookaroData.service_durations[key];
        this.service_duration = Number(mapped) || 0;
      }
      // Bug 54: open at step 1 (calendar) if either the user is logged
      // in OR the site allows anonymous bookings; otherwise gate at
      // step 0 (login). Previously this was unconditionally
      // (isLoggedIn ? 1 : 0), so flipping require_login=0 in settings
      // had no UI effect — visitors still saw "Please log in or sign up"
      // even though the server would happily accept their POST.
      this.step          = ( this.isLoggedIn || ! this.requireLogin ) ? 1 : 0;
      this.selected_date   = '';
      this.selected_date_to = '';
      this.selected_time   = '';
      this.available_times = [];
      this.hasPickedDate = false;
      this.booking_id = 0;
      this.error        = '';
      this.currentMonth = window.dayjs().startOf('month');
      this.booking_form_started_at = Math.floor(Date.now() / 1000);
      this.booking_honeypot = '';

      if (this.usesBookaroServices) {
        this.services = Array.isArray(BookaroData.services) ? BookaroData.services : [];
        this.servicesError = '';
        this.fetchServices();
      } else {
        this.services = [];
        this.selected_service_id = '';
        this.service_id = 0;
      }

      if (this.isLoggedIn || ! this.requireLogin) {
        this.fetchAvailability();
      } else {
        this.availability = { weekly: {}, timeslots: [], exceptions: {}, bookedSlots: {} };
        this.availLoading = false;
      }
      this.visible = true;
    },
    closeModal() {
      this.visible = false;
      this.hasPickedDate = false;
    },
    handleAdvance(payload) {
      // If any keys are present in payload, merge them BUT do NOT advance step.
      if (payload && Object.keys(payload).length > 0) {
        Object.keys(payload).forEach(k => {
          this[k] = payload[k];
        });
        if (payload.selected_date || payload.selected_date_to) {
          this.hasPickedDate = true;
        }
        return;
      }
      if (this.usesBookaroServices && this.step === 1 && !this.selected_service_id) {
        this.error = this.i18n.select_service;
        return;
      }
      // Otherwise, payload is {} (empty) ⇒ “Confirm Time” was clicked ⇒ move to next step
      this.step += 1;
    },
    handleBack() {
      this.step = this.step - 1;
    },
    submitBooking() {
      this.loading = true;
      this.error   = '';
      if (!this.user_name || !this.user_email || (this.phoneRequired && !this.user_phone)) {
        this.error   = this.phoneRequired
          ? this.i18n.fill_all
          : (this.i18n.fill_required || this.i18n.fill_all);
        this.loading = false;
        return;
      }

      restRequest('bookaro/v1/bookings', {
        method: 'POST',
        data: {
          listing_id:    this.listing_id,
          price:         this.listing_price,
          provider_id:   this.provider_id,
          service_id:    this.service_id,
          service_label: this.service_label,
          service_token: this.service_token,
          service_duration: this.service_duration,

          // SEND full datetime (Y-m-d H:i:s). Omit `time` entirely.
          booking_date: `${this.selected_date} ${this.selected_time}`.trim(),
          booking_date_to: this.selected_date_to,

          name:          this.user_name,
          email:         this.user_email,
          phone:         this.user_phone,
          bookaro_honeypot: this.booking_honeypot,
          bookaro_form_started_at: this.booking_form_started_at,
          bookaro_form_elapsed_seconds: this.bookingFormElapsedSeconds()
        }
      })
        .then((res) => {
          const bookingId = res && res.booking_id ? parseInt(res.booking_id, 10) : 0;
          this.booking_id = Number.isFinite(bookingId) ? bookingId : 0;
          this.booking_status = res && res.status ? String(res.status) : 'pending';
          this.step = 3;
        })
        .catch((err) => {
          this.error = err?.message || this.i18n.generic_error;
          this.step  = 4;
        })
        .finally(() => {
          this.loading = false;
        });
    },
    formatPrice(value) {
      const settings = this.currency || {};
      const num = Number(value);
      if (Number.isNaN(num)) {
        return '';
      }
      const decimals = Number.isInteger(settings.decimals) ? settings.decimals : 2;
      const decSep = settings.decimal_separator || '.';
      const thouSep = settings.thousand_separator || ',';
      // Bug 73: currency.symbol arrives HTML-entity-encoded (e.g. "&euro;")
      // because WP options can store HTML entities. Text interpolation in
      // Vue ({{ }}) would render the raw entity literally, so decode it
      // once here. Safe: textarea innerHTML decodes entities without
      // executing any HTML/scripts (textarea content is plain text).
      let symbol = settings.symbol || '';
      if (typeof symbol === 'string' && symbol.indexOf('&') !== -1 && typeof document !== 'undefined') {
        const decoder = document.createElement('textarea');
        decoder.innerHTML = symbol;
        symbol = decoder.value;
      }
      const format = settings.format || '%1$s%2$s';
      const fixed = num.toFixed(decimals);
      const parts = fixed.split('.');
      const intPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thouSep);
      const number = parts[1] !== undefined ? intPart + decSep + parts[1] : intPart;
      return format.replace('%1$s', symbol).replace('%2$s', number);
    },
    applyPolicyTemplate(template, hours) {
      const raw = String(template || '').trim();
      if (!raw) {
        return '';
      }
      const hoursValue = Number.isFinite(Number(hours)) ? String(hours) : '0';
      return raw
        .replace(/{{\s*hours\s*}}/g, hoursValue)
        .replace(/{{\s*window_hours\s*}}/g, hoursValue)
        .replace(/{{\s*window\s*}}/g, hoursValue);
    }
  },
  watch: {
    available_times(newVal, oldVal) {
      const newLen = Array.isArray(newVal) ? newVal.length : 0;
      const oldLen = Array.isArray(oldVal) ? oldVal.length : 0;
      if (newLen > 0 && oldLen === 0) {
        this.scrollModalToBottom();
      }
    },
    selected_time(newVal, oldVal) {
      if (newVal && newVal !== oldVal) {
        this.scrollModalToBottom();
      }
    },
    selected_date_to(newVal, oldVal) {
      if (newVal && newVal !== oldVal) {
        this.scrollModalToBottom();
      }
    }
  },
  created() {
    // Re‐open after login if needed
    const rebook = localStorage.getItem('bookaro_rebook');
    if (rebook && this.isLoggedIn) {
      const { listing_id, service_label, provider_id, listing_price, service_id, service_duration, service_token } = JSON.parse(rebook);
      localStorage.removeItem('bookaro_rebook');
      this.listing_price = listing_price;
      this.listing_price_formatted = this.formatPrice(this.listing_price);
      this.showModal(listing_id, service_label, provider_id, listing_price, service_id, service_duration, service_token);
    }
    if (this.usesBookaroServices && this.services.length) {
      this.applySelectedService();
      if ((this.isLoggedIn || ! this.requireLogin) && this.service_id) {
        this.fetchAvailability();
      }
    }
  },
  mounted() {
        const injectBookNowButtons = () => {
          const requiredClassRaw = (BookaroData?.mylisting_services_repeater_class || '').toString().trim();
          const requiredClass = requiredClassRaw ? requiredClassRaw.toLowerCase() : '';
          const repeaterAll = $('.block-type-general_repeater.block-field-repeater');

        const hasRequiredClass = ($el) => {
          if (!requiredClass) {
            return true;
          }
          const classes = ($el.attr('class') || '').toString().toLowerCase().split(/\s+/);
          return classes.includes(requiredClass);
        };

        const resolveEligibleItems = ($repeater) => {
          const $items = $repeater.find('.single-menu-item');
          if (!requiredClass) {
            return $items;
          }
          if (hasRequiredClass($repeater)) {
            return $items;
          }
          return $items.filter(function () {
            return hasRequiredClass($(this));
          });
        };

        repeaterAll.each(function () {
          const $repeater = $(this);
          const $eligibleItems = resolveEligibleItems($repeater);
          if (!$eligibleItems.length) {
            return;
          }

          $eligibleItems.each(function (index) {
          if (!$(this).find('.bookaro-booking-btn').length) {
          const label      = $(this).find('.menu-item-title').text().trim();
          const listingId  = BookaroData.listing_id;
          const providerId = BookaroData.provider_id;
          const priceStr   = $(this).find('.menu-price-btn').text().trim();
          const price      = parseFloat(priceStr.replace(/[^0-9.]/g, '')) || 0;
          const durationAttr = $(this).data('service-duration') || $(this).data('duration');
          const duration = parseInt(durationAttr, 10) || 0;
          const btnLabel   = BookaroData.i18n.book_now;
          let token = '';
          const tokenCandidates = [
            $(this).data('menuId'),
            $(this).data('menu-id'),
            $(this).data('menu_id'),
            $(this).data('id'),
            $(this).data('key'),
            $(this).data('uid')
          ];
          tokenCandidates.some((candidate) => {
            if (candidate !== undefined && candidate !== null) {
              const val = String(candidate).trim();
              if (val) {
                token = val;
                return true;
              }
            }
            return false;
          });
          if (!token) {
            token = String(index);
          }
          const btn = $('<button/>', {
            class: 'bookaro-booking-btn bookaro-booking-launch bookaro-scope'
          })
            .attr('data-listing-id', listingId)
            .attr('data-service-label', label)
            .attr('data-provider-id', providerId)
            .attr('data-listing-price', price)
            .attr('data-service-duration', duration)
            .attr('data-service-token', token)
            .css('margin-top', '10px')
            .text(btnLabel);
          if (!document.getElementById('bookaro-booking-btn')) {
            btn.attr('id', 'bookaro-booking-btn');
          }
          $(this).append(btn);
        }
      });
        });
      };

    const scrollToBookingButtons = () => {
      const hash = (window.location.hash || '').toLowerCase();
      if (hash !== '#bookaro-booking-btn' && hash !== '#bookaro-booking') {
        return;
      }
      const target = document.querySelector('.bookaro-booking-btn') || document.querySelector('.bookaro-booking-launch');
      if (!target || typeof target.scrollIntoView !== 'function') {
        return;
      }
      target.scrollIntoView({ behavior: 'smooth', block: 'center' });
    };

    if (this.hasTimeslots && (this.listing_id || this.provider_id)) {
      injectBookNowButtons();
      scrollToBookingButtons();
      setTimeout(() => {
        injectBookNowButtons();
        scrollToBookingButtons();
      }, 1000);
    }
    scrollToBookingButtons();

      $(document).on('click', '.bookaro-booking-launch', (e) => {
        e.preventDefault();
        const listingId  = $(e.currentTarget).data('listing-id');
        const label      = $(e.currentTarget).data('service-label');
        const providerId = $(e.currentTarget).data('provider-id');
        const btn        = $(e.currentTarget);
        const price      = parseFloat(btn.data('listing-price')) || 0;
        const serviceId  = parseInt($(e.currentTarget).data('service-id'), 10) || 0;
        const serviceDuration = parseInt(btn.data('service-duration'), 10) || 0;
        const serviceToken = btn.data('service-token');
        if (!BookaroData.is_logged_in) {
          localStorage.setItem(
              'bookaro_rebook',
            JSON.stringify({ listing_id: listingId, service_label: label, provider_id: providerId, listing_price: price, service_id: serviceId, service_duration: serviceDuration, service_token: serviceToken })
          );
        }
      this.showModal(listingId, label, providerId, price, serviceId, serviceDuration, serviceToken);
    });
  }
};
</script>

<style scoped>
/* Keep your booking-base.css rules here */
</style>
