import {
  useMemo,
  useState,
  type FormEvent,
} from "react";
import { Trash2 } from "lucide-react";
import {
  Button,
  DateField,
  Dialog,
  SegmentedControl,
  TimeField,
  ToggleSwitch,
  useUnsavedDraftGuard,
} from "@govoplan/core-webui";
import type {
  CalendarCollection,
  CalendarEvent,
  CalendarEventCreatePayload,
} from "../../api/calendar";
import {
  addDays,
  addHours,
  addMinutesToTime,
  calendarDraftKey,
  errorText,
  localDateTime,
  startOfDay,
  toInputDate,
  toInputTime,
} from "./calendarViewModel";

type CalendarEventEndMode = "end" | "duration";
type CalendarEventEditScope = "occurrence" | "series";
type CalendarEventAdvancedPayload = Pick<
  CalendarEventCreatePayload,
  | "organizer"
  | "attendees"
  | "categories"
  | "rrule"
  | "rdate"
  | "exdate"
  | "reminders"
  | "attachments"
  | "related_to"
  | "icalendar"
  | "metadata"
>;

export function CalendarEventDialog({
  calendars,
  defaultCalendarId,
  event,
  editScope,
  canChooseSeries,
  seriesLoaded,
  focusDate,
  saving,
  canWrite,
  canDelete,
  onEditScopeChange,
  onCancel,
  onSave,
  onDelete
}: {
  calendars: CalendarCollection[];
  defaultCalendarId: string;
  event: CalendarEvent | null;
  editScope: CalendarEventEditScope;
  canChooseSeries: boolean;
  seriesLoaded: boolean;
  focusDate: Date;
  saving: boolean;
  canWrite: boolean;
  canDelete: boolean;
  onEditScopeChange: (scope: CalendarEventEditScope) => void;
  onCancel: () => void;
  onSave: (
    payload: CalendarEventCreatePayload,
    event: CalendarEvent | null,
    editScope: CalendarEventEditScope
  ) => Promise<boolean>;
  onDelete: (
    event: CalendarEvent,
    editScope: CalendarEventEditScope
  ) => Promise<void>;
}) {
  const initialStart = event ? new Date(event.start_at) : focusDate;
  const initialEnd = event?.end_at ? new Date(event.end_at) : addHours(initialStart, 1);
  const initialAllDayEnd = event?.all_day && event.end_at ? addDays(startOfDay(initialEnd), -1) : initialEnd;
  const initialDurationSeconds = event?.duration_seconds ?? Math.max(3600, Math.round((initialEnd.getTime() - initialStart.getTime()) / 1000));
  const [summary, setSummary] = useState(event?.summary ?? "");
  const [calendarId, setCalendarId] = useState(event?.calendar_id ?? defaultCalendarId);
  const [description, setDescription] = useState(event?.description ?? "");
  const [location, setLocation] = useState(event?.location ?? "");
  const [startDate, setStartDate] = useState(toInputDate(initialStart));
  const [endDate, setEndDate] = useState(toInputDate(initialAllDayEnd < initialStart ? initialStart : initialAllDayEnd));
  const [startTime, setStartTime] = useState(toInputTime(initialStart));
  const [endTime, setEndTime] = useState(toInputTime(initialEnd));
  const [allDay, setAllDay] = useState(event?.all_day ?? false);
  const [endMode, setEndMode] = useState<CalendarEventEndMode>(event && eventUsesDuration(event) ? "duration" : "end");
  const [durationSeconds, setDurationSeconds] = useState(String(initialDurationSeconds));
  const [uid, setUid] = useState(event?.uid ?? "");
  const [recurrenceId, setRecurrenceId] = useState(event?.recurrence_id ?? "");
  const [sequence, setSequence] = useState(String(event?.sequence ?? 0));
  const [status, setStatus] = useState(event?.status ?? "CONFIRMED");
  const [transparency, setTransparency] = useState(event?.transparency ?? "OPAQUE");
  const [classification, setClassification] = useState(event?.classification ?? "PUBLIC");
  const [timezone, setTimezone] = useState(event?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC");
  const [categoriesText, setCategoriesText] = useState((event?.categories ?? []).join(", "));
  const [organizerJson, setOrganizerJson] = useState(jsonTextareaValue(event?.organizer ?? null));
  const [attendeesJson, setAttendeesJson] = useState(jsonTextareaValue(event?.attendees ?? []));
  const [rruleText, setRruleText] = useState(event?.rrule ? serializeRRuleText(event.rrule) : "");
  const [rdateJson, setRdateJson] = useState(jsonTextareaValue(event?.rdate ?? []));
  const [exdateJson, setExdateJson] = useState(jsonTextareaValue(event?.exdate ?? []));
  const [remindersJson, setRemindersJson] = useState(jsonTextareaValue(event?.reminders ?? []));
  const [attachmentsJson, setAttachmentsJson] = useState(jsonTextareaValue(event?.attachments ?? []));
  const [relatedToJson, setRelatedToJson] = useState(jsonTextareaValue(event?.related_to ?? []));
  const [sourceKind, setSourceKind] = useState(event?.source_kind ?? "local");
  const [sourceHref, setSourceHref] = useState(event?.source_href ?? "");
  const [etag, setEtag] = useState(event?.etag ?? "");
  const [icalendarJson, setICalendarJson] = useState(jsonTextareaValue(event?.icalendar ?? {}));
  const [metadataJson, setMetadataJson] = useState(jsonTextareaValue(event?.metadata ?? {}));
  const [formError, setFormError] = useState("");
  const [confirmingDelete, setConfirmingDelete] = useState(false);
  const formId = "calendar-event-form";
  const eventDraft = {
    summary,
    calendarId,
    description,
    location,
    startDate,
    endDate,
    startTime,
    endTime,
    allDay,
    endMode,
    durationSeconds,
    uid,
    recurrenceId,
    sequence,
    status,
    transparency,
    classification,
    timezone,
    categoriesText,
    organizerJson,
    attendeesJson,
    rruleText,
    rdateJson,
    exdateJson,
    remindersJson,
    attachmentsJson,
    relatedToJson,
    sourceKind,
    sourceHref,
    etag,
    icalendarJson,
    metadataJson
  };
  const initialEventDraftKey = useMemo(() => calendarDraftKey(eventDraft), []);
  const eventDirty = calendarDraftKey(eventDraft) !== initialEventDraftKey;

  useUnsavedDraftGuard({
    dirty: eventDirty,
    onSave: saveCurrent,
    onDiscard: onCancel
  });

  function handleAllDayChange(next: boolean) {
    setAllDay(next);
    if (next) {
      setStartTime("00:00");
      setEndTime("00:00");
    } else if (startTime === "00:00" && endTime === "00:00") {
      setStartTime("09:00");
      setEndTime("10:00");
    }
  }

  function handleStartDateChange(next: string) {
    setStartDate(next);
    if (endDate < next) setEndDate(next);
  }

  function handleStartTimeChange(next: string) {
    setStartTime(next);
    if (startDate === endDate && endTime <= next) setEndTime(addMinutesToTime(next, 60));
  }

  async function saveCurrent(): Promise<boolean> {
    setFormError("");
    const startAt = allDay ? localDateTime(startDate, "00:00") : localDateTime(startDate, startTime);
    const parsedDurationSeconds = Math.max(0, Number(durationSeconds) || 0);
    const usesDuration = !allDay && endMode === "duration";
    const endAt = allDay ?
    addDays(localDateTime(endDate, "00:00"), 1) :
    usesDuration ?
    new Date(startAt.getTime() + parsedDurationSeconds * 1000) :
    localDateTime(endDate, endTime);
    if (usesDuration && parsedDurationSeconds <= 0) {
      setFormError("i18n:govoplan-calendar.duration_must_be_greater_than_zero.519292f7");
      return false;
    }
    if (endAt <= startAt) {
      setFormError(allDay ? "i18n:govoplan-calendar.end_date_must_be_on_or_after_start_date.a2518865" : "i18n:govoplan-calendar.end_date_and_time_must_be_after_start_date_and_t.daf31831");
      return false;
    }
    let advanced: CalendarEventAdvancedPayload;
    try {
      advanced = {
        organizer: parseJsonObjectOrNull(organizerJson, "i18n:govoplan-calendar.organizer.debd1720"),
        attendees: parseJsonArray(attendeesJson, "i18n:govoplan-calendar.attendees.a45a0962"),
        categories: commaSeparatedValues(categoriesText),
        rrule: parseRRuleInput(rruleText),
        rdate: parseJsonArray(rdateJson, "RDATE"),
        exdate: parseJsonArray(exdateJson, "EXDATE"),
        reminders: parseJsonArray(remindersJson, "i18n:govoplan-calendar.reminders.ae8c3939"),
        attachments: parseJsonArray(attachmentsJson, "i18n:govoplan-calendar.attachments.6771ade6"),
        related_to: parseJsonArray(relatedToJson, "i18n:govoplan-calendar.related_to.0e7989ff"),
        icalendar: withDurationMode(parseJsonObject(icalendarJson, "i18n:govoplan-calendar.icalendar.f388476d"), usesDuration),
        metadata: parseJsonObject(metadataJson, "i18n:govoplan-calendar.metadata.251edc0e")
      };
    } catch (err) {
      setFormError(errorText(err));
      return false;
    }
    const payload: CalendarEventCreatePayload = {
      calendar_id: calendarId,
      summary,
      description: description || null,
      location: location || null,
      sequence: Math.max(0, Number(sequence) || 0),
      status,
      transparency,
      classification,
      start_at: startAt.toISOString(),
      end_at: endAt.toISOString(),
      duration_seconds: usesDuration ? parsedDurationSeconds : null,
      all_day: allDay,
      timezone: timezone.trim() || null,
      source_kind: sourceKind.trim() || "local",
      source_href: sourceHref.trim() || null,
      etag: etag.trim() || null,
      ...advanced
    };
    if (!event) {
      if (uid.trim()) payload.uid = uid.trim();
      if (recurrenceId.trim()) payload.recurrence_id = recurrenceId.trim();
    }
    return onSave(payload, event, editScope);
  }

  function submit(formEvent: FormEvent<HTMLFormElement>) {
    formEvent.preventDefault();
    void saveCurrent();
  }

  function requestDelete() {
    if (!event) return;
    if (!confirmingDelete) {
      setConfirmingDelete(true);
      return;
    }
    void onDelete(event, editScope);
  }

  return (
    <Dialog
      open
      title={event ? "i18n:govoplan-calendar.edit_event.a7028454" : "i18n:govoplan-calendar.new_event.2ef3795c"}
      className="calendar-event-dialog calendar-vevent-dialog"
      footerClassName="calendar-event-dialog-footer"
      closeDisabled={saving}
      onClose={onCancel}
      footer={
      <>
          <div>
            {event && canDelete &&
          <Button type="button" variant="danger" onClick={requestDelete} disabled={saving}>
                <Trash2 size={16} /> {confirmingDelete ? "i18n:govoplan-calendar.confirm_delete.c9f2829e" : "i18n:govoplan-calendar.delete.f6fdbe48"}
              </Button>
          }
          </div>
          <div className="calendar-dialog-actions">
            <Button type="button" onClick={onCancel} disabled={saving}>i18n:govoplan-calendar.cancel.77dfd213</Button>
            <Button type="submit" form={formId} variant="primary" disabled={saving || !canWrite}>{saving ? "i18n:govoplan-calendar.saving.ae7e8875" : "i18n:govoplan-calendar.save.efc007a3"}</Button>
          </div>
        </>
      }>

      <form id={formId} className="calendar-dialog-form" onSubmit={submit}>
        {canChooseSeries && (
          <SegmentedControl
            ariaLabel="Recurring event edit scope"
            value={editScope}
            onChange={onEditScopeChange}
            width="fill"
            size="equal"
            disabled={saving}
            options={[
              { id: "occurrence", label: "This occurrence" },
              {
                id: "series",
                label: seriesLoaded ? "Whole series" : "Loading series",
                disabled: !seriesLoaded
              }
            ]}
          />
        )}
        {formError && <p className="calendar-form-error">{formError}</p>}
        <label>
          <span>i18n:govoplan-calendar.title.768e0c1c</span>
          <input value={summary} onChange={(item) => setSummary(item.target.value)} required maxLength={500} autoFocus disabled={saving || !canWrite} />
        </label>
        <label>
          <span>i18n:govoplan-calendar.calendar.adab5090</span>
          <select value={calendarId} onChange={(item) => setCalendarId(item.target.value)} required disabled={saving || !canWrite}>
            {calendars.map((calendar) =>
            <option key={calendar.id} value={calendar.id}>{calendar.name}</option>
            )}
          </select>
        </label>
        <label>
          <span>i18n:govoplan-calendar.description.55f8ebc8</span>
          <textarea value={description} onChange={(item) => setDescription(item.target.value)} rows={4} disabled={saving || !canWrite} />
        </label>
        <label>
          <span>i18n:govoplan-calendar.location.d219c681</span>
          <input value={location} onChange={(item) => setLocation(item.target.value)} maxLength={500} disabled={saving || !canWrite} />
        </label>
        <ToggleSwitch label="i18n:govoplan-calendar.whole_day.951c82d1" checked={allDay} disabled={saving || !canWrite} onChange={handleAllDayChange} />
        <div className="calendar-dialog-date-row">
          <label>
            <span>i18n:govoplan-calendar.start_date.ff99f5b5</span>
            <DateField value={startDate} onChange={handleStartDateChange} required disabled={saving || !canWrite} />
          </label>
          <label>
            <span>i18n:govoplan-calendar.start_time.88d8206d</span>
            <TimeField value={startTime} onChange={handleStartTimeChange} disabled={saving || !canWrite || allDay} />
          </label>
        </div>
        {!allDay &&
        <div className="calendar-dialog-date-row">
            <label>
              <span>i18n:govoplan-calendar.end_mode.5a06de37</span>
              <select value={endMode} onChange={(item) => setEndMode(item.target.value as CalendarEventEndMode)} disabled={saving || !canWrite}>
                <option value="end">i18n:govoplan-calendar.end_time.cd7800da</option>
                <option value="duration">i18n:govoplan-calendar.duration.1370004d</option>
              </select>
            </label>
            {endMode === "duration" &&
          <label>
                <span>i18n:govoplan-calendar.duration_seconds.19d42eeb</span>
                <input type="number" min={1} step={60} value={durationSeconds} onChange={(item) => setDurationSeconds(item.target.value)} required disabled={saving || !canWrite} />
              </label>
          }
          </div>
        }
        {(allDay || endMode === "end") &&
        <div className="calendar-dialog-date-row">
            <label>
              <span>i18n:govoplan-calendar.end_date.89d10cd6</span>
              <DateField value={endDate} min={startDate} onChange={setEndDate} required disabled={saving || !canWrite} />
            </label>
            <label>
              <span>i18n:govoplan-calendar.end_time.cd7800da</span>
              <TimeField value={endTime} min={!allDay && startDate === endDate ? startTime : undefined} onChange={setEndTime} disabled={saving || !canWrite || allDay} />
            </label>
          </div>
        }
        <details className="calendar-advanced-settings calendar-vevent-details">
          <summary>i18n:govoplan-calendar.vevent.9cf5be75</summary>
          <section className="calendar-vevent-section">
            <h3>i18n:govoplan-calendar.identity.7e5a975b</h3>
            <div className="calendar-dialog-grid-three">
              <label>
                <span>i18n:govoplan-calendar.uid.d946adf5</span>
                <input value={uid} onChange={(item) => setUid(item.target.value)} maxLength={255} disabled={saving || !canWrite || Boolean(event)} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.recurrence_id.e0b780ba</span>
                <input value={recurrenceId} onChange={(item) => setRecurrenceId(item.target.value)} maxLength={255} disabled={saving || !canWrite || Boolean(event)} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.sequence.5c8f4e0e</span>
                <input type="number" min={0} step={1} value={sequence} onChange={(item) => setSequence(item.target.value)} disabled={saving || !canWrite} />
              </label>
            </div>
          </section>
          <section className="calendar-vevent-section">
            <h3>i18n:govoplan-calendar.state.a7250206</h3>
            <div className="calendar-dialog-grid-three">
              <label>
                <span>i18n:govoplan-calendar.status.bae7d5be</span>
                <select value={status} onChange={(item) => setStatus(item.target.value)} disabled={saving || !canWrite}>
                  <option value="CONFIRMED">i18n:govoplan-calendar.confirmed.0542404a</option>
                  <option value="TENTATIVE">i18n:govoplan-calendar.tentative.d19f9022</option>
                  <option value="CANCELLED">i18n:govoplan-calendar.cancelled.5587b0af</option>
                </select>
              </label>
              <label>
                <span>i18n:govoplan-calendar.transparency.7cb338a9</span>
                <select value={transparency} onChange={(item) => setTransparency(item.target.value)} disabled={saving || !canWrite}>
                  <option value="OPAQUE">i18n:govoplan-calendar.opaque.3e1d0194</option>
                  <option value="TRANSPARENT">i18n:govoplan-calendar.transparent.ea4efcae</option>
                </select>
              </label>
              <label>
                <span>i18n:govoplan-calendar.class.41ff354b</span>
                <select value={classification} onChange={(item) => setClassification(item.target.value)} disabled={saving || !canWrite}>
                  <option value="PUBLIC">i18n:govoplan-calendar.public.d1785ca2</option>
                  <option value="PRIVATE">i18n:govoplan-calendar.private.b0b7ba46</option>
                  <option value="CONFIDENTIAL">i18n:govoplan-calendar.confidential.84c9cc88</option>
                </select>
              </label>
              <label>
                <span>i18n:govoplan-calendar.timezone.d1f7dc89</span>
                <input value={timezone} onChange={(item) => setTimezone(item.target.value)} maxLength={100} disabled={saving || !canWrite} />
              </label>
              <label className="calendar-dialog-wide">
                <span>i18n:govoplan-calendar.categories.6ccb6007</span>
                <input value={categoriesText} onChange={(item) => setCategoriesText(item.target.value)} disabled={saving || !canWrite} />
              </label>
            </div>
          </section>
          <section className="calendar-vevent-section">
            <h3>i18n:govoplan-calendar.participants.cd56e083</h3>
            <div className="calendar-dialog-grid-two">
              <label>
                <span>i18n:govoplan-calendar.organizer_json.3add6f9f</span>
                <textarea value={organizerJson} onChange={(item) => setOrganizerJson(item.target.value)} rows={5} disabled={saving || !canWrite} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.attendees_json.aeb487bb</span>
                <textarea value={attendeesJson} onChange={(item) => setAttendeesJson(item.target.value)} rows={5} disabled={saving || !canWrite} />
              </label>
            </div>
          </section>
          <section className="calendar-vevent-section">
            <h3>i18n:govoplan-calendar.recurrence.f7ad40f5</h3>
            <label>
              <span>i18n:govoplan-calendar.rrule.c7b2f8a3</span>
              <input value={rruleText} onChange={(item) => setRruleText(item.target.value)} disabled={saving || !canWrite} />
            </label>
            <div className="calendar-dialog-grid-two">
              <label>
                <span>i18n:govoplan-calendar.rdate_json.5b51fca4</span>
                <textarea value={rdateJson} onChange={(item) => setRdateJson(item.target.value)} rows={5} disabled={saving || !canWrite} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.exdate_json.7d0c538d</span>
                <textarea value={exdateJson} onChange={(item) => setExdateJson(item.target.value)} rows={5} disabled={saving || !canWrite} />
              </label>
            </div>
          </section>
          <section className="calendar-vevent-section">
            <h3>i18n:govoplan-calendar.related.917df91e</h3>
            <div className="calendar-dialog-grid-three">
              <label>
                <span>i18n:govoplan-calendar.reminders_json.ca25e08f</span>
                <textarea value={remindersJson} onChange={(item) => setRemindersJson(item.target.value)} rows={5} disabled={saving || !canWrite} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.attachments_json.d91abf3c</span>
                <textarea value={attachmentsJson} onChange={(item) => setAttachmentsJson(item.target.value)} rows={5} disabled={saving || !canWrite} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.related_to_json.2d4e8f59</span>
                <textarea value={relatedToJson} onChange={(item) => setRelatedToJson(item.target.value)} rows={5} disabled={saving || !canWrite} />
              </label>
            </div>
          </section>
          <section className="calendar-vevent-section">
            <h3>i18n:govoplan-calendar.source.6da13add</h3>
            <div className="calendar-dialog-grid-three">
              <label>
                <span>i18n:govoplan-calendar.source_kind.7eda9bc4</span>
                <input value={sourceKind} onChange={(item) => setSourceKind(item.target.value)} maxLength={30} disabled={saving || !canWrite} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.source_href.819fa147</span>
                <input value={sourceHref} onChange={(item) => setSourceHref(item.target.value)} maxLength={1000} disabled={saving || !canWrite} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.etag.11d00f6e</span>
                <input value={etag} onChange={(item) => setEtag(item.target.value)} maxLength={255} disabled={saving || !canWrite} />
              </label>
            </div>
          </section>
          <section className="calendar-vevent-section">
            <h3>i18n:govoplan-calendar.raw.da433cd4</h3>
            <div className="calendar-dialog-grid-two">
              <label>
                <span>i18n:govoplan-calendar.icalendar_json.fb6cc33e</span>
                <textarea value={icalendarJson} onChange={(item) => setICalendarJson(item.target.value)} rows={8} disabled={saving || !canWrite} />
              </label>
              <label>
                <span>i18n:govoplan-calendar.metadata_json.b0e4c283</span>
                <textarea value={metadataJson} onChange={(item) => setMetadataJson(item.target.value)} rows={8} disabled={saving || !canWrite} />
              </label>
            </div>
          </section>
        </details>
      </form>
    </Dialog>);

}

function jsonTextareaValue(value: unknown): string {
  return JSON.stringify(value, null, 2);
}

function parseJsonObject(text: string, label: string): Record<string, unknown> {
  const value = parseJsonText(text, label, {});
  if (!isPlainObject(value)) throw new Error(`${label} must be a JSON object.`);
  return value;
}

function parseJsonObjectOrNull(text: string, label: string): Record<string, unknown> | null {
  if (!text.trim()) return null;
  const value = parseJsonText(text, label, null);
  if (value === null) return null;
  if (!isPlainObject(value)) throw new Error(`${label} must be a JSON object or null.`);
  return value;
}

function parseJsonArray(text: string, label: string): Record<string, unknown>[] {
  const value = parseJsonText(text, label, []);
  if (!Array.isArray(value)) throw new Error(`${label} must be a JSON array.`);
  return value.map((item, index) => {
    if (!isPlainObject(item)) throw new Error(`${label} item ${index + 1} must be a JSON object.`);
    return item;
  });
}

function parseJsonText(text: string, label: string, fallback: unknown): unknown {
  const trimmed = text.trim();
  if (!trimmed) return fallback;
  try {
    return JSON.parse(trimmed) as unknown;
  } catch (err) {
    throw new Error(`${label} contains invalid JSON: ${errorText(err)}`);
  }
}

function parseRRuleInput(text: string): Record<string, unknown> | null {
  const trimmed = text.trim();
  if (!trimmed) return null;
  if (trimmed.startsWith("{")) return parseJsonObject(trimmed, "RRULE");
  const result: Record<string, unknown> = {};
  for (const item of trimmed.split(";")) {
    const [rawKey, ...rawValueParts] = item.split("=");
    const key = rawKey.trim().toUpperCase();
    const rawValue = rawValueParts.join("=").trim();
    if (!key || !rawValue) continue;
    const values = rawValue.split(",").map((value) => value.trim()).filter(Boolean);
    result[key] = values.length > 1 ? values : values[0] ?? rawValue;
  }
  return Object.keys(result).length ? result : null;
}

function serializeRRuleText(value: Record<string, unknown>): string {
  return Object.entries(value).
  map(([key, item]) => `${key.toUpperCase()}=${Array.isArray(item) ? item.join(",") : String(item)}`).
  join(";");
}

function commaSeparatedValues(text: string): string[] {
  return text.split(",").map((item) => item.trim()).filter(Boolean);
}

function withDurationMode(value: Record<string, unknown>, usesDuration: boolean): Record<string, unknown> {
  const next = { ...value };
  const standard = isPlainObject(next.standard) ? { ...next.standard } : {};
  if (usesDuration) {
    standard.uses_duration = true;
  } else {
    delete standard.uses_duration;
  }
  if (Object.keys(standard).length > 0) {
    next.standard = standard;
  } else {
    delete next.standard;
  }
  return next;
}

function eventUsesDuration(event: CalendarEvent): boolean {
  const standard = isPlainObject(event.icalendar?.standard) ? event.icalendar.standard : null;
  return standard?.uses_duration === true;
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
