/*
 * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Input } from "@base-ui/react/input";
import React, { useCallback, useMemo, useState } from "react";
import styles from "./TimePicker.module.css.js";
const NUMERIC_INPUT_PATTERN = "[0-9]*";
const SEGMENT_LIMITS = {
  hours: {
    min: 0,
    max: 23
  },
  minutes: {
    min: 0,
    max: 59
  }
};
export const TimePicker = /*#__PURE__*/React.memo(function ({
  value,
  onChange,
  label = "Time"
}) {
  const valueTimestamp = value?.getTime() ?? null;
  const valueSegments = useMemo(() => segmentsFromTimestamp(valueTimestamp), [valueTimestamp]);
  // draftSegments always holds the displayed text. It is reset to
  // valueSegments whenever the controlled value changes, so the parent
  // Date is the source of truth once an edit is committed or discarded.
  const [draftSegments, setDraftSegments] = useState(valueSegments);
  const [prevSegments, setPrevSegments] = useState(valueSegments);
  if (prevSegments.hours !== valueSegments.hours || prevSegments.minutes !== valueSegments.minutes) {
    setPrevSegments(valueSegments);
    setDraftSegments(valueSegments);
  }
  const hourText = draftSegments.hours;
  const minuteText = draftSegments.minutes;
  const hourInvalid = isSegmentInvalid(hourText, "hours");
  const minuteInvalid = isSegmentInvalid(minuteText, "minutes");
  const handleSegmentChange = useCallback((segment, nextText) => {
    setDraftSegments(prev => replaceSegmentText(prev, {
      segment,
      nextText
    }));
  }, []);
  const emitChange = useCallback((hours, minutes) => {
    const nextDate = value != null ? new Date(value.getTime()) : new Date();
    nextDate.setHours(hours, minutes, 0, 0);
    onChange?.(nextDate);
  }, [onChange, value]);
  const handleSegmentBlur = useCallback((segment, text) => {
    const parsedSegment = parseNumber(text);
    if (parsedSegment == null) {
      // Non-numeric text cannot be converted into a valid time segment, so
      // restore the controlled value instead of emitting an arbitrary Date.
      setDraftSegments(valueSegments);
      return;
    }
    const clampedSegment = clampSegment(parsedSegment, segment);
    setDraftSegments(prev => replaceSegmentText(prev, {
      segment,
      nextText: formatSegment(clampedSegment, segment)
    }));
    const currentHours = Number(valueSegments.hours);
    const currentMinutes = Number(valueSegments.minutes);
    const nextHours = segment === "hours" ? clampedSegment : currentHours;
    const nextMinutes = segment === "minutes" ? clampedSegment : currentMinutes;
    if (nextHours !== currentHours || nextMinutes !== currentMinutes) {
      emitChange(nextHours, nextMinutes);
    }
  }, [emitChange, valueSegments]);
  const handleHourChange = useCallback(nextText => handleSegmentChange("hours", nextText), [handleSegmentChange]);
  const handleMinuteChange = useCallback(nextText => handleSegmentChange("minutes", nextText), [handleSegmentChange]);
  const handleHourBlur = useCallback(() => handleSegmentBlur("hours", hourText), [handleSegmentBlur, hourText]);
  const handleMinuteBlur = useCallback(() => handleSegmentBlur("minutes", minuteText), [handleSegmentBlur, minuteText]);
  return /*#__PURE__*/React.createElement("div", {
    className: styles.osdkTimePickerRoot,
    role: "group",
    "aria-label": label
  }, /*#__PURE__*/React.createElement(Input, {
    type: "text",
    inputMode: "numeric",
    pattern: NUMERIC_INPUT_PATTERN,
    value: hourText,
    onValueChange: handleHourChange,
    onBlur: handleHourBlur,
    className: styles.osdkTimePickerInput,
    "aria-label": `${label} hours`,
    "aria-invalid": hourInvalid || undefined
  }), /*#__PURE__*/React.createElement("span", {
    className: styles.osdkTimePickerSeparator,
    "aria-hidden": "true"
  }, ":"), /*#__PURE__*/React.createElement(Input, {
    type: "text",
    inputMode: "numeric",
    pattern: NUMERIC_INPUT_PATTERN,
    value: minuteText,
    onValueChange: handleMinuteChange,
    onBlur: handleMinuteBlur,
    className: styles.osdkTimePickerInput,
    "aria-label": `${label} minutes`,
    "aria-invalid": minuteInvalid || undefined
  }));
});
function replaceSegmentText(segments, {
  segment,
  nextText
}) {
  return {
    ...segments,
    [segment]: nextText
  };
}
function segmentsFromTimestamp(valueTimestamp) {
  if (valueTimestamp == null) {
    return {
      hours: formatSegment(0, "hours"),
      minutes: formatSegment(0, "minutes")
    };
  }
  const value = new Date(valueTimestamp);
  return {
    hours: formatSegment(value.getHours(), "hours"),
    minutes: formatSegment(value.getMinutes(), "minutes")
  };
}
function formatSegment(value, segment) {
  // Hours stay compact while minutes stay two digits so the HH:mm control
  // remains easy to scan.
  return segment === "hours" ? String(value) : String(value).padStart(2, "0");
}
function parseNumber(text) {
  if (!/^\d{1,2}$/u.test(text)) {
    return undefined;
  }
  const parsed = Number(text);
  return Number.isNaN(parsed) ? undefined : parsed;
}
function parseSegment(text, segment) {
  const parsed = parseNumber(text);
  if (parsed == null) {
    return undefined;
  }
  const {
    min,
    max
  } = SEGMENT_LIMITS[segment];
  return parsed >= min && parsed <= max ? parsed : undefined;
}
function isSegmentInvalid(text, segment) {
  return parseSegment(text, segment) == null;
}
function clampSegment(value, segment) {
  const {
    min,
    max
  } = SEGMENT_LIMITS[segment];
  return Math.min(Math.max(value, min), max);
}
//# sourceMappingURL=TimePicker.js.map