import React, { Component } from 'react';
import { __ } from '@wordpress/i18n';
import { ChevronLeft, ChevronRight, Globe, Clock, Calendar } from 'lucide-react';
import Skeleton from '@mui/material/Skeleton';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(utc);
dayjs.extend(timezone);

class DateTimeStep extends Component {
  state = {
    selectedDate: this.props.state.date || null,
    selectedSlots: Array.isArray(this.props.state.slot) ? this.props.state.slot : [],
    availableSlots: this.props.state.slots || [],
    slotsLoading: false,
    calendarLoading: true,
    userTimezone: '',
    allTimezones: [],
    timeFormat: this.props.state.timeFormat || 'hh:mm A',
    daysOpen: this.props.state.daysOpen || {},
    specialDate: this.props.state.specialDate || {},
    holidays: this.props.state.holidays || [],
    staffId: this.props.state.staffId || null,
    staffName: this.props.state.staffName || '',
    servicePrice: Number(this.props.state.servicePrice) || 0,
    serviceName: this.props.state.serviceName || '',
    staffMultipleBooking: false,
    calendarAnimateIn: false,
    slotsAnimateIn: false,
    slotsRenderVersion: 0,
    currentMonth: (this.props.state.date ? dayjs(this.props.state.date) : dayjs()).startOf('month'),
  };

  hasMounted = false;
  calendarAnimationFrame = null;
  slotsAnimationFrame = null;
  staffFetchInProgress = false;

  componentDidMount() {
    const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
    let allTimezones = [];
    try {
      allTimezones = typeof Intl.supportedValuesOf === 'function' ? Intl.supportedValuesOf('timeZone') : [];
    } catch (e) {
      allTimezones = [];
    }

    if (!Array.isArray(allTimezones) || allTimezones.length === 0) {
      allTimezones = tz ? [tz] : [];
    }

    this.setState({ userTimezone: tz, allTimezones });

    this.maybeFetchServicesByLocation();
    this.maybeResolveServiceFromWp();
    this.maybeLoadStaffAndDates();
    this.handleCalendarAnimateToggle();
    this.handleSlotsAnimateToggle();
  }

  componentDidUpdate(prevProps, prevState) {
    const prevContext = prevProps.state;
    const context = this.props.state;

    if (context.locationId !== prevContext.locationId) {
      this.clearAvailability();

      if (wpbApp?.HideServices === 'true') {
        if (!this.hasMounted) {
          this.hasMounted = true;
        } else {
          this.props.updateInput({
            serviceId: null,
            serviceName: '',
            staffId: null,
            staffName: '',
            daysOpen: {},
            specialDate: {},
            holidays: [],
            date: null,
            slot: [],
            slots: [],
          });
        }
      }
      this.maybeFetchServicesByLocation();
      this.maybeResolveServiceFromWp();
      this.maybeLoadStaffAndDates();
      return;
    }

    if (context.serviceId !== prevContext.serviceId) {
      this.clearAvailability();
      this.props.updateInput({
        daysOpen: {},
        specialDate: {},
        holidays: [],
        date: null,
        slot: [],
        slots: [],
      });

      if (prevContext.serviceId == null && context.serviceId != null) {
        this.maybeLoadStaffAndDates();
      }
    }

    if (
      context.categorizedServices !== prevContext.categorizedServices ||
      context.services !== prevContext.services
    ) {
      this.maybeResolveServiceFromWp();
      this.maybeLoadStaffAndDates();
    }

    if (
      context.staffId !== prevContext.staffId ||
      this.state.calendarLoading !== prevState.calendarLoading ||
      this.state.currentMonth !== prevState.currentMonth
    ) {
      this.handleCalendarAnimateToggle();
    }

    // Slot animation is triggered deterministically inside `fetchSlotsForDate`
    // to ensure we render once in a hidden state, then animate in.
  }

  componentWillUnmount() {
    if (this.calendarAnimationFrame) {
      cancelAnimationFrame(this.calendarAnimationFrame);
    }
    if (this.slotsAnimationFrame) {
      cancelAnimationFrame(this.slotsAnimationFrame);
    }
  }

  getMaxDate = () => {
    const { state } = this.props;
    if (state.toggleLimitedBooking === 'Enable') {
      return dayjs().add(parseInt(state.limitedBookingMonths || '0', 10), 'month');
    }
    return dayjs().add(50, 'year');
  };

