"use client";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import * as React from "react";
import { z } from "zod";
import { ScheduleView } from "@open-mercato/ui/backend/schedule";
import { Button } from "@open-mercato/ui/primitives/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@open-mercato/ui/primitives/dialog";
import { Input } from "@open-mercato/ui/primitives/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue
} from "@open-mercato/ui/primitives/select";
import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
import { apiCall, apiCallOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
import { createCrud, deleteCrud, updateCrud } from "@open-mercato/ui/backend/utils/crud";
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
import { flash } from "@open-mercato/ui/backend/FlashMessages";
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
import { Spinner } from "@open-mercato/ui/primitives/spinner";
import { ComboboxInput, TimePicker } from "@open-mercato/ui/backend/inputs";
import { DictionaryEntrySelect } from "@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect";
import {
  createUnavailabilityReasonEntry,
  loadUnavailabilityReasonEntries
} from "@open-mercato/core/modules/planner/components/unavailabilityReasons";
import { useT } from "@open-mercato/shared/lib/i18n/context";
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
import { normalizeCrudServerError } from "@open-mercato/ui/backend/utils/serverErrors";
import { parseAvailabilityRuleWindow } from "@open-mercato/core/modules/planner/lib/availabilitySchedule";
import { deleteAvailabilityRuleSet } from "@open-mercato/core/modules/planner/lib/deleteAvailabilityRuleSet";
import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
import { Calendar, Clock, List, PencilLine, Plus, Trash2 } from "lucide-react";
import {
  resolveRuleSetSelectValue,
  requiresResetConfirmation,
  selectCustomRuleIdsToDelete
} from "./availabilityRulesEditorState.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("planner").child({ component: "AvailabilityRulesEditor" });
function withOptimisticLockForRule(rule, mutation) {
  return withScopedApiRequestHeaders(buildOptimisticLockHeader(rule.updatedAt ?? rule.updated_at ?? null), mutation);
}
function withOptimisticLockForRuleSet(ruleSet, mutation) {
  return withScopedApiRequestHeaders(buildOptimisticLockHeader(ruleSet?.updatedAt ?? ruleSet?.updated_at ?? null), mutation);
}
function resolveDateSpecificLockToken(rules) {
  let latestMs = Number.NEGATIVE_INFINITY;
  let latestIso = null;
  for (const rule of rules) {
    const raw = rule.updatedAt ?? rule.updated_at ?? null;
    if (!raw) continue;
    const ms = Date.parse(raw);
    if (!Number.isFinite(ms) || ms <= latestMs) continue;
    latestMs = ms;
    latestIso = raw;
  }
  return latestIso;
}
function withOptimisticLockForDateSpecific(rules, mutation) {
  return withScopedApiRequestHeaders(buildOptimisticLockHeader(resolveDateSpecificLockToken(rules)), mutation);
}
const DAY_LABELS = [
  { code: "SU", short: "S", nameKey: "schedule.weekday.sunday", fallback: "Sunday" },
  { code: "MO", short: "M", nameKey: "schedule.weekday.monday", fallback: "Monday" },
  { code: "TU", short: "T", nameKey: "schedule.weekday.tuesday", fallback: "Tuesday" },
  { code: "WE", short: "W", nameKey: "schedule.weekday.wednesday", fallback: "Wednesday" },
  { code: "TH", short: "T", nameKey: "schedule.weekday.thursday", fallback: "Thursday" },
  { code: "FR", short: "F", nameKey: "schedule.weekday.friday", fallback: "Friday" },
  { code: "SA", short: "S", nameKey: "schedule.weekday.saturday", fallback: "Saturday" }
];
const DEFAULT_WINDOW = { start: "09:00", end: "17:00" };
function resolveRuleReasonEntryId(rule) {
  if (!rule) return null;
  if (typeof rule.unavailabilityReasonEntryId === "string" && rule.unavailabilityReasonEntryId.length) {
    return rule.unavailabilityReasonEntryId;
  }
  const fallback = rule.unavailability_reason_entry_id;
  return typeof fallback === "string" && fallback.length ? fallback : null;
}
function resolveRuleReasonValue(rule) {
  if (!rule) return null;
  if (typeof rule.unavailabilityReasonValue === "string" && rule.unavailabilityReasonValue.length) {
    return rule.unavailabilityReasonValue;
  }
  const fallback = rule.unavailability_reason_value;
  return typeof fallback === "string" && fallback.length ? fallback : null;
}
function createDefaultWindow() {
  return { ...DEFAULT_WINDOW };
}
function getDefaultWindowDurationMinutes() {
  const start = parseTimeInput(DEFAULT_WINDOW.start);
  const end = parseTimeInput(DEFAULT_WINDOW.end);
  if (!start || !end) return 60;
  const minutes = end.hours * 60 + end.minutes - (start.hours * 60 + start.minutes);
  return Math.max(1, minutes);
}
function formatTimeFromMinutes(totalMinutes) {
  const clamped = Math.max(0, Math.min(23 * 60 + 59, totalMinutes));
  const hours = String(Math.floor(clamped / 60)).padStart(2, "0");
  const minutes = String(clamped % 60).padStart(2, "0");
  return `${hours}:${minutes}`;
}
function buildNextWindow(windows) {
  if (windows.length === 0) return createDefaultWindow();
  const lastWindow = windows[windows.length - 1];
  const end = parseTimeInput(lastWindow.end);
  if (!end) return createDefaultWindow();
  const startMinutes = end.hours * 60 + end.minutes;
  if (startMinutes >= 23 * 60 + 59) return createDefaultWindow();
  const durationMinutes = getDefaultWindowDurationMinutes();
  const endMinutes = Math.min(startMinutes + durationMinutes, 23 * 60 + 59);
  if (endMinutes <= startMinutes) return createDefaultWindow();
  return {
    start: formatTimeFromMinutes(startMinutes),
    end: formatTimeFromMinutes(endMinutes)
  };
}
function getTimezoneOptions() {
  const options = /* @__PURE__ */ new Set();
  const resolved = Intl.DateTimeFormat().resolvedOptions().timeZone;
  if (resolved) options.add(resolved);
  options.add("UTC");
  const intlWithSupportedValues = Intl;
  if (typeof intlWithSupportedValues.supportedValuesOf === "function") {
    intlWithSupportedValues.supportedValuesOf("timeZone").forEach((timezone) => {
      if (timezone) options.add(timezone);
    });
  }
  return Array.from(options).sort((a, b) => a.localeCompare(b));
}
function formatTimeInput(value) {
  const hours = String(value.getHours()).padStart(2, "0");
  const minutes = String(value.getMinutes()).padStart(2, "0");
  return `${hours}:${minutes}`;
}
function formatDateInput(value) {
  const year = value.getFullYear();
  const month = String(value.getMonth() + 1).padStart(2, "0");
  const day = String(value.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
function parseDateInput(value) {
  const [year, month, day] = value.split("-").map((part) => Number(part));
  if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) return null;
  const date = new Date(year, month - 1, day);
  if (Number.isNaN(date.getTime())) return null;
  return date;
}
function parseTimeInput(value) {
  const [hours, minutes] = value.split(":").map((part) => Number(part));
  if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return null;
  if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;
  return { hours, minutes };
}
function toDateForWeekday(weekday, time) {
  const parsed = parseTimeInput(time);
  if (!parsed) return null;
  const now = /* @__PURE__ */ new Date();
  const base = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const diff = (weekday - base.getDay() + 7) % 7;
  const target = new Date(base.getTime() + diff * 24 * 60 * 60 * 1e3);
  target.setHours(parsed.hours, parsed.minutes, 0, 0);
  return target;
}
function toDateForDay(value, time) {
  if (!value) return null;
  const parsed = parseTimeInput(time);
  if (!parsed) return null;
  const parts = value.split("-").map((part) => Number(part));
  if (parts.length !== 3 || parts.some((part) => Number.isNaN(part))) return null;
  const [year, month, day] = parts;
  const date = new Date(year, month - 1, day, parsed.hours, parsed.minutes, 0, 0);
  return Number.isNaN(date.getTime()) ? null : date;
}
function getWeekdayIndex(value) {
  const date = toDateForDay(value, "00:00");
  return date ? date.getDay() : null;
}
function getWindowError(window2, labels) {
  const start = parseTimeInput(window2.start);
  const end = parseTimeInput(window2.end);
  if (!start || !end) return labels.windowErrorRequired;
  if (start.hours > end.hours || start.hours === end.hours && start.minutes >= end.minutes) {
    return labels.windowErrorRange;
  }
  return null;
}
function formatDuration(minutes) {
  const clamped = Math.max(1, minutes);
  const hours = Math.floor(clamped / 60);
  const mins = clamped % 60;
  if (hours > 0 && mins > 0) return `PT${hours}H${mins}M`;
  if (hours > 0) return `PT${hours}H`;
  return `PT${mins}M`;
}
function buildAvailabilityRrule(start, end, repeat) {
  const dtStart = start.toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
  const durationMinutes = Math.max(1, Math.round((end.getTime() - start.getTime()) / 6e4));
  const duration = formatDuration(durationMinutes);
  let rule = "";
  if (repeat === "once") {
    rule = "FREQ=DAILY;COUNT=1";
  } else if (repeat === "weekly") {
    const dayCode = DAY_LABELS[start.getDay()].code;
    rule = `FREQ=WEEKLY;BYDAY=${dayCode}`;
  } else {
    rule = "FREQ=DAILY";
  }
  return `DTSTART:${dtStart}
DURATION:${duration}
RRULE:${rule}`;
}
function groupRulesByDate(rules) {
  const map = /* @__PURE__ */ new Map();
  rules.forEach((rule) => {
    const window2 = parseAvailabilityRuleWindow(rule);
    const key = formatDateInput(window2.startAt);
    const list = map.get(key) ?? [];
    list.push(rule);
    map.set(key, list);
  });
  return map;
}
function buildWindowsFromRules(rules) {
  return rules.map((rule) => {
    const window2 = parseAvailabilityRuleWindow(rule);
    return {
      start: formatTimeInput(window2.startAt),
      end: formatTimeInput(window2.endAt)
    };
  }).sort((a, b) => a.start.localeCompare(b.start));
}
function createEmptyWeeklyWindows() {
  return Array.from({ length: 7 }, () => []);
}
function cloneWeeklyWindows(windows) {
  return windows.map((dayWindows) => dayWindows.map((window2) => ({ ...window2 })));
}
function normalizeWeeklyWindows(windows) {
  return windows.map((dayWindows) => {
    const unique = /* @__PURE__ */ new Map();
    dayWindows.forEach((window2) => {
      if (!window2.start || !window2.end) return;
      const key = `${window2.start}-${window2.end}`;
      if (!unique.has(key)) unique.set(key, { start: window2.start, end: window2.end });
    });
    return Array.from(unique.values()).sort((left, right) => left.start.localeCompare(right.start));
  });
}
function buildWeeklyDraft(rules) {
  const draft = createEmptyWeeklyWindows();
  rules.forEach((rule) => {
    const window2 = parseAvailabilityRuleWindow(rule);
    const repeat = window2.repeat;
    if (repeat === "once") return;
    const windowValue = {
      start: formatTimeInput(window2.startAt),
      end: formatTimeInput(window2.endAt)
    };
    if (repeat === "daily") {
      for (let day2 = 0; day2 < 7; day2 += 1) {
        draft[day2].push(windowValue);
      }
      return;
    }
    const day = window2.startAt.getDay();
    draft[day].push(windowValue);
  });
  return normalizeWeeklyWindows(draft);
}
function serializeWeeklyWindows(windows) {
  const payload = windows.map((dayWindows) => dayWindows.map((window2) => ({ start: window2.start, end: window2.end })));
  return JSON.stringify(payload);
}
function buildWeeklyPayload(windows) {
  const payload = [];
  const seen = /* @__PURE__ */ new Set();
  windows.forEach((dayWindows, day) => {
    dayWindows.forEach((window2) => {
      const start = toDateForWeekday(day, window2.start);
      const end = toDateForWeekday(day, window2.end);
      if (!start || !end || start >= end) return;
      const key = `${day}:${window2.start}:${window2.end}`;
      if (seen.has(key)) return;
      seen.add(key);
      payload.push({ weekday: day, start: window2.start, end: window2.end });
    });
  });
  return payload;
}
function AvailabilityRulesEditor({
  subjectType,
  subjectId,
  labelPrefix,
  mode: _mode = "availability",
  initialTimezone,
  rulesetId,
  onRulesetChange,
  allowRuleSetDelete,
  buildScheduleItems,
  loadBookedEvents,
  readOnly,
  allowUnavailability
}) {
  const t = useT();
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
  const mutationContextId = "planner-availability-rules-editor";
  const { runMutation, retryLastMutation } = useGuardedMutation({
    contextId: mutationContextId,
    blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
  });
  const mutationContext = React.useMemo(
    () => ({
      formId: mutationContextId,
      resourceKind: "planner.availability",
      retryLastMutation
    }),
    [mutationContextId, retryLastMutation]
  );
  const isReadOnly = Boolean(readOnly);
  const canManageUnavailability = allowUnavailability ?? true;
  const dialogRef = React.useRef(null);
  const createRuleSetDialogRef = React.useRef(null);
  const [availabilityRules, setAvailabilityRules] = React.useState([]);
  const [rulesetRules, setRulesetRules] = React.useState([]);
  const [availabilityLoading, setAvailabilityLoading] = React.useState(true);
  const [availabilityError, setAvailabilityError] = React.useState(null);
  const [ruleSets, setRuleSets] = React.useState([]);
  const [ruleSetsLoading, setRuleSetsLoading] = React.useState(false);
  const [bookedEvents, setBookedEvents] = React.useState([]);
  const [bookedLoading, setBookedLoading] = React.useState(false);
  const [bookedError, setBookedError] = React.useState(null);
  const [viewMode, setViewMode] = React.useState("list");
  const [scheduleView, setScheduleView] = React.useState("month");
  const [range, setRange] = React.useState(() => {
    const now = /* @__PURE__ */ new Date();
    const start = new Date(now.getFullYear(), now.getMonth(), 1);
    const end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
    return { start, end };
  });
  const [timezone, setTimezone] = React.useState(
    () => initialTimezone ?? (Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC")
  );
  const [timezoneDirty, setTimezoneDirty] = React.useState(false);
  const [editorOpen, setEditorOpen] = React.useState(false);
  const [editorScope, setEditorScope] = React.useState("date");
  const [editorDates, setEditorDates] = React.useState([]);
  const [editorWeekday, setEditorWeekday] = React.useState((/* @__PURE__ */ new Date()).getDay());
  const [editorWindows, setEditorWindows] = React.useState([createDefaultWindow()]);
  const [editorRules, setEditorRules] = React.useState([]);
  const [editorUnavailable, setEditorUnavailable] = React.useState(false);
  const [editorNote, setEditorNote] = React.useState("");
  const [editorReasonEntryId, setEditorReasonEntryId] = React.useState(null);
  const [editorReasonValue, setEditorReasonValue] = React.useState("");
  const [reasonEntriesById, setReasonEntriesById] = React.useState({});
  const [createRuleSetOpen, setCreateRuleSetOpen] = React.useState(false);
  const [isWeeklyAutoSaving, setIsWeeklyAutoSaving] = React.useState(false);
  const [customOverridesEnabled, setCustomOverridesEnabled] = React.useState(false);
  const [selectedRulesetId, setSelectedRulesetId] = React.useState(rulesetId ?? null);
  const autoSaveTimerRef = React.useRef(null);
  const lastSavedWeeklyKeyRef = React.useRef(null);
  const weeklySaveStateRef = React.useRef({ inFlight: false, queued: false });
  const timezoneSaveTimerRef = React.useRef(null);
  const timezoneSaveInFlightRef = React.useRef(false);
  const viewModeRef = React.useRef(viewMode);
  const saveWeeklyRef = React.useRef(null);
  const timezoneOptions = React.useMemo(() => getTimezoneOptions(), []);
  React.useEffect(() => {
    setSelectedRulesetId(rulesetId ?? null);
  }, [rulesetId]);
  const effectiveRulesetId = selectedRulesetId;
  const usingRuleSet = Boolean(effectiveRulesetId) && availabilityRules.length === 0 && !customOverridesEnabled;
  const activeRules = usingRuleSet ? rulesetRules : availabilityRules;
  const scheduleRules = React.useMemo(() => {
    const dateBlockers = /* @__PURE__ */ new Set();
    activeRules.forEach((rule) => {
      if (rule.kind !== "unavailability") return;
      const window2 = parseAvailabilityRuleWindow(rule);
      if (window2.repeat !== "once") return;
      dateBlockers.add(formatDateInput(window2.startAt));
    });
    if (!dateBlockers.size) return activeRules;
    return activeRules.filter((rule) => {
      const window2 = parseAvailabilityRuleWindow(rule);
      if (window2.repeat !== "once") return true;
      if (rule.kind === "unavailability") return true;
      return !dateBlockers.has(formatDateInput(window2.startAt));
    });
  }, [activeRules]);
  const scheduleItems = React.useMemo(
    () => buildScheduleItems({ availabilityRules: scheduleRules, bookedEvents, translate: t }),
    [bookedEvents, buildScheduleItems, scheduleRules, t]
  );
  const listLabels = React.useMemo(() => {
    const modeBase = `${labelPrefix}.availability`;
    return {
      title: t(`${labelPrefix}.availability.section.title`, "Availability"),
      weeklyTitle: t(`${labelPrefix}.availability.weekly.title`, "Weekly hours"),
      weeklySubtitle: t(`${labelPrefix}.availability.weekly.subtitle`, "Set when you are typically available."),
      dateSpecificTitle: t(`${labelPrefix}.availability.dateSpecific.title`, "Date-specific hours"),
      dateSpecificSubtitle: t(`${labelPrefix}.availability.dateSpecific.subtitle`, "Adjust hours for specific days."),
      addHours: t(`${labelPrefix}.availability.dateSpecific.add`, "Add hours"),
      timezoneLabel: t(`${labelPrefix}.availability.timezone`, "Timezone"),
      timezonePlaceholder: t(`${labelPrefix}.availability.timezone.placeholder`, "Search timezones..."),
      timezoneSaveError: t(`${labelPrefix}.availability.timezone.saveError`, "Failed to save timezone."),
      ruleSetLabel: t(`${labelPrefix}.availability.ruleset.label`, "Schedule"),
      ruleSetPlaceholder: t(`${labelPrefix}.availability.ruleset.placeholder`, "Custom schedule"),
      ruleSetCustomize: t(`${labelPrefix}.availability.ruleset.customize`, "Customize schedule"),
      ruleSetReset: t(`${labelPrefix}.availability.ruleset.reset`, "Reset to schedule"),
      ruleSetConfirm: t(`${labelPrefix}.availability.ruleset.confirm`, "Resetting to the schedule will delete your custom hours. Continue?"),
      ruleSetLoading: t(`${labelPrefix}.availability.ruleset.loading`, "Loading schedules..."),
      ruleSetError: t(`${labelPrefix}.availability.ruleset.error`, "Failed to load schedules."),
      ruleSetCreateLabel: t(`${labelPrefix}.availability.ruleset.create`, "New schedule"),
      ruleSetCreateTitle: t(`${labelPrefix}.availability.ruleset.createTitle`, "Save as schedule"),
      ruleSetCreateSubmit: t(`${labelPrefix}.availability.ruleset.createSubmit`, "Save schedule"),
      ruleSetCreateNameLabel: t(`${labelPrefix}.availability.ruleset.createName`, "Schedule name"),
      ruleSetCreateTimezoneLabel: t(`${labelPrefix}.availability.ruleset.createTimezone`, "Timezone"),
      ruleSetCreateSuccess: t(`${labelPrefix}.availability.ruleset.createSuccess`, "Schedule saved."),
      ruleSetCreateError: t(`${labelPrefix}.availability.ruleset.createError`, "Failed to save schedule."),
      ruleSetDelete: t(`${labelPrefix}.availability.ruleset.delete`, "Delete schedule"),
      ruleSetDeleteConfirm: t(`${labelPrefix}.availability.ruleset.deleteConfirm`, 'Delete schedule "{{name}}"?'),
      ruleSetDeleteSubmit: t(`${labelPrefix}.availability.ruleset.deleteSubmit`, "Delete"),
      ruleSetDeleteSuccess: t(`${labelPrefix}.availability.ruleset.deleteSuccess`, "Schedule deleted."),
      ruleSetDeleteError: t(`${labelPrefix}.availability.ruleset.deleteError`, "Failed to delete schedule."),
      editTitle: t(`${modeBase}.form.title.edit`, "Edit availability"),
      addTitle: t(`${modeBase}.form.title.create`, "Add availability"),
      applyLabel: t(`${labelPrefix}.availability.actions.apply`, "Apply"),
      cancelLabel: t("ui.forms.actions.cancel", "Cancel"),
      applyScopeLabel: t(`${labelPrefix}.availability.scope.label`, "Apply to"),
      applyScopeDate: t(`${labelPrefix}.availability.scope.date`, "This date"),
      applyScopeWeekday: t(`${labelPrefix}.availability.scope.weekday`, "Weekday:"),
      windowsLabel: t(`${labelPrefix}.availability.windows.label`, "What hours are you available?"),
      addWindow: t(`${labelPrefix}.availability.windows.add`, "Add window"),
      removeWindow: t(`${labelPrefix}.availability.windows.remove`, "Remove"),
      windowErrorRequired: t(`${labelPrefix}.availability.windows.errors.required`, "Start and end times are required."),
      windowErrorRange: t(`${labelPrefix}.availability.windows.errors.range`, "End time must be after start."),
      noHours: t(`${labelPrefix}.availability.weekly.empty`, "Unavailable"),
      saveWeekly: t(`${labelPrefix}.availability.weekly.save`, "Save weekly hours"),
      saveWeeklySuccess: t(`${labelPrefix}.availability.weekly.saved`, "Weekly hours saved."),
      saveWeeklyError: t(`${labelPrefix}.availability.weekly.error`, "Failed to save weekly hours."),
      saveDateSuccess: t(`${labelPrefix}.availability.dateSpecific.saved`, "Date-specific hours saved."),
      saveDateError: t(`${labelPrefix}.availability.dateSpecific.error`, "Failed to save date-specific hours."),
      unavailableLabel: t(`${labelPrefix}.availability.unavailable.label`, "Unavailable on this day"),
      unavailableHelp: t(`${labelPrefix}.availability.unavailable.help`, "Blocks availability for the entire day."),
      unavailableTitle: t(`${labelPrefix}.availability.unavailable.title`, "Unavailable"),
      unavailableNoteLabel: t(`${labelPrefix}.availability.unavailable.note`, "Note"),
      unavailableNotePlaceholder: t(`${labelPrefix}.availability.unavailable.notePlaceholder`, "Holiday"),
      unavailableReasonLabel: t(`${labelPrefix}.availability.unavailable.reason.label`, "Reason"),
      unavailableReasonPlaceholder: t(`${labelPrefix}.availability.unavailable.reason.placeholder`, "Select a reason"),
      unavailableReasonAddLabel: t(`${labelPrefix}.availability.unavailable.reason.addLabel`, "Add reason"),
      unavailableReasonAddPrompt: t(`${labelPrefix}.availability.unavailable.reason.addPrompt`, "Name the reason"),
      unavailableReasonDialogTitle: t(`${labelPrefix}.availability.unavailable.reason.dialogTitle`, "Add reason"),
      unavailableReasonValueLabel: t(`${labelPrefix}.availability.unavailable.reason.valueLabel`, "Reason"),
      unavailableReasonValuePlaceholder: t(`${labelPrefix}.availability.unavailable.reason.valuePlaceholder`, "Reason name"),
      unavailableReasonLabelLabel: t(`${labelPrefix}.availability.unavailable.reason.labelLabel`, "Label"),
      unavailableReasonLabelPlaceholder: t(`${labelPrefix}.availability.unavailable.reason.labelPlaceholder`, "Display label (optional)"),
      unavailableReasonEmptyError: t(`${labelPrefix}.availability.unavailable.reason.emptyError`, "Please enter a reason"),
      unavailableReasonSaveLabel: t(`${labelPrefix}.availability.unavailable.reason.saveLabel`, "Save"),
      unavailableReasonErrorLoad: t(`${labelPrefix}.availability.unavailable.reason.errorLoad`, "Failed to load reasons"),
      unavailableReasonErrorSave: t(`${labelPrefix}.availability.unavailable.reason.errorSave`, "Failed to save reason"),
      unavailableReasonLoading: t(`${labelPrefix}.availability.unavailable.reason.loading`, "Loading reasons..."),
      unavailableReasonManageTitle: t(`${labelPrefix}.availability.unavailable.reason.manageTitle`, "Manage reasons"),
      scheduleError: t(`${labelPrefix}.schedule.error.load`, "Failed to load schedule."),
      scheduleLoading: t(`${labelPrefix}.schedule.loading`, "Loading schedule..."),
      customizePrompt: t(`${labelPrefix}.availability.ruleset.customizePrompt`, "This schedule is based on a shared ruleset. Customize it to make changes."),
      calendarLabel: t(`${labelPrefix}.availability.view.calendar`, "Calendar"),
      listLabel: t(`${labelPrefix}.availability.view.list`, "List"),
      editAllLabel: t(`${labelPrefix}.availability.scope.weekdayShort`, "All {{weekday}}s", { weekday: DAY_LABELS[editorWeekday].fallback }),
      addDateLabel: t(`${labelPrefix}.availability.dateSpecific.addDate`, "Add date")
    };
  }, [editorWeekday, labelPrefix, t]);
  const unavailableReasonLabels = React.useMemo(() => ({
    placeholder: listLabels.unavailableReasonPlaceholder,
    addLabel: listLabels.unavailableReasonAddLabel,
    addPrompt: listLabels.unavailableReasonAddPrompt,
    dialogTitle: listLabels.unavailableReasonDialogTitle,
    valueLabel: listLabels.unavailableReasonValueLabel,
    valuePlaceholder: listLabels.unavailableReasonValuePlaceholder,
    labelLabel: listLabels.unavailableReasonLabelLabel,
    labelPlaceholder: listLabels.unavailableReasonLabelPlaceholder,
    emptyError: listLabels.unavailableReasonEmptyError,
    cancelLabel: listLabels.cancelLabel,
    saveLabel: listLabels.unavailableReasonSaveLabel,
    errorLoad: listLabels.unavailableReasonErrorLoad,
    errorSave: listLabels.unavailableReasonErrorSave,
    loadingLabel: listLabels.unavailableReasonLoading,
    manageTitle: listLabels.unavailableReasonManageTitle
  }), [listLabels]);
  const refreshAvailability = React.useCallback(async () => {
    if (!subjectId) return;
    setAvailabilityLoading(true);
    setAvailabilityError(null);
    try {
      const params = new URLSearchParams({
        page: "1",
        pageSize: "100",
        subjectType,
        subjectIds: subjectId
      });
      const call = await apiCall(`/api/planner/availability?${params.toString()}`);
      const items = Array.isArray(call.result?.items) ? call.result.items : [];
      setAvailabilityRules(items);
    } catch (error2) {
      const message = error2 instanceof Error ? error2.message : t(`${labelPrefix}.availability.error.load`, "Failed to load availability.");
      setAvailabilityError(message);
    } finally {
      setAvailabilityLoading(false);
    }
  }, [labelPrefix, subjectId, subjectType, t]);
  const refreshRuleSetRules = React.useCallback(async () => {
    if (!effectiveRulesetId) {
      setRulesetRules([]);
      return;
    }
    try {
      const params = new URLSearchParams({
        page: "1",
        pageSize: "100",
        subjectType: "ruleset",
        subjectIds: effectiveRulesetId
      });
      const call = await apiCall(`/api/planner/availability?${params.toString()}`);
      const items = Array.isArray(call.result?.items) ? call.result.items : [];
      setRulesetRules(items);
    } catch {
      setRulesetRules([]);
    }
  }, [effectiveRulesetId]);
  const refreshRuleSets = React.useCallback(async () => {
    if (!onRulesetChange) return;
    setRuleSetsLoading(true);
    try {
      const params = new URLSearchParams({ page: "1", pageSize: "100" });
      const call = await apiCall(`/api/planner/availability-rule-sets?${params.toString()}`);
      const items = Array.isArray(call.result?.items) ? call.result.items : [];
      if (!effectiveRulesetId || items.some((entry) => entry.id === effectiveRulesetId)) {
        setRuleSets(items);
        return;
      }
      const selectedParams = new URLSearchParams({
        page: "1",
        pageSize: "1",
        ids: effectiveRulesetId
      });
      const selectedCall = await apiCall(
        `/api/planner/availability-rule-sets?${selectedParams.toString()}`
      );
      const selected = Array.isArray(selectedCall.result?.items) ? selectedCall.result.items[0] : null;
      setRuleSets(selected ? [selected, ...items] : items);
    } catch {
      setRuleSets([]);
    } finally {
      setRuleSetsLoading(false);
    }
  }, [effectiveRulesetId, onRulesetChange]);
  const fetchUnavailabilityReasonOptions = React.useCallback(async () => {
    const entries = await loadUnavailabilityReasonEntries(subjectType);
    const map = {};
    entries.forEach((entry) => {
      map[entry.id] = entry;
    });
    setReasonEntriesById(map);
    return entries.map((entry) => ({
      value: entry.id,
      label: entry.label ?? entry.value,
      color: entry.color,
      icon: entry.icon
    }));
  }, [subjectType]);
  const createUnavailabilityReasonOption = React.useCallback(async (input) => {
    const entry = await createUnavailabilityReasonEntry(subjectType, input);
    if (!entry) return null;
    setReasonEntriesById((prev) => ({ ...prev, [entry.id]: entry }));
    return {
      value: entry.id,
      label: entry.label ?? entry.value,
      color: entry.color,
      icon: entry.icon
    };
  }, [subjectType]);
  React.useEffect(() => {
    if (!editorReasonEntryId) {
      if (editorReasonValue) setEditorReasonValue("");
      return;
    }
    const entry = reasonEntriesById[editorReasonEntryId];
    if (entry && entry.value !== editorReasonValue) {
      setEditorReasonValue(entry.value);
    }
  }, [editorReasonEntryId, editorReasonValue, reasonEntriesById]);
  React.useEffect(() => {
    void refreshAvailability();
  }, [refreshAvailability]);
  React.useEffect(() => {
    void refreshRuleSetRules();
  }, [refreshRuleSetRules]);
  React.useEffect(() => {
    if (viewMode !== "calendar") return;
    void refreshAvailability();
    void refreshRuleSetRules();
  }, [refreshAvailability, refreshRuleSetRules, viewMode]);
  React.useEffect(() => {
    void refreshRuleSets();
  }, [refreshRuleSets]);
  React.useEffect(() => {
    viewModeRef.current = viewMode;
  }, [viewMode]);
  React.useEffect(() => {
    if (timezoneDirty) return;
    if (initialTimezone) {
      setTimezone(initialTimezone);
      return;
    }
    const ruleTimezone = activeRules.find((rule) => rule.timezone)?.timezone;
    if (ruleTimezone) setTimezone(ruleTimezone);
  }, [activeRules, initialTimezone, timezoneDirty]);
  React.useEffect(() => {
    if (timezoneDirty) return;
    if (!usingRuleSet || !effectiveRulesetId) return;
    const ruleset = ruleSets.find((entry) => entry.id === effectiveRulesetId);
    if (ruleset?.timezone) setTimezone(ruleset.timezone);
  }, [effectiveRulesetId, ruleSets, timezoneDirty, usingRuleSet]);
  React.useEffect(() => {
    if (availabilityRules.length > 0) {
      setCustomOverridesEnabled(true);
    }
  }, [availabilityRules.length]);
  React.useEffect(() => {
    if (!effectiveRulesetId) {
      setCustomOverridesEnabled(false);
    }
  }, [effectiveRulesetId]);
  const refreshBookedEvents = React.useCallback(async () => {
    if (!loadBookedEvents) return;
    setBookedLoading(true);
    setBookedError(null);
    try {
      const items = await loadBookedEvents(range);
      setBookedEvents(Array.isArray(items) ? items : []);
    } catch (error2) {
      const message = error2 instanceof Error ? error2.message : listLabels.scheduleError;
      setBookedError(message);
    } finally {
      setBookedLoading(false);
    }
  }, [listLabels.scheduleError, loadBookedEvents, range]);
  React.useEffect(() => {
    void refreshBookedEvents();
  }, [refreshBookedEvents]);
  const refreshAfterRuleSetConflict = React.useCallback(() => {
    void refreshRuleSets();
    void refreshRuleSetRules();
    void refreshAvailability();
  }, [refreshAvailability, refreshRuleSetRules, refreshRuleSets]);
  const weeklyDraft = React.useMemo(() => buildWeeklyDraft(activeRules), [activeRules]);
  const [weeklyWindows, setWeeklyWindows] = React.useState(() => cloneWeeklyWindows(weeklyDraft));
  const weeklyWindowsRef = React.useRef(cloneWeeklyWindows(weeklyDraft));
  const weeklyDirtyRef = React.useRef(false);
  const weeklyDraftKey = React.useMemo(() => serializeWeeklyWindows(weeklyDraft), [weeklyDraft]);
  const weeklyKey = React.useMemo(() => serializeWeeklyWindows(weeklyWindows), [weeklyWindows]);
  const weeklyWindowErrors = React.useMemo(
    () => weeklyWindows.map((dayWindows) => dayWindows.map((window2) => getWindowError(window2, listLabels))),
    [listLabels, weeklyWindows]
  );
  const weeklyHasErrors = React.useMemo(
    () => weeklyWindowErrors.some((errors) => errors.some(Boolean)),
    [weeklyWindowErrors]
  );
  React.useEffect(() => {
    if (weeklyDirtyRef.current) return;
    const nextWindows = cloneWeeklyWindows(weeklyDraft);
    setWeeklyWindows(nextWindows);
    weeklyWindowsRef.current = nextWindows;
  }, [weeklyDraft]);
  React.useEffect(() => {
    lastSavedWeeklyKeyRef.current = weeklyDraftKey;
  }, [weeklyDraftKey]);
  const dateSpecificRules = React.useMemo(
    () => activeRules.filter((rule) => parseAvailabilityRuleWindow(rule).repeat === "once"),
    [activeRules]
  );
  const dateGroups = React.useMemo(() => groupRulesByDate(dateSpecificRules), [dateSpecificRules]);
  const isLoading = availabilityLoading || bookedLoading;
  const error = availabilityError ?? bookedError;
  React.useEffect(() => {
    weeklyWindowsRef.current = weeklyWindows;
  }, [weeklyWindows]);
  const handleWeeklyWindowChange = React.useCallback((day, index, next) => {
    weeklyDirtyRef.current = true;
    setWeeklyWindows((prev) => {
      const nextWindows = prev.map((dayWindows) => [...dayWindows]);
      const list = nextWindows[day] ?? [];
      list[index] = next;
      nextWindows[day] = list;
      return nextWindows;
    });
  }, []);
  const handleWeeklyWindowAdd = React.useCallback((day) => {
    weeklyDirtyRef.current = true;
    setWeeklyWindows((prev) => {
      const nextWindows = prev.map((dayWindows) => [...dayWindows]);
      const list = nextWindows[day] ?? [];
      list.push(buildNextWindow(list));
      nextWindows[day] = list;
      return nextWindows;
    });
  }, []);
  const handleWeeklyWindowRemove = React.useCallback((day, index) => {
    weeklyDirtyRef.current = true;
    setWeeklyWindows((prev) => {
      const nextWindows = prev.map((dayWindows) => [...dayWindows]);
      const list = nextWindows[day] ?? [];
      list.splice(index, 1);
      nextWindows[day] = list;
      return nextWindows;
    });
  }, []);
  const saveWeeklyHours = React.useCallback(async (options) => {
    if (isReadOnly) return;
    const subjectForRules = usingRuleSet ? "ruleset" : subjectType;
    const subjectIdForRules = usingRuleSet ? effectiveRulesetId ?? "" : subjectId;
    if (!subjectIdForRules) return;
    if (weeklyHasErrors) return;
    const parentRuleSet = subjectForRules === "ruleset" ? ruleSets.find((entry) => entry.id === subjectIdForRules) ?? null : null;
    const shouldSkipRefresh = Boolean(options?.skipRefresh);
    setIsWeeklyAutoSaving(options?.silentSuccess === true);
    try {
      const windows = buildWeeklyPayload(normalizeWeeklyWindows(weeklyWindowsRef.current));
      await runMutation({
        operation: () => withOptimisticLockForRuleSet(parentRuleSet, () => apiCallOrThrow("/api/planner/availability-weekly", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            subjectType: subjectForRules,
            subjectId: subjectIdForRules,
            timezone,
            windows
          })
        }, { errorMessage: listLabels.saveWeeklyError })),
        context: mutationContext,
        mutationPayload: { action: "save-weekly", subjectType: subjectForRules, subjectId: subjectIdForRules }
      });
      lastSavedWeeklyKeyRef.current = weeklyKey;
      weeklyDirtyRef.current = false;
      if (!options?.silentSuccess) {
        flash(listLabels.saveWeeklySuccess, "success");
      }
      if (!shouldSkipRefresh) {
        await refreshAvailability();
        await refreshRuleSetRules();
        if (usingRuleSet) {
          await refreshRuleSets();
        }
      }
      if (parentRuleSet) {
        await refreshRuleSets();
      }
    } catch (error2) {
      if (surfaceRecordConflict(error2, t, { onRefresh: refreshAfterRuleSetConflict })) return;
      const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
      flash(message, "error");
    } finally {
      setIsWeeklyAutoSaving(false);
    }
  }, [
    listLabels.saveWeeklyError,
    listLabels.saveWeeklySuccess,
    refreshAvailability,
    refreshRuleSetRules,
    refreshRuleSets,
    refreshAfterRuleSetConflict,
    effectiveRulesetId,
    ruleSets,
    subjectId,
    subjectType,
    t,
    timezone,
    usingRuleSet,
    weeklyHasErrors,
    weeklyKey,
    isReadOnly,
    mutationContext,
    runMutation
  ]);
  React.useEffect(() => {
    saveWeeklyRef.current = saveWeeklyHours;
  }, [saveWeeklyHours]);
  const queueWeeklySave = React.useCallback((options) => {
    if (weeklySaveStateRef.current.inFlight) {
      weeklySaveStateRef.current.queued = true;
      return;
    }
    weeklySaveStateRef.current.inFlight = true;
    void (async () => {
      try {
        await saveWeeklyRef.current?.(options);
      } finally {
        weeklySaveStateRef.current.inFlight = false;
        if (weeklySaveStateRef.current.queued) {
          weeklySaveStateRef.current.queued = false;
          queueWeeklySave({ silentSuccess: true, skipRefresh: viewModeRef.current === "list" });
        }
      }
    })();
  }, []);
  React.useEffect(() => {
    if (usingRuleSet) return;
    if (weeklyHasErrors) return;
    if (weeklyKey === lastSavedWeeklyKeyRef.current) return;
    if (autoSaveTimerRef.current !== null) {
      window.clearTimeout(autoSaveTimerRef.current);
    }
    autoSaveTimerRef.current = window.setTimeout(() => {
      queueWeeklySave({ silentSuccess: true, skipRefresh: viewMode === "list" });
    }, 600);
    return () => {
      if (autoSaveTimerRef.current !== null) {
        window.clearTimeout(autoSaveTimerRef.current);
      }
    };
  }, [queueWeeklySave, usingRuleSet, viewMode, weeklyHasErrors, weeklyKey]);
  const persistTimezone = React.useCallback(async (nextTimezone) => {
    if (isReadOnly) return;
    if (timezoneSaveInFlightRef.current) return;
    const trimmedTimezone = nextTimezone.trim() || "UTC";
    const rulesetTimezoneId = subjectType === "ruleset" ? subjectId : effectiveRulesetId;
    const rulesToUpdate = activeRules.filter((rule) => rule.timezone !== trimmedTimezone);
    if (!rulesToUpdate.length && !rulesetTimezoneId) {
      setTimezoneDirty(false);
      return;
    }
    timezoneSaveInFlightRef.current = true;
    try {
      const updates = [];
      rulesToUpdate.forEach((rule) => {
        updates.push(withOptimisticLockForRule(rule, () => updateCrud("planner/availability", {
          id: rule.id,
          timezone: trimmedTimezone
        })));
      });
      if (rulesetTimezoneId) {
        const ruleSet = ruleSets.find((entry) => entry.id === rulesetTimezoneId) ?? null;
        updates.push(withOptimisticLockForRuleSet(ruleSet, () => updateCrud("planner/availability-rule-sets", {
          id: rulesetTimezoneId,
          timezone: trimmedTimezone
        })));
      }
      if (updates.length) {
        await runMutation({
          operation: () => Promise.all(updates),
          context: mutationContext,
          mutationPayload: { action: "update-timezone", timezone: trimmedTimezone }
        });
        await refreshAvailability();
        await refreshRuleSetRules();
      }
      setTimezoneDirty(false);
    } catch (error2) {
      const message = error2 instanceof Error ? error2.message : listLabels.timezoneSaveError;
      flash(message, "error");
      setTimezoneDirty(false);
    } finally {
      timezoneSaveInFlightRef.current = false;
    }
  }, [
    activeRules,
    listLabels.timezoneSaveError,
    refreshAvailability,
    refreshRuleSetRules,
    ruleSets,
    effectiveRulesetId,
    subjectId,
    subjectType,
    isReadOnly,
    mutationContext,
    runMutation
  ]);
  React.useEffect(() => {
    if (!timezoneDirty) return;
    const nextTimezone = timezone.trim();
    if (!nextTimezone) return;
    if (timezoneSaveTimerRef.current !== null) {
      window.clearTimeout(timezoneSaveTimerRef.current);
    }
    timezoneSaveTimerRef.current = window.setTimeout(() => {
      void persistTimezone(nextTimezone);
    }, 400);
    return () => {
      if (timezoneSaveTimerRef.current !== null) {
        window.clearTimeout(timezoneSaveTimerRef.current);
      }
    };
  }, [persistTimezone, timezone, timezoneDirty]);
  const handleTimezoneChange = React.useCallback((nextTimezone) => {
    if (isReadOnly) return;
    const trimmed = nextTimezone.trim();
    setTimezone(trimmed || "UTC");
    setTimezoneDirty(true);
  }, [canManageUnavailability, isReadOnly]);
  const handleCustomize = React.useCallback(async () => {
    if (isReadOnly) return;
    if (!effectiveRulesetId) return;
    try {
      const creations = rulesetRules.map((rule) => createCrud("planner/availability", {
        subjectType,
        subjectId,
        timezone: rule.timezone,
        rrule: rule.rrule,
        exdates: rule.exdates ?? [],
        kind: rule.kind ?? "availability",
        note: rule.note ?? null
      }));
      await runMutation({
        operation: () => Promise.all(creations),
        context: mutationContext,
        mutationPayload: { action: "customize", count: creations.length }
      });
      setCustomOverridesEnabled(true);
      await refreshAvailability();
    } catch (error2) {
      const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
      flash(message, "error");
    }
  }, [effectiveRulesetId, listLabels.saveWeeklyError, refreshAvailability, rulesetRules, subjectId, subjectType, isReadOnly, mutationContext, runMutation]);
  const handleResetToRuleSet = React.useCallback(async () => {
    if (isReadOnly) return;
    if (!effectiveRulesetId) return;
    if (requiresResetConfirmation(availabilityRules)) {
      const confirmed = await confirm({
        title: listLabels.ruleSetConfirm,
        variant: "destructive"
      });
      if (!confirmed) return;
    }
    try {
      const idsToDelete = selectCustomRuleIdsToDelete("reset", availabilityRules);
      const idsToDeleteSet = new Set(idsToDelete);
      await runMutation({
        operation: () => Promise.all(
          availabilityRules.filter((rule) => idsToDeleteSet.has(rule.id)).map((rule) => withOptimisticLockForRule(
            rule,
            () => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError })
          ))
        ),
        context: mutationContext,
        mutationPayload: { action: "reset-to-ruleset", ids: idsToDelete }
      });
      setCustomOverridesEnabled(false);
      await refreshAvailability();
    } catch (error2) {
      const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
      flash(message, "error");
    }
  }, [availabilityRules, confirm, effectiveRulesetId, listLabels.ruleSetConfirm, listLabels.saveWeeklyError, refreshAvailability, isReadOnly, mutationContext, runMutation]);
  const handleRuleSetChange = React.useCallback(async (nextId) => {
    if (isReadOnly) return;
    if (!onRulesetChange) return;
    const idsToDelete = selectCustomRuleIdsToDelete("switch", availabilityRules);
    if (idsToDelete.length) {
      const idsToDeleteSet = new Set(idsToDelete);
      await runMutation({
        operation: () => Promise.all(
          availabilityRules.filter((rule) => idsToDeleteSet.has(rule.id)).map((rule) => withOptimisticLockForRule(
            rule,
            () => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError })
          ))
        ),
        context: mutationContext,
        mutationPayload: { action: "switch-ruleset", ids: idsToDelete }
      });
    }
    setSelectedRulesetId(nextId);
    setCustomOverridesEnabled(false);
    try {
      await onRulesetChange(nextId);
      await refreshAvailability();
    } catch (error2) {
      setSelectedRulesetId(effectiveRulesetId);
      throw error2;
    }
  }, [
    availabilityRules,
    effectiveRulesetId,
    listLabels.saveWeeklyError,
    onRulesetChange,
    refreshAvailability,
    isReadOnly,
    mutationContext,
    runMutation
  ]);
  const handleDeleteRuleSet = React.useCallback(async () => {
    if (isReadOnly || !onRulesetChange || !rulesetId) return;
    const selected = ruleSets.find((entry) => entry.id === rulesetId);
    const name = selected?.name ?? rulesetId;
    await deleteAvailabilityRuleSet({
      ruleSetId: rulesetId,
      confirmDelete: () => confirm({
        title: listLabels.ruleSetDeleteConfirm.replace("{{name}}", name),
        confirmText: listLabels.ruleSetDeleteSubmit,
        variant: "destructive"
      }),
      deleteRuleSet: async (id) => {
        await runMutation({
          operation: () => withOptimisticLockForRuleSet(selected, () => deleteCrud("planner/availability-rule-sets", id, { errorMessage: listLabels.ruleSetDeleteError })),
          context: mutationContext,
          mutationPayload: { action: "delete-ruleset", id }
        });
      },
      clearAssignment: async () => {
        setCustomOverridesEnabled(false);
        await onRulesetChange(null);
        await refreshAvailability();
      },
      refreshRuleSets,
      onSuccess: () => flash(listLabels.ruleSetDeleteSuccess, "success"),
      onError: (error2) => {
        if (surfaceRecordConflict(error2, t, { onRefresh: refreshAfterRuleSetConflict })) return;
        logger.error("Failed to delete availability rule set", { err: error2 });
        const normalized = normalizeCrudServerError(error2);
        flash(normalized.message ?? listLabels.ruleSetDeleteError, "error");
      }
    });
  }, [
    confirm,
    isReadOnly,
    listLabels.ruleSetDeleteConfirm,
    listLabels.ruleSetDeleteError,
    listLabels.ruleSetDeleteSubmit,
    listLabels.ruleSetDeleteSuccess,
    onRulesetChange,
    refreshAvailability,
    refreshAfterRuleSetConflict,
    refreshRuleSets,
    ruleSets,
    rulesetId,
    t,
    mutationContext,
    runMutation
  ]);
  const ruleSetFormSchema = React.useMemo(
    () => z.object({
      name: z.string().min(1, t("ui.forms.errors.required", "Required"))
    }),
    [t]
  );
  const ruleSetFormFields = React.useMemo(() => [
    {
      id: "name",
      label: listLabels.ruleSetCreateNameLabel,
      type: "text",
      required: true,
      placeholder: listLabels.ruleSetCreateNameLabel
    }
  ], [listLabels.ruleSetCreateNameLabel]);
  const ruleSetInitialValues = React.useMemo(() => ({
    name: ""
  }), []);
  const handleCreateRuleSet = React.useCallback(async (values) => {
    if (isReadOnly) return;
    const name = values.name.trim();
    const timezoneValue = timezone;
    const response = await createCrud("planner/availability-rule-sets", {
      name,
      timezone: timezoneValue,
      description: null
    }, { errorMessage: listLabels.ruleSetCreateError });
    const id = typeof response.result?.id === "string" ? response.result.id : null;
    if (!id) throw new Error(listLabels.ruleSetCreateError);
    const creations = [];
    const weeklyWindows2 = buildWeeklyPayload(normalizeWeeklyWindows(weeklyWindowsRef.current));
    weeklyWindows2.forEach((window2) => {
      const start = toDateForWeekday(window2.weekday, window2.start);
      const end = toDateForWeekday(window2.weekday, window2.end);
      if (!start || !end) return;
      const rrule = buildAvailabilityRrule(start, end, "weekly");
      creations.push(createCrud("planner/availability", {
        subjectType: "ruleset",
        subjectId: id,
        timezone: timezoneValue,
        rrule,
        exdates: [],
        kind: "availability",
        note: null
      }, { errorMessage: listLabels.ruleSetCreateError }));
    });
    if (dateSpecificRules.length) {
      dateSpecificRules.forEach((rule) => {
        creations.push(createCrud("planner/availability", {
          subjectType: "ruleset",
          subjectId: id,
          timezone: rule.timezone || timezoneValue,
          rrule: rule.rrule,
          exdates: rule.exdates ?? [],
          kind: rule.kind ?? "availability",
          note: rule.note ?? null,
          unavailabilityReasonEntryId: resolveRuleReasonEntryId(rule),
          unavailabilityReasonValue: resolveRuleReasonValue(rule)
        }, { errorMessage: listLabels.ruleSetCreateError }));
      });
    }
    if (creations.length) {
      await Promise.all(creations);
    }
    await refreshRuleSets();
    if (onRulesetChange) {
      setSelectedRulesetId(id);
      await onRulesetChange(id);
      await refreshAvailability();
    }
    await refreshRuleSetRules();
    flash(listLabels.ruleSetCreateSuccess, "success");
    setCreateRuleSetOpen(false);
  }, [
    activeRules,
    listLabels.ruleSetCreateError,
    listLabels.ruleSetCreateSuccess,
    dateSpecificRules,
    onRulesetChange,
    refreshAvailability,
    refreshRuleSetRules,
    refreshRuleSets,
    timezone,
    isReadOnly
  ]);
  const openEditor = React.useCallback((scope, options) => {
    if (isReadOnly) return;
    setEditorScope(scope);
    const rules = options?.rules ?? [];
    const unavailableRule = rules.find((rule) => rule.kind === "unavailability");
    if (unavailableRule && !canManageUnavailability) return;
    setEditorRules(rules);
    setEditorUnavailable(scope === "date" && Boolean(unavailableRule));
    setEditorNote(unavailableRule?.note ?? "");
    setEditorReasonEntryId(scope === "date" ? resolveRuleReasonEntryId(unavailableRule) : null);
    setEditorReasonValue(scope === "date" ? resolveRuleReasonValue(unavailableRule) ?? "" : "");
    if (scope === "date") {
      const date = options?.date ?? /* @__PURE__ */ new Date();
      const windows = buildWindowsFromRules(rules);
      setEditorDates([formatDateInput(date)]);
      setEditorWeekday(date.getDay());
      setEditorWindows(windows.length ? windows : [createDefaultWindow()]);
    } else {
      const weekday = options?.weekday ?? (/* @__PURE__ */ new Date()).getDay();
      const windows = buildWindowsFromRules(rules);
      setEditorWeekday(weekday);
      setEditorDates([]);
      setEditorWindows(windows.length ? windows : [createDefaultWindow()]);
      setEditorUnavailable(false);
      setEditorNote("");
      setEditorReasonEntryId(null);
      setEditorReasonValue("");
    }
    setEditorOpen(true);
  }, [isReadOnly]);
  const handleEditorWindowChange = React.useCallback((index, window2) => {
    setEditorWindows((prev) => {
      const next = [...prev];
      next[index] = window2;
      return next;
    });
  }, []);
  const handleEditorWindowAdd = React.useCallback(() => {
    setEditorWindows((prev) => [...prev, buildNextWindow(prev)]);
  }, []);
  const handleEditorWindowRemove = React.useCallback((index) => {
    setEditorWindows((prev) => prev.filter((_, idx) => idx !== index));
  }, []);
  const handleEditorDateAdd = React.useCallback(() => {
    setEditorDates((prev) => {
      const lastValue = [...prev].reverse().find((value) => value && value.length) ?? "";
      const base = lastValue ? parseDateInput(lastValue) : null;
      const nextBase = base ?? /* @__PURE__ */ new Date();
      const nextDate = new Date(nextBase.getFullYear(), nextBase.getMonth(), nextBase.getDate() + 1);
      return [...prev, formatDateInput(nextDate)];
    });
  }, []);
  const handleEditorDateChange = React.useCallback((index, value) => {
    setEditorDates((prev) => {
      const next = [...prev];
      next[index] = value;
      return next;
    });
  }, []);
  const handleEditorDateRemove = React.useCallback((index) => {
    setEditorDates((prev) => prev.filter((_, idx) => idx !== index));
  }, []);
  const editorWindowErrors = React.useMemo(
    () => editorUnavailable ? [] : editorWindows.map((window2) => getWindowError(window2, listLabels)),
    [editorUnavailable, editorWindows, listLabels]
  );
  const handleEditorSubmit = React.useCallback(async () => {
    if (isReadOnly) return;
    if (editorUnavailable && !canManageUnavailability) return;
    const subjectForRules = usingRuleSet ? "ruleset" : subjectType;
    const subjectIdForRules = usingRuleSet ? effectiveRulesetId ?? "" : subjectId;
    if (!subjectIdForRules) return;
    if (!editorUnavailable && editorWindowErrors.some(Boolean)) return;
    const validWindows = editorWindows;
    try {
      const dates = Array.from(new Set(editorDates.filter((value) => value && value.length)));
      if (editorScope === "date") {
        const trimmedNote = editorNote.trim();
        const reasonEntryId = editorUnavailable ? editorReasonEntryId : null;
        const fallbackReasonValue = editorReasonValue.trim();
        const reasonValue = reasonEntryId ? reasonEntriesById[reasonEntryId]?.value ?? (fallbackReasonValue || null) : null;
        const payload = {
          subjectType: subjectForRules,
          subjectId: subjectIdForRules,
          timezone,
          dates,
          windows: editorUnavailable ? [] : validWindows,
          kind: editorUnavailable ? "unavailability" : "availability",
          note: editorUnavailable && trimmedNote.length ? trimmedNote : null,
          unavailabilityReasonEntryId: editorUnavailable ? reasonEntryId : null,
          unavailabilityReasonValue: editorUnavailable ? reasonValue : null
        };
        await withOptimisticLockForDateSpecific(editorRules, () => apiCallOrThrow("/api/planner/availability-date-specific", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload)
        }, { errorMessage: listLabels.saveDateError }));
      } else {
        const rulesToDelete = editorRules;
        const uniqueRulesById = new Map(rulesToDelete.map((rule) => [rule.id, rule]));
        await Promise.all(
          Array.from(uniqueRulesById.values()).map((rule) => withOptimisticLockForRule(
            rule,
            () => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveDateError })
          ))
        );
        const creations = [];
        validWindows.forEach((window2) => {
          const start = toDateForWeekday(editorWeekday, window2.start);
          const end = toDateForWeekday(editorWeekday, window2.end);
          if (!start || !end) return;
          const rrule = buildAvailabilityRrule(start, end, "weekly");
          creations.push(createCrud("planner/availability", {
            subjectType: subjectForRules,
            subjectId: subjectIdForRules,
            timezone,
            rrule,
            exdates: [],
            kind: "availability",
            note: null
          }, { errorMessage: listLabels.saveDateError }));
        });
        await Promise.all(creations);
      }
      flash(listLabels.saveDateSuccess, "success");
      setEditorOpen(false);
      setEditorRules([]);
      await refreshAvailability();
      await refreshRuleSetRules();
    } catch (error2) {
      if (surfaceRecordConflict(error2, t)) return;
      const message = error2 instanceof Error ? error2.message : listLabels.saveDateError;
      flash(message, "error");
    }
  }, [
    editorDates,
    editorRules,
    editorScope,
    editorUnavailable,
    editorNote,
    editorReasonEntryId,
    editorReasonValue,
    editorWeekday,
    editorWindowErrors,
    editorWindows,
    listLabels.saveDateError,
    listLabels.saveDateSuccess,
    refreshAvailability,
    refreshRuleSetRules,
    reasonEntriesById,
    effectiveRulesetId,
    subjectId,
    subjectType,
    timezone,
    usingRuleSet,
    canManageUnavailability,
    isReadOnly,
    t
  ]);
  const handleSlotClick = React.useCallback((slot) => {
    if (usingRuleSet || isReadOnly) return;
    openEditor("date", { date: slot.start });
  }, [openEditor, usingRuleSet, isReadOnly]);
  const handleItemClick = React.useCallback((item) => {
    if (usingRuleSet || isReadOnly) return;
    const rule = item.metadata?.rule;
    if (!rule) return;
    const window2 = parseAvailabilityRuleWindow(rule);
    if (window2.repeat === "weekly") {
      const weekday = window2.startAt.getDay();
      const rules = activeRules.filter((candidate) => {
        const candidateWindow = parseAvailabilityRuleWindow(candidate);
        return candidateWindow.repeat === "weekly" && candidateWindow.startAt.getDay() === weekday;
      });
      openEditor("weekday", { weekday, rules });
    } else {
      const dateKey = formatDateInput(window2.startAt);
      const rules = activeRules.filter((candidate) => {
        const candidateWindow = parseAvailabilityRuleWindow(candidate);
        return candidateWindow.repeat === "once" && formatDateInput(candidateWindow.startAt) === dateKey;
      });
      openEditor("date", { date: window2.startAt, rules });
    }
  }, [activeRules, openEditor, usingRuleSet, isReadOnly]);
  return /* @__PURE__ */ jsxs(Fragment, { children: [
    /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-card p-4 shadow-sm", children: [
      /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
        /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: listLabels.title }) }),
        /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
          /* @__PURE__ */ jsx(
            Button,
            {
              type: "button",
              variant: "outline",
              size: "icon",
              onClick: () => setViewMode("list"),
              "aria-label": listLabels.listLabel,
              title: listLabels.listLabel,
              className: viewMode === "list" ? "bg-accent text-accent-foreground" : void 0,
              children: /* @__PURE__ */ jsx(List, { className: "size-4", "aria-hidden": true })
            }
          ),
          /* @__PURE__ */ jsx(
            Button,
            {
              type: "button",
              variant: "outline",
              size: "icon",
              onClick: () => setViewMode("calendar"),
              "aria-label": listLabels.calendarLabel,
              title: listLabels.calendarLabel,
              className: viewMode === "calendar" ? "bg-accent text-accent-foreground" : void 0,
              children: /* @__PURE__ */ jsx(Calendar, { className: "size-4", "aria-hidden": true })
            }
          )
        ] })
      ] }),
      /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-3 rounded-lg border bg-muted/30 p-3", children: [
        onRulesetChange ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
          /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: listLabels.ruleSetLabel }),
          ruleSetsLoading ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: listLabels.ruleSetLoading }) : /* @__PURE__ */ jsxs(
            Select,
            {
              value: resolveRuleSetSelectValue(ruleSets, effectiveRulesetId),
              onValueChange: (value) => {
                void handleRuleSetChange(value ? value : null);
              },
              disabled: isReadOnly,
              children: [
                /* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "w-auto min-w-[10rem]", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: listLabels.ruleSetPlaceholder }) }),
                /* @__PURE__ */ jsx(SelectContent, { children: ruleSets.map((ruleSet) => /* @__PURE__ */ jsx(SelectItem, { value: ruleSet.id, children: ruleSet.name }, ruleSet.id)) })
              ]
            }
          ),
          /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setCreateRuleSetOpen(true), disabled: isReadOnly, children: [
            /* @__PURE__ */ jsx(Plus, { className: "size-4 mr-2", "aria-hidden": true }),
            listLabels.ruleSetCreateLabel
          ] }),
          effectiveRulesetId && usingRuleSet ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleCustomize, disabled: isReadOnly, children: listLabels.ruleSetCustomize }) : null,
          effectiveRulesetId && !usingRuleSet ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleResetToRuleSet, disabled: isReadOnly, children: listLabels.ruleSetReset }) : null,
          allowRuleSetDelete && rulesetId ? /* @__PURE__ */ jsxs(
            Button,
            {
              type: "button",
              variant: "destructive-outline",
              size: "sm",
              onClick: () => {
                void handleDeleteRuleSet();
              },
              disabled: isReadOnly,
              children: [
                /* @__PURE__ */ jsx(Trash2, { className: "size-4 mr-2", "aria-hidden": true }),
                listLabels.ruleSetDelete
              ]
            }
          ) : null
        ] }) : null,
        /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
          /* @__PURE__ */ jsx("span", { children: listLabels.timezoneLabel }),
          /* @__PURE__ */ jsx("div", { className: "w-[220px]", children: /* @__PURE__ */ jsx(
            ComboboxInput,
            {
              value: timezone,
              onChange: handleTimezoneChange,
              suggestions: timezoneOptions,
              placeholder: listLabels.timezonePlaceholder,
              allowCustomValues: true,
              disabled: isReadOnly
            }
          ) })
        ] })
      ] }),
      /* @__PURE__ */ jsx("div", { className: "mt-6", children: isLoading ? /* @__PURE__ */ jsx(LoadingMessage, { label: listLabels.scheduleLoading }) : error ? /* @__PURE__ */ jsx(ErrorMessage, { label: error }) : viewMode === "calendar" ? /* @__PURE__ */ jsx(
        ScheduleView,
        {
          items: scheduleItems,
          view: scheduleView,
          range,
          timezone,
          onRangeChange: setRange,
          onViewChange: setScheduleView,
          onTimezoneChange: handleTimezoneChange,
          onItemClick: handleItemClick,
          onSlotClick: handleSlotClick
        }
      ) : /* @__PURE__ */ jsxs("div", { className: "grid gap-6 lg:grid-cols-2", children: [
        usingRuleSet ? /* @__PURE__ */ jsx("div", { className: "lg:col-span-2 rounded-lg border border-dashed p-4 text-sm text-muted-foreground", children: listLabels.customizePrompt }) : null,
        /* @__PURE__ */ jsxs("section", { children: [
          /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
            /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
              /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: listLabels.weeklyTitle }),
              isWeeklyAutoSaving ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 text-xs text-muted-foreground", children: [
                /* @__PURE__ */ jsx(Spinner, { size: "sm" }),
                t("ui.forms.status.saving", "Saving...")
              ] }) : null
            ] }),
            /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: listLabels.weeklySubtitle })
          ] }) }),
          /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-3", children: DAY_LABELS.map((day, index) => {
            const windows = weeklyWindows[index] ?? [];
            return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-3 rounded-lg border bg-background p-3", children: [
              /* @__PURE__ */ jsx("div", { className: "flex w-10 justify-center", children: /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-muted text-xs font-semibold", children: day.short }) }),
              /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
                windows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: listLabels.noHours }) : windows.map((window2, windowIndex) => {
                  const windowError = weeklyWindowErrors[index]?.[windowIndex] ?? null;
                  const errorClass = windowError ? "border-red-500 aria-invalid:ring-destructive" : "";
                  return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
                    /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
                      /* @__PURE__ */ jsx(
                        TimePicker,
                        {
                          value: window2.start,
                          onChange: (value) => handleWeeklyWindowChange(index, windowIndex, { ...window2, start: value ?? "" }),
                          className: `w-[120px] ${errorClass}`,
                          disabled: usingRuleSet || isReadOnly,
                          showClearButton: false
                        }
                      ),
                      /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "-" }),
                      /* @__PURE__ */ jsx(
                        TimePicker,
                        {
                          value: window2.end,
                          onChange: (value) => handleWeeklyWindowChange(index, windowIndex, { ...window2, end: value ?? "" }),
                          className: `w-[120px] ${errorClass}`,
                          disabled: usingRuleSet || isReadOnly,
                          showClearButton: false
                        }
                      ),
                      /* @__PURE__ */ jsx(
                        Button,
                        {
                          type: "button",
                          variant: "outline",
                          size: "icon",
                          onClick: () => handleWeeklyWindowRemove(index, windowIndex),
                          disabled: usingRuleSet || isReadOnly,
                          "aria-label": listLabels.removeWindow,
                          children: /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": true })
                        }
                      )
                    ] }),
                    windowError ? /* @__PURE__ */ jsx("div", { className: "text-xs text-red-600", children: windowError }) : null
                  ] }, `${day.code}-${windowIndex}`);
                }),
                /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => handleWeeklyWindowAdd(index), disabled: usingRuleSet || isReadOnly, children: [
                  /* @__PURE__ */ jsx(Plus, { className: "size-4 mr-2", "aria-hidden": true }),
                  listLabels.addWindow
                ] })
              ] })
            ] }, day.code);
          }) })
        ] }),
        /* @__PURE__ */ jsxs("section", { children: [
          /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
            /* @__PURE__ */ jsxs("div", { children: [
              /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: listLabels.dateSpecificTitle }),
              /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: listLabels.dateSpecificSubtitle })
            ] }),
            /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => openEditor("date"), disabled: usingRuleSet || isReadOnly, children: [
              /* @__PURE__ */ jsx(Clock, { className: "size-4 mr-2", "aria-hidden": true }),
              listLabels.addHours
            ] })
          ] }),
          /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-3", children: Array.from(dateGroups.entries()).map(([date, rules]) => {
            const weekdayIndex = getWeekdayIndex(date);
            const weekdayLabel = weekdayIndex === null ? null : DAY_LABELS[weekdayIndex];
            const weekdayText = weekdayLabel ? t(weekdayLabel.nameKey, weekdayLabel.fallback) : date;
            const weekdayShort = weekdayLabel ? weekdayLabel.short : "?";
            const unavailableRule = rules.find((rule) => rule.kind === "unavailability");
            const unavailableReason = resolveRuleReasonValue(unavailableRule);
            const lockUnavailabilityActions = Boolean(unavailableRule) && !canManageUnavailability;
            return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-3 rounded-lg border bg-background p-3", children: [
              /* @__PURE__ */ jsx("div", { className: "flex w-10 justify-center pt-1", children: /* @__PURE__ */ jsx(
                "span",
                {
                  className: "flex h-8 w-8 items-center justify-center rounded-full bg-muted text-xs font-semibold",
                  "aria-label": weekdayText,
                  title: weekdayText,
                  children: weekdayShort
                }
              ) }),
              /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
                /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
                  /* @__PURE__ */ jsxs("div", { children: [
                    /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-semibold", children: [
                      /* @__PURE__ */ jsx(Calendar, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
                      /* @__PURE__ */ jsx("span", { children: date })
                    ] }),
                    /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: weekdayText })
                  ] }),
                  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
                    /* @__PURE__ */ jsxs(
                      Button,
                      {
                        type: "button",
                        variant: "outline",
                        size: "sm",
                        onClick: () => openEditor("date", { date: /* @__PURE__ */ new Date(`${date}T00:00:00`), rules }),
                        disabled: usingRuleSet || isReadOnly || lockUnavailabilityActions,
                        children: [
                          /* @__PURE__ */ jsx(PencilLine, { className: "size-4 mr-2", "aria-hidden": true }),
                          listLabels.editTitle
                        ]
                      }
                    ),
                    /* @__PURE__ */ jsx(
                      Button,
                      {
                        type: "button",
                        variant: "outline",
                        size: "icon",
                        onClick: async () => {
                          await runMutation({
                            operation: () => Promise.all(
                              rules.map((rule) => withOptimisticLockForRule(
                                rule,
                                () => deleteCrud("planner/availability", rule.id)
                              ))
                            ),
                            context: mutationContext,
                            mutationPayload: { action: "delete-date-rules", count: rules.length }
                          });
                          await refreshAvailability();
                          await refreshRuleSetRules();
                        },
                        disabled: usingRuleSet || isReadOnly || lockUnavailabilityActions,
                        "aria-label": listLabels.removeWindow,
                        children: /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": true })
                      }
                    )
                  ] })
                ] }),
                /* @__PURE__ */ jsx("div", { className: "space-y-1 text-sm text-muted-foreground", children: unavailableRule ? /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
                  /* @__PURE__ */ jsx("div", { className: "font-medium text-foreground", children: listLabels.unavailableTitle }),
                  unavailableReason ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: unavailableReason }) : null,
                  unavailableRule.note ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: unavailableRule.note }) : null
                ] }) : buildWindowsFromRules(rules).map((window2, index) => /* @__PURE__ */ jsxs("div", { children: [
                  window2.start,
                  " - ",
                  window2.end
                ] }, `${date}-${index}`)) })
              ] })
            ] }, date);
          }) })
        ] })
      ] }) })
    ] }),
    /* @__PURE__ */ jsx(
      Dialog,
      {
        open: editorOpen,
        onOpenChange: (open) => {
          if (!open) {
            setEditorOpen(false);
            setEditorRules([]);
            setEditorUnavailable(false);
            setEditorNote("");
            setEditorReasonEntryId(null);
            setEditorReasonValue("");
          }
        },
        children: /* @__PURE__ */ jsxs(
          DialogContent,
          {
            ref: dialogRef,
            onKeyDown: (event) => {
              if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
                event.preventDefault();
                void handleEditorSubmit();
              }
            },
            children: [
              /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: editorRules.length ? listLabels.editTitle : listLabels.addTitle }) }),
              /* @__PURE__ */ jsx(
                CrudForm,
                {
                  embedded: true,
                  fields: [
                    {
                      id: "availabilityEditor",
                      label: listLabels.applyScopeLabel,
                      type: "custom",
                      component: () => /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-muted/30 p-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
                        /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs("div", { role: "tablist", "aria-label": listLabels.applyScopeLabel, className: "inline-flex rounded-lg border bg-muted p-1 text-xs", children: [
                          /* @__PURE__ */ jsx(
                            Button,
                            {
                              type: "button",
                              role: "tab",
                              "aria-selected": editorScope === "date",
                              variant: "ghost",
                              size: "sm",
                              className: `h-auto rounded-md px-3 py-1.5 font-medium ${editorScope === "date" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
                              onClick: () => setEditorScope("date"),
                              children: listLabels.applyScopeDate
                            }
                          ),
                          /* @__PURE__ */ jsx(
                            Button,
                            {
                              type: "button",
                              role: "tab",
                              "aria-selected": editorScope === "weekday",
                              variant: "ghost",
                              size: "sm",
                              className: `h-auto rounded-md px-3 py-1.5 font-medium ${editorScope === "weekday" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
                              onClick: () => {
                                setEditorScope("weekday");
                                setEditorUnavailable(false);
                                setEditorNote("");
                                setEditorReasonEntryId(null);
                                setEditorReasonValue("");
                              },
                              children: listLabels.editAllLabel
                            }
                          )
                        ] }) }),
                        editorScope === "date" ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
                          editorDates.map((value, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
                            /* @__PURE__ */ jsx(
                              Input,
                              {
                                type: "date",
                                value,
                                onChange: (event) => handleEditorDateChange(index, event.target.value)
                              }
                            ),
                            /* @__PURE__ */ jsx(
                              Button,
                              {
                                type: "button",
                                variant: "outline",
                                size: "icon",
                                onClick: () => handleEditorDateRemove(index),
                                "aria-label": listLabels.removeWindow,
                                children: /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": true })
                              }
                            )
                          ] }, `${value}-${index}`)),
                          /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleEditorDateAdd, children: [
                            /* @__PURE__ */ jsx(Plus, { className: "size-4 mr-2", "aria-hidden": true }),
                            listLabels.addDateLabel
                          ] }),
                          /* @__PURE__ */ jsxs(
                            "label",
                            {
                              htmlFor: "availability-unavailable-toggle",
                              className: "flex cursor-pointer items-start gap-2 pt-2 text-sm",
                              children: [
                                /* @__PURE__ */ jsx(
                                  "input",
                                  {
                                    id: "availability-unavailable-toggle",
                                    type: "checkbox",
                                    className: "mt-0.5 size-4",
                                    checked: editorUnavailable,
                                    disabled: !canManageUnavailability,
                                    onChange: (event) => {
                                      const checked = event.target.checked;
                                      setEditorUnavailable(checked);
                                      if (!checked) {
                                        setEditorNote("");
                                        setEditorReasonEntryId(null);
                                        setEditorReasonValue("");
                                      }
                                    }
                                  }
                                ),
                                /* @__PURE__ */ jsxs("div", { children: [
                                  /* @__PURE__ */ jsx("div", { className: "font-medium text-foreground", children: listLabels.unavailableLabel }),
                                  /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: listLabels.unavailableHelp })
                                ] })
                              ]
                            }
                          ),
                          editorUnavailable ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
                            /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
                              /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: listLabels.unavailableReasonLabel }),
                              /* @__PURE__ */ jsx(
                                DictionaryEntrySelect,
                                {
                                  value: editorReasonEntryId ?? void 0,
                                  onChange: (next) => {
                                    setEditorReasonEntryId(next ?? null);
                                    setEditorReasonValue("");
                                  },
                                  fetchOptions: fetchUnavailabilityReasonOptions,
                                  createOption: createUnavailabilityReasonOption,
                                  labels: unavailableReasonLabels,
                                  selectClassName: "w-full",
                                  sortOptions: "none",
                                  manageHref: "/backend/config/dictionaries",
                                  disabled: !canManageUnavailability
                                }
                              )
                            ] }),
                            /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
                              /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: listLabels.unavailableNoteLabel }),
                              /* @__PURE__ */ jsx(
                                Input,
                                {
                                  type: "text",
                                  value: editorNote,
                                  placeholder: listLabels.unavailableNotePlaceholder,
                                  disabled: !canManageUnavailability,
                                  onChange: (event) => setEditorNote(event.target.value)
                                }
                              )
                            ] })
                          ] }) : null
                        ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
                          /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: listLabels.applyScopeWeekday }),
                          /* @__PURE__ */ jsxs(
                            Select,
                            {
                              value: String(editorWeekday),
                              onValueChange: (value) => setEditorWeekday(Number(value)),
                              children: [
                                /* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "w-auto min-w-[8rem]", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
                                /* @__PURE__ */ jsx(SelectContent, { children: DAY_LABELS.map((day, index) => /* @__PURE__ */ jsx(SelectItem, { value: String(index), children: t(day.nameKey, day.fallback) }, day.code)) })
                              ]
                            }
                          )
                        ] }),
                        editorUnavailable && editorScope === "date" ? null : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
                          /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: listLabels.windowsLabel }),
                          editorWindows.map((window2, index) => {
                            const windowError = editorWindowErrors[index] ?? null;
                            const errorClass = windowError ? "border-red-500 aria-invalid:ring-destructive" : "";
                            return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
                              /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
                                /* @__PURE__ */ jsx(
                                  TimePicker,
                                  {
                                    value: window2.start,
                                    onChange: (value) => handleEditorWindowChange(index, { ...window2, start: value ?? "" }),
                                    className: `w-[120px] ${errorClass}`,
                                    showClearButton: false
                                  }
                                ),
                                /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "-" }),
                                /* @__PURE__ */ jsx(
                                  TimePicker,
                                  {
                                    value: window2.end,
                                    onChange: (value) => handleEditorWindowChange(index, { ...window2, end: value ?? "" }),
                                    className: `w-[120px] ${errorClass}`,
                                    showClearButton: false
                                  }
                                ),
                                /* @__PURE__ */ jsx(
                                  Button,
                                  {
                                    type: "button",
                                    variant: "outline",
                                    size: "icon",
                                    onClick: () => handleEditorWindowRemove(index),
                                    "aria-label": listLabels.removeWindow,
                                    children: /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": true })
                                  }
                                )
                              ] }),
                              windowError ? /* @__PURE__ */ jsx("div", { className: "text-xs text-red-600", children: windowError }) : null
                            ] }, `${index}-${window2.start}`);
                          }),
                          /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleEditorWindowAdd, children: [
                            /* @__PURE__ */ jsx(Plus, { className: "size-4 mr-2", "aria-hidden": true }),
                            listLabels.addWindow
                          ] })
                        ] })
                      ] }) })
                    }
                  ],
                  onSubmit: handleEditorSubmit,
                  submitLabel: listLabels.applyLabel,
                  extraActions: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => setEditorOpen(false), children: listLabels.cancelLabel })
                }
              )
            ]
          }
        )
      }
    ),
    /* @__PURE__ */ jsx(
      Dialog,
      {
        open: createRuleSetOpen,
        onOpenChange: (open) => setCreateRuleSetOpen(open),
        children: /* @__PURE__ */ jsxs(
          DialogContent,
          {
            ref: createRuleSetDialogRef,
            onKeyDown: (event) => {
              if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
                event.preventDefault();
                const form = createRuleSetDialogRef.current?.querySelector("form");
                form?.requestSubmit();
              }
            },
            children: [
              /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: listLabels.ruleSetCreateTitle }) }),
              /* @__PURE__ */ jsx(
                CrudForm,
                {
                  embedded: true,
                  schema: ruleSetFormSchema,
                  fields: ruleSetFormFields,
                  initialValues: ruleSetInitialValues,
                  onSubmit: handleCreateRuleSet,
                  submitLabel: listLabels.ruleSetCreateSubmit,
                  extraActions: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => setCreateRuleSetOpen(false), children: listLabels.cancelLabel })
                }
              )
            ]
          }
        )
      }
    ),
    ConfirmDialogElement
  ] });
}
export {
  AvailabilityRulesEditor
};
//# sourceMappingURL=AvailabilityRulesEditor.js.map