  getMonthOptions = () => ([
    __('January', 'bookify'),
    __('February', 'bookify'),
    __('March', 'bookify'),
    __('April', 'bookify'),
    __('May', 'bookify'),
    __('June', 'bookify'),
    __('July', 'bookify'),
    __('August', 'bookify'),
    __('September', 'bookify'),
    __('October', 'bookify'),
    __('November', 'bookify'),
    __('December', 'bookify'),
  ]);

  getYearOptions = () => {
    const today = dayjs();
    const maxDate = this.getMaxDate();
    const years = [];
    for (let y = today.year(); y <= maxDate.year(); y++) {
      years.push(y);
    }
    return years;
  };

  resolveServiceFromWp = () => {
    const raw = wpbApp?.Services;
    if (!raw) return null;
    return Array.isArray(raw) ? raw[0] : raw;
  };

  maybeFetchServicesByLocation = () => {
    const { state, updateInput } = this.props;
    if (wpbApp?.HideServices !== 'true') return;
    if (!state.locationId) return;
    if (state.serviceId) return;
    if (Array.isArray(state.categorizedServices) && state.categorizedServices.length > 0) return;

    updateInput({
      serviceByLocaionLoading: true,
      serviceStopRerendering: false,
      categorizedServices: [],
      serviceId: null,
      serviceName: '',
      servicePrice: 0,
    });

    fetch(`${wpbApp.root}bookify/frontend/v1/get-services-by-location`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': wpbApp.nonce,
      },
      body: JSON.stringify({
        location_id: state.locationId,
        services: wpbApp.Services,
      }),
    })
      .then((response) => response.json())
      .then((data) => {
        const categorizedServices =
          (data.services || []).map((category) => ({
            name: category.category_name,
            services: (category.services || []).map((service) => ({
              id: service.id,
              name: service.service_name || service.name,
              category: category.category_name,
              price: Number(service.service_price ?? service.price ?? 0),
              duration: service.service_duration || service.duration || '',
              image: service.service_img || service.image || '',
            })),
          })) || [];

        updateInput({
          categorizedServices,
          serviceByLocaionLoading: false,
          serviceStopRerendering: true,
        });
      })
      .catch((error) => {
        console.error('Error fetching services by location (hidden services):', error);
        updateInput({ serviceByLocaionLoading: false });
      });
  };

  maybeResolveServiceFromWp = () => {
    const { state, updateInput } = this.props;
    if (wpbApp?.HideServices !== 'true') return;
    if (state.serviceId) return;

    const resolvedServiceId = this.resolveServiceFromWp();
    if (!resolvedServiceId) return;

    let validServices = state.services || [];
    if (state.locationId && Array.isArray(state.categorizedServices)) {
      validServices = state.categorizedServices.flatMap((cat) => cat.services || []);
    }

    const matched = validServices.find(
      (svc) => `${svc.id ?? svc.service_id}` === `${resolvedServiceId}`
    );

    if (!matched) {
      updateInput({
        serviceId: null,
        serviceName: '',
        servicePrice: 0,
      });
      return;
    }

    const derivedName = matched?.service_name || matched?.name || state.serviceName || this.state.serviceName;
    const derivedPrice = Number(
      matched?.service_price ?? matched?.price ?? state.servicePrice ?? this.state.servicePrice ?? 0
    );

    updateInput({
      serviceId: resolvedServiceId,
      serviceName: derivedName,
      servicePrice: derivedPrice,
    });

    this.setState({
      serviceName: derivedName,
      servicePrice: derivedPrice,
    });
  };

  maybeLoadStaffAndDates = () => {
    const { state, updateInput } = this.props;
    if (!state.serviceId) return;

    const hideServices = wpbApp?.HideServices === 'true';

    if (hideServices && state.locationId && Array.isArray(state.categorizedServices) && state.categorizedServices.length > 0) {
      const validServices = state.categorizedServices.flatMap((cat) => cat.services || []);
      const isValid = validServices.some((svc) => `${svc.id}` === `${state.serviceId}`);
      if (!isValid) {
        updateInput({
          serviceId: null,
          serviceName: '',
          staffId: null,
          staffName: '',
          daysOpen: {},
          specialDate: {},
          holidays: [],
          date: null,
          slot: [],
          slots: [],
        });
        this.clearAvailability();
        return;
      }
    }

    this.setState({ calendarLoading: true }, async () => {
      try {
        await this.fetchStaffAndDates(state.serviceId);
      } finally {
        this.setState({ calendarLoading: false });
      }
    });
  };

  clearAvailability = () => {
    this.setState({
      daysOpen: {},
      specialDate: {},
      holidays: [],
      selectedDate: null,
      availableSlots: [],
      selectedSlots: [],
      staffMultipleBooking: false,
    });
  };

  handleCalendarAnimateToggle = () => {
    if (this.calendarAnimationFrame) {
      cancelAnimationFrame(this.calendarAnimationFrame);
    }

    this.setState({ calendarAnimateIn: false }, () => {
      this.calendarAnimationFrame = requestAnimationFrame(() =>
        this.setState({ calendarAnimateIn: true })
      );
    });
  };

  handleSlotsAnimateToggle = () => {
    if (this.slotsAnimationFrame) {
      cancelAnimationFrame(this.slotsAnimationFrame);
    }

    // Use double rAF so the "hidden" state is painted first; otherwise
    // the browser may skip the transition.
    this.setState({ slotsAnimateIn: false }, () => {
      this.slotsAnimationFrame = requestAnimationFrame(() => {
        this.slotsAnimationFrame = requestAnimationFrame(() => {
          this.setState({ slotsAnimateIn: true });
        });
      });
    });
  };

  fetchStaffAndDates = async (serviceId) => {
    const { state, updateInput } = this.props;
    if (this.staffFetchInProgress) return;
    this.staffFetchInProgress = true;
    try {
      const ProStaffStep = window.BookifyPro && window.BookifyPro.Light ? window.BookifyPro.Light.StaffStep : null;
      const showStaffStep = wpbApp?.HideStaffs !== 'true' && ProStaffStep;

      let staff = null;

      if (Array.isArray(state.staffsByServices) && state.staffsByServices.length > 0) {
        if (state.staffId) {
          staff = state.staffsByServices.find((s) => `${s.staff_id}` === `${state.staffId}`);
        }
        if (!staff && showStaffStep) {
          staff = state.staffsByServices[0];
        }
        
        if (staff) {
          const staffMultipleBooking = staff.mulitple_booking ? JSON.parse(staff.mulitple_booking) : false;
          this.setState({ staffMultipleBooking: staffMultipleBooking });
        }
      }

      if (!staff) {
        const locationIdToSend =
          state.locationId || (wpbApp?.HideLocations === 'true'
            ? (Array.isArray(wpbApp.Locations) ? wpbApp.Locations[0] : wpbApp.Locations)
            : null);
        const staffIdsToSend = wpbApp?.Staffs;

        const res = await fetch(`${wpbApp.root}bookify/v1/staffs-by-service`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-WP-Nonce': wpbApp.nonce,
          },
          body: JSON.stringify({
            location_id: locationIdToSend || 'all',
            service_id: serviceId,
            staff_id: staffIdsToSend || '',
          }),
        });
        const data = await res.json();

        staff = data?.staffs?.[0];

        if (wpbApp.HideStaffs === 'true') {
          const requestedStaffId = Array.isArray(wpbApp.Staffs) ? wpbApp.Staffs[0] : wpbApp.Staffs;
          if (requestedStaffId) {
            staff = (data?.staffs || []).find((s) => `${s.staff_id}` === `${requestedStaffId}`);
            if (!staff) {
              updateInput({
                staffId: null,
                staffName: '',
                daysOpen: {},
                specialDate: {},
                holidays: [],
                date: null,
                slot: [],
                slots: [],
              });
              this.clearAvailability();
              return;
            }
          }
        }
      }

      if (staff) {
        const staffMultipleBooking = staff.mulitple_booking ? JSON.parse(staff.mulitple_booking) : false;
        updateInput({
          staffId: staff.staff_id,
          staffName: staff.staff_name,
          servicePrice: Number(staff.service_price) || 0,
          serviceName: staff.service_name || state.serviceName,
        });
        this.setState({
          staffId: staff.staff_id,
          staffName: staff.staff_name,
          servicePrice: Number(staff.service_price) || 0,
          serviceName: staff.service_name || state.serviceName,
          staffMultipleBooking: staffMultipleBooking,
        });
        await this.fetchDatesByStaff(serviceId, staff.staff_id);
      }
    } catch (error) {
      console.error('Error fetching staff:', error);
    } finally {
      this.staffFetchInProgress = false;
    }
  };

  fetchDatesByStaff = async (serviceId, staffIdValue) => {
    const { state, updateInput } = this.props;
    try {
      const locationIdToSend =
        state.locationId || (wpbApp?.HideLocations === 'true'
          ? (Array.isArray(wpbApp.Locations) ? wpbApp.Locations[0] : wpbApp.Locations)
          : null);
      const res = await fetch(`${wpbApp.root}bookify/v1/dates-by-staff`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-WP-Nonce': wpbApp.nonce,
        },
        body: JSON.stringify({
          location_id: locationIdToSend || '',
          service_id: serviceId,
          staff_id: staffIdValue,
          services: wpbApp?.Services || '',
          staffs: wpbApp?.Staffs || '',
        }),
      });
      const data = await res.json();
      this.setState({
        daysOpen: data.dates || {},
        specialDate: data.special || {},
        holidays: data.holidays || [],
      });
      updateInput({
        daysOpen: data.dates || {},
        specialDate: data.special || {},
        holidays: data.holidays || [],
        servicePrice: data.servicePrice ?? state.servicePrice,
        serviceName: data.serviceName ?? state.serviceName,
      });
    } catch (error) {
      console.error('Error fetching dates:', error);
    }
  };

  getHolidayDates = () => {
    try {
      const { holidays } = this.state;
      if (!holidays) return [];
      if (Array.isArray(holidays)) {
        if (holidays.length === 0) return [];
        if (typeof holidays[0] === 'string') return holidays;
        return holidays.map((h) => h.dateFormated || h.date);
      }
      const parsed = typeof holidays === 'string' ? JSON.parse(holidays) : holidays;
      return Object.values(parsed).map((h) => h.dateFormated || h.date);
    } catch {
      return [];
    }
  };

  shouldDisableDate = (dateObj) => {
    const { daysOpen, specialDate } = this.state;
    const day = dateObj.day();
    const formatted = dateObj.format('YYYY-MM-DD');
    const today = dayjs();
    const maxDate = this.getMaxDate();

    if (dateObj.isBefore(today, 'day')) return true;
    if (dateObj.isAfter(maxDate, 'day')) return true;

    const specialDates = Object.values(specialDate || {}).map((entry) => entry.date);
    if (specialDates.includes(formatted)) return false;

    if (Object.keys(daysOpen).length <= 0) return true;

    const eachDay = Object.keys(daysOpen).map((dayKey) => parseInt(dayKey, 10));
    const isDayOpen = eachDay.includes(day);

    const holidayDates = this.getHolidayDates();
    if (holidayDates.length > 0) {
      const isHoliday = holidayDates.includes(formatted);
      return !isDayOpen || isHoliday;
    }

    return !isDayOpen;
  };

  getWeekStartDay = () => {
    const { state, data } = this.props;
    const weekDays = {
      Sunday: 0,
      Monday: 1,
      Tuesday: 2,
      Wednesday: 3,
      Thursday: 4,
      Friday: 5,
      Saturday: 6,
    };
    const weekStartKey = state.weekStartsOn || data?.weekStartsOn;
    return weekDays[weekStartKey] ?? 0;
  };

  getWeekDayOrder = () => {
    const weekStartDay = this.getWeekStartDay();
    const labels = [
      __('Sun', 'bookify'),
      __('Mon', 'bookify'),
      __('Tue', 'bookify'),
      __('Wed', 'bookify'),
      __('Thu', 'bookify'),
      __('Fri', 'bookify'),
      __('Sat', 'bookify'),
    ];
    return [...labels.slice(weekStartDay), ...labels.slice(0, weekStartDay)];
  };

  getCalendarDays = () => {
    const { currentMonth } = this.state;
    const weekStartDay = this.getWeekStartDay();
    const startOfMonth = currentMonth.startOf('month');
    const endOfMonth = currentMonth.endOf('month');

    let startCursor = startOfMonth;
    while (startCursor.day() !== weekStartDay) {
      startCursor = startCursor.subtract(1, 'day');
    }

    let endCursor = endOfMonth;
    while (endCursor.day() !== ((weekStartDay + 6) % 7)) {
      endCursor = endCursor.add(1, 'day');
    }

    const daysList = [];
    let cursor = startCursor;
    while (cursor.isBefore(endCursor) || cursor.isSame(endCursor, 'day')) {
      daysList.push(cursor);
      cursor = cursor.add(1, 'day');
    }

    return daysList;
  };

  getDaysInWeeks = () => {
    const calendarDays = this.getCalendarDays();
    const weeks = [];
    for (let i = 0; i < calendarDays.length; i += 7) {
      weeks.push(calendarDays.slice(i, i + 7));
    }
    return weeks;
  };

  goPrevMonth = () => {
    this.setState((prev) => ({ currentMonth: prev.currentMonth.subtract(1, 'month') }));
  };

  goNextMonth = () => {
    this.setState((prev) => ({ currentMonth: prev.currentMonth.add(1, 'month') }));
  };

  handleMonthChange = (e) => {
    const newMonth = parseInt(e.target.value, 10);
    this.setState((prev) => ({
      currentMonth: prev.currentMonth.month(newMonth).startOf('month'),
      selectedDate: null,
      selectedSlots: [],
    }));
  };

  handleYearChange = (e) => {
    const newYear = parseInt(e.target.value, 10);
    this.setState((prev) => ({
      currentMonth: prev.currentMonth.year(newYear).startOf('month'),
      selectedDate: null,
      selectedSlots: [],
    }));
  };

  handleTimezoneChange = (e) => {
    const nextTz = e.target.value;
    const { updateInput } = this.props;

    this.setState((prev) => ({
      userTimezone: nextTz,
      selectedDate: null,
      availableSlots: [],
      selectedSlots: [],
      slotsLoading: false,
      slotsAnimateIn: false,
      slotsRenderVersion: (prev.slotsRenderVersion || 0) + 1,
    }));

    // Clear any previously selected date/slots in booking context as well.
    updateInput({
      date: null,
      slot: [],
      slots: [],
      timezone: nextTz,
    });
  };

  getActiveTz = () => this.state.userTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone;

  convertSlotFromWpToUser = (slot, format) => {
    const activeTz = this.getActiveTz();
    const [start, end] = slot.split(' - ');

    const formatedStartTime = dayjs(`1970-01-01 ${start}`).format('HH:mm');
    const formatedEndTime = dayjs(`1970-01-01 ${end}`).format('HH:mm');

    const startTime = dayjs.tz(`1970-01-01 ${formatedStartTime}`, wpbApp.wpTimezone).tz(activeTz).format(format);
    const endTime = dayjs.tz(`1970-01-01 ${formatedEndTime}`, wpbApp.wpTimezone).tz(activeTz).format(format);

    return `${startTime} - ${endTime}`;
  };

  convertSlotToAdminTimezone = (slot, format) => {
    const activeTz = this.getActiveTz();
    const [start, end] = slot.split(' - ');

    const formatedStartTime = dayjs(`1970-01-01 ${start}`).format('HH:mm');
    const formatedEndTime = dayjs(`1970-01-01 ${end}`).format('HH:mm');

    const convertedStartTime = dayjs.tz(`1970-01-01 ${formatedStartTime}`, activeTz).tz(wpbApp.wpTimezone).format(format);
    const convertedEndTime = dayjs.tz(`1970-01-01 ${formatedEndTime}`, activeTz).tz(wpbApp.wpTimezone).format(format);

    return `${convertedStartTime} - ${convertedEndTime}`;
  };

  fetchSlotsForDate = async (dateStr) => {
    const { specialDate, daysOpen, staffId } = this.state;
    const { state } = this.props;
    const weekDay = dayjs(dateStr).day();
    let timeSlots = [];
    let isSpecialDate = false;

    for (let key in specialDate) {
      if (specialDate[key].date === dateStr) {
        timeSlots = specialDate[key].slots || [];
        isSpecialDate = true;
        break;
      }
    }

    if (!isSpecialDate) {
      for (let key in daysOpen) {
        if (daysOpen[weekDay]) {
          timeSlots = daysOpen[weekDay].slots || [];
          break;
        }
      }
    }

    if (!timeSlots || timeSlots.length === 0) {
      this.setState({ availableSlots: [] });
      return;
    }

    // Start slot-loading: show skeletons, and bump a version so slot items remount.
    const nextVersion = (this.state.slotsRenderVersion || 0) + 1;
    this.setState({ slotsLoading: true, slotsAnimateIn: false, slotsRenderVersion: nextVersion });
    try {
      let reqStaffId = staffId || state.staffId;

      if (reqStaffId === null && wpbApp.HideStaffs === 'true') {
        reqStaffId = wpbApp.Staffs[0];
      }

      const payload = {
        date: dateStr,
        slots: JSON.stringify(timeSlots),
        staff_id: reqStaffId,
      };

      const res = await fetch(`${wpbApp.root}bookify/v1/available-slots`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-WP-Nonce': wpbApp.nonce,
        },
        body: JSON.stringify(payload),
      });

      const data = await res.json();
      const tf = data.timeFormat === '12-hour' ? 'hh:mm A' : 'HH:mm';
      this.setState({ timeFormat: tf });

      const converted = (data.available_slots || []).map((slot) =>
        this.convertSlotFromWpToUser(slot, tf)
      );

      this.setState({ availableSlots: converted });
    } catch (error) {
      console.error('Error fetching slots:', error);
      this.setState({ availableSlots: [] });
    } finally {
      // Phase 1: render slots visible in DOM but hidden (animateIn=false),
      // Phase 2: next paint -> animate them in.
      this.setState({ slotsLoading: false, slotsAnimateIn: false }, () => {
        this.slotsAnimationFrame = requestAnimationFrame(() => {
          this.slotsAnimationFrame = requestAnimationFrame(() => {
            // Only animate if we're still on the same fetch cycle.
            if (this.state.slotsRenderVersion === nextVersion) {
              this.setState({ slotsAnimateIn: true });
            }
          });
        });
      });
    }
  };

  handleDateSelect = (date) => {
    const formatted = date.format('YYYY-MM-DD');
    this.setState({
      currentMonth: date.startOf('month'),
      selectedDate: formatted,
      selectedSlots: [],
    });
    this.fetchSlotsForDate(formatted);
  };

  handleSlotClick = (slot) => {
    const ProStaffMultipleBooking = window.ProStaffMultipleBooking;
    const { selectedSlots, staffMultipleBooking } = this.state;

    let newSlots;
    if (ProStaffMultipleBooking && staffMultipleBooking) {
      newSlots = selectedSlots.includes(slot)
        ? selectedSlots.filter((s) => s !== slot)
        : [...selectedSlots, slot];
    } else {
      newSlots = [slot];
    }
    this.setState({ selectedSlots: newSlots }, () => {
      if (!(ProStaffMultipleBooking && staffMultipleBooking)) {
        const { selectedDate } = this.state;
        if (selectedDate && newSlots.length > 0) {
          this.performContinue(newSlots);
        }
      }
    });
  };

  performContinue = (overrideSlots = null) => {
    const { state, updateInput, onNext } = this.props;
    const { selectedDate, selectedSlots, timeFormat, availableSlots, staffId, staffName, servicePrice, serviceName, daysOpen, specialDate, holidays } = this.state;

    const effectiveSlots = overrideSlots || selectedSlots;
    if (!selectedDate || effectiveSlots.length === 0) return;

    const format = timeFormat || 'hh:mm A';
    const activeTz = this.getActiveTz();
    const slotsInAdminTimezone = effectiveSlots.map((slot) =>
      this.convertSlotToAdminTimezone(slot, format)
    );

    const resolvedServiceId =
      state.serviceId ||
      (wpbApp.HideServices === 'true' ? this.resolveServiceFromWp() : null);
    const resolvedServiceName = serviceName || state.serviceName || '';
    const resolvedServicePrice = Number(servicePrice || state.servicePrice || 0);
    const finalStaffId = staffId || state.staffId || (wpbApp.HideStaffs === 'true' ? wpbApp.Staffs?.[0] : null);
    const finalStaffName = staffName || state.staffName || '';
    const finalServicePrice = resolvedServicePrice;

    const slotCount = effectiveSlots.length;
    const totalPrice =
      (wpbApp.HideStaffs === 'true'
        ? finalServicePrice
        : Number(state.servicePrice ?? finalServicePrice ?? 0)) * slotCount;

    updateInput({
      date: selectedDate,
      slot: slotsInAdminTimezone,
      slots: availableSlots,
      timezone: activeTz,
      timeFormat: format,
      total: totalPrice,
      staffId: finalStaffId,
      staffName: finalStaffName,
      servicePrice: finalServicePrice,
      serviceId: resolvedServiceId,
      serviceName: resolvedServiceName,
      daysOpen: daysOpen,
      specialDate: specialDate,
      holidays: holidays,
    });

    onNext();
  };

  handleContinue = () => {
    this.performContinue();
  };

  handleBack = () => {
    const { updateInput, onBack } = this.props;
    this.clearAvailability();
    updateInput({
      daysOpen: {},
      specialDate: {},
      holidays: [],
      date: null,
      slot: [],
      slots: [],
    });
    onBack();
  };

  render() {
    const bookingState = this.props.state || {};
    const allowTimezoneSelect =
      Boolean(window.ProSlotTimezone) &&
      (bookingState.slotsTimezone === true ||
        bookingState.slotsTimezone === 'true' ||
        bookingState.slotsTimezone === 1 ||
        bookingState.slotsTimezone === '1');

    const {
      selectedDate,
      selectedSlots,
      availableSlots,
      slotsLoading,
      calendarLoading,
      calendarAnimateIn,
      slotsAnimateIn,
      currentMonth,
      userTimezone,
      allTimezones,
    } = this.state;
    const visibleMonth = currentMonth.format('MMMM YYYY');
    const weekDayOrder = this.getWeekDayOrder();
    const daysInWeeks = this.getDaysInWeeks();
    const disablePrevMonth = !currentMonth.startOf('month').isAfter(dayjs().startOf('month'));
    const disableNextMonth = !currentMonth.endOf('month').isBefore(this.getMaxDate().endOf('month'));

    return (
      <>
        <div className="flex flex-col min-h-[436px] space-y-8 p-5">
          <div className="flex flex-col md:flex-row gap-5 items-start">
            <div
              className="flex-1 bg-white rounded-2xl p-3 border border-slate-150 md:sticky md:top-5 md:self-start"
              style={{
                transition: 'opacity 600ms ease, transform 600ms ease',
                transitionDelay: '80ms',
                transform: calendarAnimateIn ? 'translateY(0)' : 'translateY(24px)',
                opacity: calendarAnimateIn ? 1 : 0,
              }}
            >
              <div className="flex flex-col gap-3 mb-6">
                <div className="flex items-center justify-between gap-3">
                  <div className="flex items-center gap-2 text-sm text-slate-700">
                    <label className="font-semibold" htmlFor="month-select">{__('Month', 'bookify')}</label>
                    <select
                      id="month-select"
                      className="border border-slate-200 rounded-md p-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-400"
                      value={currentMonth.month()}
                      onChange={this.handleMonthChange}
                    >
                      {this.getMonthOptions().map((label, idx) => (
                        <option key={label} value={idx}>
                          {label}
                        </option>
                      ))}
                    </select>
                  </div>
                  <div className="flex items-center gap-2 text-sm text-slate-700">
                    <label className="font-semibold" htmlFor="year-select">{__('Year', 'bookify')}</label>
                    <select
                      id="year-select"
                      className="border border-slate-200 rounded-md p-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-400"
                      value={currentMonth.year()}
                      onChange={this.handleYearChange}
                    >
                      {this.getYearOptions().map((year) => (
                        <option key={year} value={year}>
                          {year}
                        </option>
                      ))}
                    </select>
                  </div>
                </div>
                <div className="flex items-center justify-between">
                  <h3 className="font-medium text-slate-800 text-lg">{visibleMonth}</h3>
                  <div className="flex space-x-2 text-xs text-slate-400">
                    <button
                      onClick={this.goPrevMonth}
                      disabled={disablePrevMonth}
                      className="p-2 rounded-md border border-slate-200 outline-none hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed"
                      aria-label={__('Previous month', 'bookify')}
                    >
                      <ChevronLeft size={20} />
                    </button>
                    <button
                      onClick={this.goNextMonth}
                      disabled={disableNextMonth}
                      className="p-2 rounded-md border border-slate-200 outline-none hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed"
                      aria-label={__('Next month', 'bookify')}
                    >
                      <ChevronRight size={20} />
                    </button>
                  </div>
                </div>
              </div>

              <div className="grid grid-cols-7 gap-2 text-center text-xs text-black font-medium mb-4 tracking-widest uppercase">
                {weekDayOrder.map((label) => (
                  <span key={label}>{label}</span>
                ))}
              </div>

              <div className="grid grid-cols-7 gap-2">
                {calendarLoading
                  ? Array.from({ length: 35 }).map((_, idx) => (
                    <Skeleton key={idx} variant="rounded" height={32} />
                  ))
                  : daysInWeeks.map((week, weekIdx) =>
                    week.map((dateObj) => {
                      const dateStr = dateObj.format('YYYY-MM-DD');
                      const isSelected = selectedDate === dateStr;
                      const isOtherMonth = dateObj.month() !== currentMonth.month();
                      const disabled = isOtherMonth || this.shouldDisableDate(dateObj);
                      return (
                        <button
                          key={`${weekIdx}-${dateStr}`}
                          disabled={disabled}
                          onClick={() => this.handleDateSelect(dateObj)}
                          className={`aspect-square flex items-center justify-center rounded-full text-sm outline-none ${isSelected
                            ? 'bg-blue-600 text-white'
                            : disabled
                              ? 'text-black-400 cursor-not-allowed'
                              : 'bg-slate-100 text-blue-700 hover:bg-slate-300 hover:text-blue-900'
                            }`}
                        >
                          {dateObj.date()}
                        </button>
                      );
                    })
                  )}
              </div>

              <div className="mt-6 flex items-center justify-center gap-2 text-slate-400 text-xs font-medium">
                <Globe size={14} />
                {allowTimezoneSelect ? (
                  <>
                    <label htmlFor="bookify-timezone">{__('Timezone', 'bookify')}</label>
                    <select
                      id="bookify-timezone"
                      className="w-[110px] border border-slate-200 rounded-md px-2 py-1 text-xs bg-white text-slate-700 focus:outline-none focus:ring-1 focus:ring-blue-400"
                      value={userTimezone || ''}
                      onChange={this.handleTimezoneChange}
                    >
                      {(Array.isArray(allTimezones) ? allTimezones : []).map((tz) => (
                        <option key={tz} value={tz}>
                          {tz}
                        </option>
                      ))}
                    </select>
                  </>
                ) : (
                  <span>{__('Timezone:', 'bookify')} {userTimezone || __('Loading...', 'bookify')}</span>
                )}
              </div>
            </div>

            <div
              className="flex-1 space-y-4"
              style={{
                transition: 'opacity 600ms ease, transform 600ms ease',
                transitionDelay: '80ms',
                transform: calendarAnimateIn ? 'translateY(0)' : 'translateY(24px)',
                opacity: calendarAnimateIn ? 1 : 0,
              }}
            >
              <div className="flex items-center space-x-2 text-slate-800 mb-3">
                <Clock size={20} className="text-blue-600" />
                <span className="font-medium text-base">{__('Available Slots', 'bookify')}</span>
              </div>

              {selectedDate ? (
                <div className="grid grid-cols-1 gap-3">
                  {slotsLoading ? (
                    Array.from({ length: 8 }).map((_, idx) => (
                      <Skeleton key={idx} variant="rounded" height={38} />
                    ))
                  ) : availableSlots.length === 0 ? (
                    <div className="flex flex-col items-center justify-center text-slate-400 border-2 border-dashed border-slate-150 rounded-3xl p-8 text-center">
                      <Calendar size={48} strokeWidth={1} className="mb-4 opacity-20" />
                      <p className="text-sm">{__('No slots available for the selected date, please select another date to see available slots.', 'bookify')}</p>
                    </div>
                  ) : (
                    availableSlots.map((slot, slotIndex) => {
                      const isSelected = selectedSlots.includes(slot);
                      const slotDelay = `${slotIndex * 70}ms`;
                      return (
                        <button
                          key={`${this.state.slotsRenderVersion}-${slot}`}
                          onClick={() => this.handleSlotClick(slot)}
                          className={`p-2 rounded-xl text-sm font-medium border outline-none ${isSelected
                            ? 'border-blue-600 bg-blue-50 text-blue-700'
                            : 'border-slate-150 hover:border-blue-200 text-slate-600 bg-white'
                            }`}
                          style={{
                            transition: 'opacity 600ms ease, transform 600ms ease',
                            transitionDelay: slotDelay,
                            transform: slotsAnimateIn ? 'translateY(0)' : 'translateY(24px)',
                            opacity: slotsAnimateIn ? 1 : 0,
                          }}
                        >
                          {slot}
                        </button>
                      );
                    })
                  )}
                </div>
              ) : (
                <div className="h-64 flex flex-col items-center justify-center text-slate-400 border-2 border-dashed border-slate-150 rounded-3xl p-8 text-center">
                  <Calendar size={48} strokeWidth={1} className="mb-4 opacity-20" />
                  <p className="text-sm">{__('Please select a date to view available time slots.', 'bookify')}</p>
                </div>
              )}
            </div>
          </div>
        </div>

        <div className="px-[20px] py-[10px] flex justify-end sticky bottom-0 bg-white border-t border-slate-150">
          <button
            onClick={this.handleBack}
            className="px-[16px] py-[6px] text-slate-400 text-sm font-bold rounded-sm hover:bg-slate-50 transition-colors"
          >
            {__('Back', 'bookify')}
          </button>
          <button
            disabled={!selectedDate || selectedSlots.length === 0}
            onClick={this.handleContinue}
            className="px-[16px] py-[6px] bg-blue-600 text-white text-sm font-bold rounded-sm shadow-xl shadow-blue-200 hover:bg-blue-700 disabled:opacity-50 disabled:shadow-none"
          >
            {__('Next', 'bookify')}
          </button>
        </div>
      </>
    );
  }
}

export default DateTimeStep;