import {
    CalendarDaysIcon,
    ChevronLeftIcon,
    ChevronRightIcon,
    ClockIcon,
} from '@heroicons/react/24/solid';
import { createPortal } from 'react-dom';
import {
    type ClipboardEvent,
    type ChangeEvent,
    type ChangeEventHandler,
    type FocusEvent,
    type FocusEventHandler,
    type KeyboardEvent as ReactKeyboardEvent,
    type ReactNode,
    type Ref,
    useCallback,
    useEffect,
    useMemo,
    useRef,
    useState,
} from 'react';
import type { ComponentSize } from './types';

type DatePickerKind = 'date' | 'time' | 'date-time';
type Layout = 'stacked' | 'inline';
type PopupPlacement = 'top' | 'bottom';
type SegmentKind = 'day' | 'month' | 'year' | 'hour' | 'minute';
type FormatToken = 'dd' | 'mm' | 'yyyy' | 'HH';
type PickerValue = {
    date: Date;
    hour: number;
    minute: number;
};

type PopupPosition = {
    top: number;
    left: number;
    minWidth: number;
    placement: PopupPlacement;
};

type RawFormatPart =
    | {
          type: 'literal';
          value: string;
      }
    | {
          type: 'token';
          token: FormatToken;
      };

type FormatPart =
    | {
          type: 'literal';
          value: string;
          start: number;
          end: number;
      }
    | {
          type: 'segment';
          kind: SegmentKind;
          token: FormatToken;
          length: number;
          start: number;
          end: number;
          segmentIndex: number;
      };

type FormatSegment = Extract<FormatPart, { type: 'segment' }>;
type FormatConfig = {
    type: DatePickerKind;
    format: string;
    parts: FormatPart[];
    segments: FormatSegment[];
    totalLength: number;
    literalChars: Set<string>;
};

const HOURS = Array.from({ length: 24 }, (_, idx) => idx);
const MINUTES = Array.from({ length: 60 }, (_, idx) => idx);
const YEAR_WINDOW = 50;
const POPUP_GAP = 6;
const POPUP_MARGIN = 8;
const SEGMENT_EDIT_TIMEOUT_MS = 1500;
const DEFAULT_FORMAT: Record<DatePickerKind, string> = {
    date: 'yyyy/mm/dd',
    time: 'HH:mm',
    'date-time': 'yyyy/mm/dd HH:mm',
};

export type DatePickerProps = {
    label?: string;
    placeholder?: string;
    type: DatePickerKind;
    size?: ComponentSize;
    width?: ComponentSize;
    layout?: Layout;
    value: string;
    format?: string;
    min?: string;
    max?: string;
    name?: string;
    onChange?: ChangeEventHandler<HTMLInputElement>;
    onBlur?: FocusEventHandler<HTMLInputElement>;
    inputRef?: Ref<HTMLInputElement>;
    disabled?: boolean;
    required?: boolean;
    error?: string;
    rightIcon?: ReactNode;
    className?: string;
    inputClassName?: string;
};

function pad2(value: number): string {
    return String(value).padStart(2, '0');
}

function pad4(value: number): string {
    return String(value).padStart(4, '0');
}

function clampNumber(value: number, minValue: number, maxValue: number): number {
    return Math.min(maxValue, Math.max(minValue, value));
}

function createDateAtLocalMidnight(year: number, monthIndex: number, day: number): Date {
    const candidate = new Date(0);
    candidate.setHours(0, 0, 0, 0);
    candidate.setFullYear(year, monthIndex, day);
    return candidate;
}

function createDateTimeFromPickerValue(value: PickerValue): Date {
    const candidate = createDateAtLocalMidnight(
        value.date.getFullYear(),
        value.date.getMonth(),
        value.date.getDate(),
    );
    candidate.setHours(value.hour, value.minute, 0, 0);
    return candidate;
}

function startOfDay(value: Date): Date {
    const candidate = new Date(value);
    candidate.setHours(0, 0, 0, 0);
    return candidate;
}

function startOfMonth(value: Date): Date {
    const candidate = startOfDay(value);
    candidate.setDate(1);
    return candidate;
}

function isSameDay(left: Date, right: Date): boolean {
    return (
        left.getFullYear() === right.getFullYear() &&
        left.getMonth() === right.getMonth() &&
        left.getDate() === right.getDate()
    );
}

function createValidatedDate(year: number, month: number, day: number): Date | null {
    const candidate = createDateAtLocalMidnight(year, month - 1, day);
    if (
        candidate.getFullYear() !== year ||
        candidate.getMonth() !== month - 1 ||
        candidate.getDate() !== day
    ) {
        return null;
    }

    return candidate;
}

function daysInMonth(year: number, month: number): number {
    return createDateAtLocalMidnight(year, month, 0).getDate();
}

function clonePickerValue(value: PickerValue): PickerValue {
    return {
        date: startOfDay(value.date),
        hour: value.hour,
        minute: value.minute,
    };
}

function resolveLocale(): string {
    if (typeof navigator === 'undefined') {
        return 'en-US';
    }

    return navigator.languages?.[0] ?? navigator.language ?? 'en-US';
}

function resolveWeekStart(locale: string): number {
    if (typeof Intl === 'undefined' || typeof Intl.Locale !== 'function') {
        return 0;
    }

    try {
        const localeInfo = new Intl.Locale(locale) as Intl.Locale & {
            weekInfo?: { firstDay?: number };
        };
        const firstDay = localeInfo.weekInfo?.firstDay;

        if (typeof firstDay !== 'number') {
            return 0;
        }

        if (firstDay === 7) {
            return 0;
        }

        if (firstDay >= 1 && firstDay <= 6) {
            return firstDay;
        }
    } catch {
        return 0;
    }

    return 0;
}

function buildMonthGrid(viewMonth: Date, weekStart: number): Date[] {
    const monthStart = startOfMonth(viewMonth);
    const dayOffset = (monthStart.getDay() - weekStart + 7) % 7;
    const gridStart = createDateAtLocalMidnight(
        monthStart.getFullYear(),
        monthStart.getMonth(),
        monthStart.getDate() - dayOffset,
    );

    return Array.from({ length: 42 }, (_, index) => {
        return createDateAtLocalMidnight(
            gridStart.getFullYear(),
            gridStart.getMonth(),
            gridStart.getDate() + index,
        );
    });
}

function joinClassNames(...parts: Array<string | false | null | undefined>): string {
    return parts.filter(Boolean).join(' ').trim();
}

function assignRef(ref: Ref<HTMLInputElement> | undefined, node: HTMLInputElement | null): void {
    if (!ref) {
        return;
    }

    if (typeof ref === 'function') {
        ref(node);
        return;
    }

    (ref as { current: HTMLInputElement | null }).current = node;
}

function tokenizeFormat(format: string): RawFormatPart[] {
    const result: RawFormatPart[] = [];
    let pointer = 0;
    let literalBuffer = '';

    const flushLiteral = () => {
        if (literalBuffer.length > 0) {
            result.push({ type: 'literal', value: literalBuffer });
            literalBuffer = '';
        }
    };

    while (pointer < format.length) {
        if (format.startsWith('yyyy', pointer)) {
            flushLiteral();
            result.push({ type: 'token', token: 'yyyy' });
            pointer += 4;
            continue;
        }
        if (format.startsWith('dd', pointer)) {
            flushLiteral();
            result.push({ type: 'token', token: 'dd' });
            pointer += 2;
            continue;
        }
        if (format.startsWith('HH', pointer)) {
            flushLiteral();
            result.push({ type: 'token', token: 'HH' });
            pointer += 2;
            continue;
        }
        if (format.startsWith('mm', pointer)) {
            flushLiteral();
            result.push({ type: 'token', token: 'mm' });
            pointer += 2;
            continue;
        }

        literalBuffer += format[pointer];
        pointer += 1;
    }

    flushLiteral();
    return result;
}

function buildFormatConfigOrNull(type: DatePickerKind, format: string): FormatConfig | null {
    const rawParts = tokenizeFormat(format);
    if (rawParts.length === 0) {
        return null;
    }

    const parts: FormatPart[] = [];
    const literalChars = new Set<string>();
    const counts: Record<SegmentKind, number> = {
        day: 0,
        month: 0,
        year: 0,
        hour: 0,
        minute: 0,
    };

    let hourSeen = false;
    let segmentIndex = 0;
    let offset = 0;

    for (const part of rawParts) {
        if (part.type === 'literal') {
            for (const char of part.value) {
                literalChars.add(char);
            }

            parts.push({
                type: 'literal',
                value: part.value,
                start: offset,
                end: offset + part.value.length,
            });
            offset += part.value.length;
            continue;
        }

        let kind: SegmentKind;
        if (part.token === 'dd') {
            kind = 'day';
        } else if (part.token === 'yyyy') {
            kind = 'year';
        } else if (part.token === 'HH') {
            kind = 'hour';
            hourSeen = true;
        } else if (type === 'date') {
            kind = 'month';
        } else if (type === 'time') {
            kind = 'minute';
        } else {
            kind = hourSeen ? 'minute' : 'month';
        }

        counts[kind] += 1;

        const length = part.token.length;
        parts.push({
            type: 'segment',
            kind,
            token: part.token,
            length,
            start: offset,
            end: offset + length,
            segmentIndex,
        });
        segmentIndex += 1;
        offset += length;
    }

    const isValid =
        (type === 'date' &&
            counts.day === 1 &&
            counts.month === 1 &&
            counts.year === 1 &&
            counts.hour === 0 &&
            counts.minute === 0) ||
        (type === 'time' &&
            counts.day === 0 &&
            counts.month === 0 &&
            counts.year === 0 &&
            counts.hour === 1 &&
            counts.minute === 1) ||
        (type === 'date-time' &&
            counts.day === 1 &&
            counts.month === 1 &&
            counts.year === 1 &&
            counts.hour === 1 &&
            counts.minute === 1);

    if (!isValid) {
        return null;
    }

    const segments = parts.filter((part): part is FormatSegment => part.type === 'segment');
    /* c8 ignore start -- validated token counts always yield at least one segment. */
    if (segments.length === 0) {
        return null;
    }
    /* c8 ignore stop */

    return {
        type,
        format,
        parts,
        segments,
        totalLength: offset,
        literalChars,
    };
}

function buildFormatConfig(type: DatePickerKind, requestedFormat?: string): FormatConfig {
    if (requestedFormat != null && requestedFormat.trim() !== '') {
        const explicit = buildFormatConfigOrNull(type, requestedFormat.trim());
        if (explicit) {
            return explicit;
        }
    }

    const fallback = buildFormatConfigOrNull(type, DEFAULT_FORMAT[type]);
    /* c8 ignore start -- static defaults are valid for all supported picker types. */
    if (!fallback) {
        throw new Error('Failed to initialize DatePicker format configuration.');
    }
    /* c8 ignore stop */

    return fallback;
}

function parsePickerValueWithFormat(rawValue: string, config: FormatConfig): PickerValue | null {
    const value = rawValue.trim();
    if (value.length !== config.totalLength) {
        return null;
    }

    let year: number | null = null;
    let month: number | null = null;
    let day: number | null = null;
    let hour: number | null = null;
    let minute: number | null = null;

    for (const part of config.parts) {
        const chunk = value.slice(part.start, part.end);

        if (part.type === 'literal') {
            if (chunk !== part.value) {
                return null;
            }
            continue;
        }

        if (!/^\d+$/.test(chunk)) {
            return null;
        }

        const numeric = Number(chunk);
        /* c8 ignore start -- numeric chunks are finite after /^\d+$/ validation. */
        if (!Number.isFinite(numeric)) {
            return null;
        }
        /* c8 ignore stop */

        if (part.kind === 'year') {
            year = numeric;
        } else if (part.kind === 'month') {
            month = numeric;
        } else if (part.kind === 'day') {
            day = numeric;
        } else if (part.kind === 'hour') {
            hour = numeric;
        } else {
            minute = numeric;
        }
    }

    if (config.type !== 'time') {
        /* c8 ignore start -- date/month/year segments are guaranteed for non-time validated formats. */
        if (year == null || month == null || day == null) {
            return null;
        }
        /* c8 ignore stop */
        /* c8 ignore start -- 'yyyy' token bounds parsed year to 0..9999. */
        if (year < 0 || year > 9999) {
            return null;
        }
        /* c8 ignore stop */

        const parsedDate = createValidatedDate(year, month, day);
        if (!parsedDate) {
            return null;
        }

        if (config.type === 'date') {
            return {
                date: parsedDate,
                hour: 0,
                minute: 0,
            };
        }

        /* c8 ignore start -- date-time format validation guarantees hour/minute segments. */
        if (hour == null || minute == null) {
            return null;
        }
        /* c8 ignore stop */

        if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
            return null;
        }

        return {
            date: parsedDate,
            hour,
            minute,
        };
    }

    /* c8 ignore start -- time format validation guarantees hour/minute segments. */
    if (hour == null || minute == null) {
        return null;
    }
    /* c8 ignore stop */

    if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
        return null;
    }

    return {
        date: startOfDay(new Date()),
        hour,
        minute,
    };
}

function formatPickerValueWithFormat(value: PickerValue, config: FormatConfig): string {
    const fields: Record<SegmentKind, string> = {
        day: pad2(value.date.getDate()),
        month: pad2(value.date.getMonth() + 1),
        year: pad4(value.date.getFullYear()),
        hour: pad2(value.hour),
        minute: pad2(value.minute),
    };

    return config.parts
        .map((part) => {
            if (part.type === 'literal') {
                return part.value;
            }

            return fields[part.kind];
        })
        .join('');
}

function comparePickerValue(left: PickerValue, right: PickerValue, type: DatePickerKind): number {
    if (type === 'time') {
        const leftTotal = left.hour * 60 + left.minute;
        const rightTotal = right.hour * 60 + right.minute;
        return leftTotal - rightTotal;
    }

    if (type === 'date') {
        return startOfDay(left.date).getTime() - startOfDay(right.date).getTime();
    }

    const leftDateTime = createDateTimeFromPickerValue(left).getTime();
    const rightDateTime = createDateTimeFromPickerValue(right).getTime();

    return leftDateTime - rightDateTime;
}

function normalizeRange(
    minValue: PickerValue | null,
    maxValue: PickerValue | null,
    type: DatePickerKind,
): { minValue: PickerValue | null; maxValue: PickerValue | null } {
    if (minValue == null || maxValue == null) {
        return {
            minValue,
            maxValue,
        };
    }

    if (comparePickerValue(minValue, maxValue, type) <= 0) {
        return { minValue, maxValue };
    }

    return {
        minValue: maxValue,
        maxValue: minValue,
    };
}

function clampPickerToRange(
    candidate: PickerValue,
    minValue: PickerValue | null,
    maxValue: PickerValue | null,
    type: DatePickerKind,
): PickerValue {
    if (minValue && comparePickerValue(candidate, minValue, type) < 0) {
        return clonePickerValue(minValue);
    }

    if (maxValue && comparePickerValue(candidate, maxValue, type) > 0) {
        return clonePickerValue(maxValue);
    }

    return clonePickerValue(candidate);
}

function isWithinRange(
    candidate: PickerValue,
    minValue: PickerValue | null,
    maxValue: PickerValue | null,
    type: DatePickerKind,
): boolean {
    if (minValue && comparePickerValue(candidate, minValue, type) < 0) {
        return false;
    }

    if (maxValue && comparePickerValue(candidate, maxValue, type) > 0) {
        return false;
    }

    return true;
}

function applySegmentDigits(
    baseValue: PickerValue,
    kind: SegmentKind,
    digits: string,
): PickerValue {
    const parsedDigits = Number(digits);
    if (!Number.isFinite(parsedDigits)) {
        return clonePickerValue(baseValue);
    }

    let year = baseValue.date.getFullYear();
    let month = baseValue.date.getMonth() + 1;
    let day = baseValue.date.getDate();
    let hour = baseValue.hour;
    let minute = baseValue.minute;

    if (kind === 'year') {
        year = clampNumber(parsedDigits, 0, 9999);
    } else if (kind === 'month') {
        month = clampNumber(parsedDigits, 1, 12);
    } else if (kind === 'day') {
        const maxDay = daysInMonth(year, month);
        day = clampNumber(parsedDigits, 1, maxDay);
    } else if (kind === 'hour') {
        hour = clampNumber(parsedDigits, 0, 23);
    } else {
        minute = clampNumber(parsedDigits, 0, 59);
    }

    const maxDayForCurrentMonth = daysInMonth(year, month);
    day = clampNumber(day, 1, maxDayForCurrentMonth);

    const nextDate =
        createValidatedDate(year, month, day) ?? createDateAtLocalMidnight(year, month - 1, day);
    return {
        date: nextDate,
        hour,
        minute,
    };
}

function findSegmentIndexByCaret(segments: FormatSegment[], caretPosition: number | null): number {
    if (segments.length === 0) {
        return 0;
    }

    if (caretPosition == null) {
        return 0;
    }

    for (const segment of segments) {
        if (caretPosition >= segment.start && caretPosition <= segment.end) {
            return segment.segmentIndex;
        }

        if (caretPosition < segment.start) {
            return segment.segmentIndex;
        }
    }

    return segments[segments.length - 1].segmentIndex;
}

function isDateSelectableForRange(
    dateValue: Date,
    type: DatePickerKind,
    minValue: PickerValue | null,
    maxValue: PickerValue | null,
): boolean {
    if (type === 'date') {
        return isWithinRange(
            {
                date: startOfDay(dateValue),
                hour: 0,
                minute: 0,
            },
            minValue,
            maxValue,
            type,
        );
    }

    const dayStart: PickerValue = {
        date: startOfDay(dateValue),
        hour: 0,
        minute: 0,
    };
    const dayEnd: PickerValue = {
        date: startOfDay(dateValue),
        hour: 23,
        minute: 59,
    };

    if (minValue && comparePickerValue(dayEnd, minValue, type) < 0) {
        return false;
    }

    if (maxValue && comparePickerValue(dayStart, maxValue, type) > 0) {
        return false;
    }

    return true;
}

function isHourSelectableForRange(
    dateValue: Date,
    hour: number,
    type: DatePickerKind,
    minValue: PickerValue | null,
    maxValue: PickerValue | null,
): boolean {
    const candidateStart: PickerValue = {
        date: startOfDay(dateValue),
        hour,
        minute: 0,
    };
    const candidateEnd: PickerValue = {
        date: startOfDay(dateValue),
        hour,
        minute: 59,
    };

    if (minValue && comparePickerValue(candidateEnd, minValue, type) < 0) {
        return false;
    }

    if (maxValue && comparePickerValue(candidateStart, maxValue, type) > 0) {
        return false;
    }

    return true;
}

// eslint-disable-next-line react-refresh/only-export-components -- test-only export of pure helpers.
export const __datePickerTestUtils = {
    pad2,
    pad4,
    clampNumber,
    createDateAtLocalMidnight,
    createDateTimeFromPickerValue,
    startOfDay,
    startOfMonth,
    isSameDay,
    createValidatedDate,
    daysInMonth,
    clonePickerValue,
    resolveLocale,
    resolveWeekStart,
    buildMonthGrid,
    joinClassNames,
    assignRef,
    tokenizeFormat,
    buildFormatConfigOrNull,
    buildFormatConfig,
    parsePickerValueWithFormat,
    formatPickerValueWithFormat,
    comparePickerValue,
    normalizeRange,
    clampPickerToRange,
    isWithinRange,
    applySegmentDigits,
    findSegmentIndexByCaret,
    isDateSelectableForRange,
    isHourSelectableForRange,
} as const;

export function DatePicker({
    label,
    placeholder = '',
    type,
    size = 'md',
    width = 'md',
    layout = 'stacked',
    value,
    format,
    min,
    max,
    name,
    onChange,
    onBlur,
    inputRef,
    disabled = false,
    required = false,
    error,
    rightIcon,
    className = '',
    inputClassName = '',
}: Readonly<DatePickerProps>) {
    const internalInputRef = useRef<HTMLInputElement | null>(null);
    const inputWrapperRef = useRef<HTMLDivElement | null>(null);
    const popupRef = useRef<HTMLDivElement | null>(null);
    const changeHandledRef = useRef(false);
    const bufferedDigitsRef = useRef<{
        segmentIndex: number;
        digits: string;
        timestamp: number;
    } | null>(null);
    const activeSegmentIndexRef = useRef(0);
    const pendingSelectionTimerRef = useRef<number | null>(null);

    const [isOpen, setIsOpen] = useState(false);
    const [viewMonth, setViewMonth] = useState<Date>(() => startOfMonth(new Date()));
    const [activeChooser, setActiveChooser] = useState<'month' | 'year' | null>(null);
    const [popupPosition, setPopupPosition] = useState<PopupPosition | null>(null);

    const closePicker = useCallback(() => {
        setIsOpen(false);
        setActiveChooser(null);
        setPopupPosition(null);
    }, []);

    const locale = useMemo(() => resolveLocale(), []);
    const weekStart = useMemo(() => resolveWeekStart(locale), [locale]);
    const formatConfig = useMemo(() => buildFormatConfig(type, format), [type, format]);

    const fallbackValue = useMemo<PickerValue>(() => {
        const today = startOfDay(new Date());
        return {
            date: today,
            hour: 0,
            minute: 0,
        };
    }, []);

    const parsedValue = useMemo(
        () => parsePickerValueWithFormat(value, formatConfig),
        [formatConfig, value],
    );
    const selectedValue = parsedValue ?? fallbackValue;

    const parsedMinValue = useMemo(() => {
        if (min == null || min.trim() === '') {
            return null;
        }
        return parsePickerValueWithFormat(min, formatConfig);
    }, [formatConfig, min]);

    const parsedMaxValue = useMemo(() => {
        if (max == null || max.trim() === '') {
            return null;
        }
        return parsePickerValueWithFormat(max, formatConfig);
    }, [formatConfig, max]);

    const { minValue: normalizedMinValue, maxValue: normalizedMaxValue } = useMemo(() => {
        return normalizeRange(parsedMinValue, parsedMaxValue, type);
    }, [parsedMaxValue, parsedMinValue, type]);

    const clampedSelectedValue = useMemo(() => {
        return clampPickerToRange(selectedValue, normalizedMinValue, normalizedMaxValue, type);
    }, [normalizedMaxValue, normalizedMinValue, selectedValue, type]);

    const selectedDate = clampedSelectedValue.date;
    const selectedHour = clampedSelectedValue.hour;
    const selectedMinute = clampedSelectedValue.minute;
    const displayValue = useMemo(() => {
        return formatPickerValueWithFormat(clampedSelectedValue, formatConfig);
    }, [clampedSelectedValue, formatConfig]);

    const containerWidthClass = {
        sm: 'max-w-xs',
        md: 'max-w-sm',
        lg: 'max-w-md',
        full: 'max-w-none',
    }[width];

    const inputSizeClass = {
        sm: 'h-8 !text-xs',
        md: 'h-10 text-sm',
        lg: 'h-12 text-sm',
        full: 'h-10 text-sm',
    }[size];

    const wrapperClass =
        layout === 'inline' ? 'inline-flex w-auto items-center gap-2' : 'block w-full gap-1';
    const labelClass = layout === 'inline' ? 'text-xs ui-body-secondary' : '';
    const inputWrapperClass = layout === 'inline' ? 'relative' : 'relative mt-1';

    const defaultRightIcon =
        type === 'time' ? (
            <ClockIcon className="h-4 w-4" aria-hidden="true" />
        ) : (
            <CalendarDaysIcon className="h-4 w-4" aria-hidden="true" />
        );
    const resolvedRightIcon = rightIcon ?? defaultRightIcon;
    const hasTrailingIcon = Boolean(resolvedRightIcon);

    const weekdayLabels = useMemo(() => {
        const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short' });
        const sundayReference = new Date(2024, 0, 7);

        return Array.from({ length: 7 }, (_, offset) => {
            const dayIndex = (weekStart + offset) % 7;
            const current = new Date(
                sundayReference.getFullYear(),
                sundayReference.getMonth(),
                sundayReference.getDate() + dayIndex,
            );
            return formatter.format(current);
        });
    }, [locale, weekStart]);

    const monthLabels = useMemo(() => {
        const formatter = new Intl.DateTimeFormat(locale, { month: 'long' });

        return Array.from({ length: 12 }, (_, monthIndex) => {
            return formatter.format(new Date(2024, monthIndex, 1));
        });
    }, [locale]);

    const yearOptions = useMemo(() => {
        const centerYear = viewMonth.getFullYear();
        const startYear = Math.max(0, centerYear - YEAR_WINDOW);
        const endYear = Math.min(9999, centerYear + YEAR_WINDOW);

        return Array.from({ length: endYear - startYear + 1 }, (_, idx) => startYear + idx);
    }, [viewMonth]);

    const monthGrid = useMemo(() => buildMonthGrid(viewMonth, weekStart), [viewMonth, weekStart]);

    const recalculatePopupPosition = useCallback(() => {
        /* c8 ignore start -- guard protects partial mount/layout states that are not deterministic to unit test. */
        if (!isOpen || !inputWrapperRef.current || !popupRef.current || !globalThis.window) {
            return;
        }
        /* c8 ignore stop */

        const anchorRect = inputWrapperRef.current.getBoundingClientRect();
        const popupRect = popupRef.current.getBoundingClientRect();
        const viewportWidth = window.innerWidth;
        const viewportHeight = window.innerHeight;

        let left = anchorRect.left;
        if (left + popupRect.width > viewportWidth - POPUP_MARGIN) {
            left = viewportWidth - POPUP_MARGIN - popupRect.width;
        }
        left = Math.max(POPUP_MARGIN, left);

        const spaceBelow = viewportHeight - anchorRect.bottom - POPUP_MARGIN;
        const spaceAbove = anchorRect.top - POPUP_MARGIN;
        const placeAbove = spaceBelow < popupRect.height && spaceAbove > spaceBelow;

        let top = placeAbove
            ? anchorRect.top - popupRect.height - POPUP_GAP
            : anchorRect.bottom + POPUP_GAP;

        top = Math.max(
            POPUP_MARGIN,
            Math.min(top, viewportHeight - POPUP_MARGIN - popupRect.height),
        );

        setPopupPosition({
            top,
            left,
            minWidth: anchorRect.width,
            placement: placeAbove ? 'top' : 'bottom',
        });
    }, [isOpen]);

    useEffect(() => {
        if (!isOpen) {
            return;
        }

        recalculatePopupPosition();

        /* c8 ignore start -- browser/window is always present in jsdom runtime. */
        if (!globalThis.window) {
            return;
        }
        /* c8 ignore stop */

        const handleWindowChange = () => {
            recalculatePopupPosition();
        };

        window.addEventListener('resize', handleWindowChange);
        window.addEventListener('scroll', handleWindowChange, true);

        return () => {
            window.removeEventListener('resize', handleWindowChange);
            window.removeEventListener('scroll', handleWindowChange, true);
        };
    }, [isOpen, recalculatePopupPosition]);

    useEffect(() => {
        if (!isOpen) {
            return;
        }

        const handlePointerDown = (event: MouseEvent | TouchEvent) => {
            const eventTarget = event.target as Node | null;
            /* c8 ignore start -- pointer events always provide a target in browser runtimes. */
            if (!eventTarget) {
                return;
            }
            /* c8 ignore stop */

            if (
                popupRef.current?.contains(eventTarget) ||
                inputWrapperRef.current?.contains(eventTarget)
            ) {
                return;
            }

            closePicker();
        };

        const handleKeyDown = (event: KeyboardEvent) => {
            if (event.key === 'Escape') {
                closePicker();
            }
        };

        document.addEventListener('mousedown', handlePointerDown);
        document.addEventListener('touchstart', handlePointerDown);
        document.addEventListener('keydown', handleKeyDown);

        return () => {
            document.removeEventListener('mousedown', handlePointerDown);
            document.removeEventListener('touchstart', handlePointerDown);
            document.removeEventListener('keydown', handleKeyDown);
        };
    }, [closePicker, isOpen]);

    useEffect(() => {
        return () => {
            if (pendingSelectionTimerRef.current != null) {
                globalThis.window.clearTimeout(pendingSelectionTimerRef.current);
            }
        };
    }, []);

    const selectSegment = useCallback(
        (segmentIndex: number) => {
            const segments = formatConfig.segments;
            /* c8 ignore start -- format configuration always includes at least one segment. */
            if (segments.length === 0) {
                return;
            }
            /* c8 ignore stop */

            const clampedIndex = clampNumber(segmentIndex, 0, segments.length - 1);
            activeSegmentIndexRef.current = clampedIndex;

            /* c8 ignore start -- browser/window is always present in jsdom runtime. */
            if (!globalThis.window) {
                return;
            }
            /* c8 ignore stop */

            if (pendingSelectionTimerRef.current != null) {
                globalThis.window.clearTimeout(pendingSelectionTimerRef.current);
            }

            pendingSelectionTimerRef.current = globalThis.window.setTimeout(() => {
                const inputNode = internalInputRef.current;
                const targetSegment = segments[clampedIndex];
                /* c8 ignore start -- input node and target segment are available while mounted. */
                if (!inputNode || !targetSegment) {
                    return;
                }
                /* c8 ignore stop */

                inputNode.setSelectionRange(targetSegment.start, targetSegment.end);
            }, 0);
        },
        [formatConfig.segments],
    );

    const resolveCurrentSegmentIndex = useCallback(() => {
        const segments = formatConfig.segments;
        /* c8 ignore start -- format configuration always includes at least one segment. */
        if (segments.length === 0) {
            return 0;
        }
        /* c8 ignore stop */

        const inputNode = internalInputRef.current;
        /* c8 ignore start -- interactions only run when input node is mounted. */
        if (!inputNode) {
            return activeSegmentIndexRef.current;
        }
        /* c8 ignore stop */

        const index = findSegmentIndexByCaret(segments, inputNode.selectionStart);
        activeSegmentIndexRef.current = index;
        return index;
    }, [formatConfig.segments]);

    const setInputNode = useCallback(
        (node: HTMLInputElement | null) => {
            internalInputRef.current = node;
            assignRef(inputRef, node);
        },
        [inputRef],
    );

    const handleInputChange: ChangeEventHandler<HTMLInputElement> = useCallback(
        (event) => {
            changeHandledRef.current = true;
            onChange?.(event);
        },
        [onChange],
    );

    const commitValue = useCallback(
        (nextValue: string) => {
            if (disabled || nextValue === displayValue) {
                return;
            }

            const inputNode = internalInputRef.current;
            /* c8 ignore start -- commits run only from active mounted input interactions. */
            if (!inputNode) {
                return;
            }
            /* c8 ignore stop */

            changeHandledRef.current = false;

            const valueSetter = Object.getOwnPropertyDescriptor(
                HTMLInputElement.prototype,
                'value',
            )?.set;
            if (valueSetter) {
                valueSetter.call(inputNode, nextValue);
            } else {
                inputNode.value = nextValue;
            }

            inputNode.dispatchEvent(new Event('change', { bubbles: true }));

            if (!changeHandledRef.current && onChange) {
                onChange({
                    target: inputNode,
                    currentTarget: inputNode,
                } as ChangeEvent<HTMLInputElement>);
            }
        },
        [disabled, displayValue, onChange],
    );

    const commitSegmentDigits = useCallback(
        (
            segmentIndex: number,
            digits: string,
            moveToNext: boolean,
            options?: {
                clearBuffer?: boolean;
            },
        ) => {
            const segment = formatConfig.segments[segmentIndex];
            /* c8 ignore start -- segmentIndex is always resolved from known format segments. */
            if (!segment) {
                return;
            }
            /* c8 ignore stop */

            const baseValue =
                parsePickerValueWithFormat(displayValue, formatConfig) ?? clampedSelectedValue;
            const nextUnclamped = applySegmentDigits(baseValue, segment.kind, digits);
            const nextClamped = clampPickerToRange(
                nextUnclamped,
                normalizedMinValue,
                normalizedMaxValue,
                type,
            );
            const nextValue = formatPickerValueWithFormat(nextClamped, formatConfig);
            commitValue(nextValue);
            if (options?.clearBuffer !== false) {
                bufferedDigitsRef.current = null;
            }

            if (moveToNext) {
                selectSegment(segmentIndex + 1);
            } else {
                selectSegment(segmentIndex);
            }
        },
        [
            clampedSelectedValue,
            commitValue,
            displayValue,
            formatConfig,
            normalizedMaxValue,
            normalizedMinValue,
            selectSegment,
            type,
        ],
    );

    const finalizeBufferedDigits = useCallback(
        (segmentIndex: number, moveToNext: boolean) => {
            const buffered = bufferedDigitsRef.current;
            const segment = formatConfig.segments[segmentIndex];
            if (buffered?.segmentIndex !== segmentIndex || !segment) {
                if (moveToNext) {
                    selectSegment(segmentIndex + 1);
                }
                return;
            }

            const padded = buffered.digits.padStart(segment.length, '0');
            commitSegmentDigits(segmentIndex, padded, moveToNext);
        },
        [commitSegmentDigits, formatConfig.segments, selectSegment],
    );

    const openPicker = useCallback(() => {
        /* c8 ignore start -- disabled inputs cannot trigger picker open interactions. */
        if (disabled) {
            return;
        }
        /* c8 ignore stop */
        if (type !== 'time') {
            setViewMonth(startOfMonth(selectedDate));
        }
        setActiveChooser(null);
        setIsOpen(true);
    }, [disabled, selectedDate, type]);

    const togglePicker = useCallback(() => {
        /* c8 ignore start -- disabled icon button cannot trigger click events. */
        if (disabled) {
            return;
        }
        /* c8 ignore stop */
        if (isOpen) {
            closePicker();
            return;
        }
        openPicker();
    }, [closePicker, disabled, isOpen, openPicker]);

    const goToPreviousMonth = useCallback(() => {
        setViewMonth((prev) => {
            if (prev.getFullYear() === 0 && prev.getMonth() === 0) {
                return prev;
            }

            return createDateAtLocalMidnight(prev.getFullYear(), prev.getMonth() - 1, 1);
        });
    }, []);

    const goToNextMonth = useCallback(() => {
        setViewMonth((prev) => {
            if (prev.getFullYear() === 9999 && prev.getMonth() === 11) {
                return prev;
            }

            return createDateAtLocalMidnight(prev.getFullYear(), prev.getMonth() + 1, 1);
        });
    }, []);

    const selectMonth = useCallback((month: number) => {
        setViewMonth((prev) => createDateAtLocalMidnight(prev.getFullYear(), month, 1));
        setActiveChooser(null);
    }, []);

    const selectYear = useCallback((year: number) => {
        setViewMonth((prev) => createDateAtLocalMidnight(year, prev.getMonth(), 1));
        setActiveChooser(null);
    }, []);

    const handleDateCommit = useCallback(
        (nextDate: Date) => {
            const normalizedDate = startOfDay(nextDate);

            if (type === 'date') {
                const candidate: PickerValue = {
                    date: normalizedDate,
                    hour: 0,
                    minute: 0,
                };
                /* c8 ignore start -- out-of-range date cells are disabled in the calendar UI. */
                if (!isWithinRange(candidate, normalizedMinValue, normalizedMaxValue, type)) {
                    return;
                }
                /* c8 ignore stop */

                commitValue(formatPickerValueWithFormat(candidate, formatConfig));
                closePicker();
                return;
            }

            const candidate: PickerValue = {
                date: normalizedDate,
                hour: selectedHour,
                minute: selectedMinute,
            };
            const nextValue = clampPickerToRange(
                candidate,
                normalizedMinValue,
                normalizedMaxValue,
                type,
            );
            commitValue(formatPickerValueWithFormat(nextValue, formatConfig));
            setViewMonth(startOfMonth(normalizedDate));
        },
        [
            closePicker,
            commitValue,
            formatConfig,
            normalizedMaxValue,
            normalizedMinValue,
            selectedHour,
            selectedMinute,
            type,
        ],
    );

    const handleHourCommit = useCallback(
        (hour: number) => {
            const baseCandidate: PickerValue = {
                date: selectedDate,
                hour,
                minute: selectedMinute,
            };

            const nextValue = clampPickerToRange(
                baseCandidate,
                normalizedMinValue,
                normalizedMaxValue,
                type,
            );
            commitValue(formatPickerValueWithFormat(nextValue, formatConfig));
        },
        [
            commitValue,
            formatConfig,
            normalizedMaxValue,
            normalizedMinValue,
            selectedDate,
            selectedMinute,
            type,
        ],
    );

    const handleMinuteCommit = useCallback(
        (minute: number) => {
            const baseCandidate: PickerValue = {
                date: selectedDate,
                hour: selectedHour,
                minute,
            };

            const nextValue = clampPickerToRange(
                baseCandidate,
                normalizedMinValue,
                normalizedMaxValue,
                type,
            );
            commitValue(formatPickerValueWithFormat(nextValue, formatConfig));
        },
        [
            commitValue,
            formatConfig,
            normalizedMaxValue,
            normalizedMinValue,
            selectedDate,
            selectedHour,
            type,
        ],
    );

    const handleInputFocus = useCallback(() => {
        if (disabled) {
            return;
        }

        selectSegment(0);
    }, [disabled, selectSegment]);

    const handleInputMouseUp = useCallback(() => {
        /* c8 ignore start -- disabled inputs do not emit mouse interaction events. */
        if (disabled) {
            return;
        }
        /* c8 ignore stop */

        selectSegment(resolveCurrentSegmentIndex());
    }, [disabled, resolveCurrentSegmentIndex, selectSegment]);

    const handleInputClick = useCallback(() => {
        /* c8 ignore start -- disabled inputs do not emit click interaction events. */
        if (disabled) {
            return;
        }
        /* c8 ignore stop */

        selectSegment(resolveCurrentSegmentIndex());
    }, [disabled, resolveCurrentSegmentIndex, selectSegment]);

    const handleInputPaste = useCallback(
        (event: ClipboardEvent<HTMLInputElement>) => {
            if (disabled) {
                return;
            }

            const pasted = event.clipboardData.getData('text').trim();
            const parsed = parsePickerValueWithFormat(pasted, formatConfig);
            if (!parsed) {
                event.preventDefault();
                return;
            }

            const clamped = clampPickerToRange(
                parsed,
                normalizedMinValue,
                normalizedMaxValue,
                type,
            );
            event.preventDefault();
            commitValue(formatPickerValueWithFormat(clamped, formatConfig));
        },
        [commitValue, disabled, formatConfig, normalizedMaxValue, normalizedMinValue, type],
    );

    const handleInputKeyDown = useCallback(
        (event: ReactKeyboardEvent<HTMLInputElement>) => {
            if (disabled) {
                return;
            }

            const key = event.key;
            const now = Date.now();
            const activeBuffer = bufferedDigitsRef.current;
            let segmentIndex = resolveCurrentSegmentIndex();

            if (
                activeBuffer &&
                now - activeBuffer.timestamp <= SEGMENT_EDIT_TIMEOUT_MS &&
                formatConfig.segments[activeBuffer.segmentIndex]
            ) {
                segmentIndex = activeBuffer.segmentIndex;
            }

            const segment = formatConfig.segments[segmentIndex];
            /* c8 ignore start -- segment index resolution always maps to a valid segment. */
            if (!segment) {
                return;
            }
            /* c8 ignore stop */

            if (key === 'ArrowDown' && !isOpen) {
                event.preventDefault();
                openPicker();
                return;
            }

            if (key === 'ArrowLeft') {
                event.preventDefault();
                finalizeBufferedDigits(segmentIndex, false);
                selectSegment(segmentIndex - 1);
                return;
            }

            if (key === 'ArrowRight') {
                event.preventDefault();
                finalizeBufferedDigits(segmentIndex, false);
                selectSegment(segmentIndex + 1);
                return;
            }

            if (key === 'Tab') {
                event.preventDefault();
                finalizeBufferedDigits(segmentIndex, false);
                selectSegment(segmentIndex + (event.shiftKey ? -1 : 1));
                return;
            }

            if (key === 'Backspace' || key === 'Delete') {
                event.preventDefault();
                bufferedDigitsRef.current = null;

                if (segment.kind === 'year') {
                    commitSegmentDigits(segmentIndex, pad4(selectedDate.getFullYear()), false);
                    return;
                }

                if (segment.kind === 'month' || segment.kind === 'day') {
                    commitSegmentDigits(segmentIndex, '01', false);
                    return;
                }

                commitSegmentDigits(segmentIndex, '00', false);
                return;
            }

            if (key.length === 1 && formatConfig.literalChars.has(key)) {
                event.preventDefault();
                finalizeBufferedDigits(segmentIndex, true);
                return;
            }

            if (/^\d$/.test(key)) {
                event.preventDefault();

                const buffered = bufferedDigitsRef.current;
                let digits = '';

                if (
                    buffered &&
                    buffered.segmentIndex === segmentIndex &&
                    now - buffered.timestamp <= SEGMENT_EDIT_TIMEOUT_MS
                ) {
                    digits = buffered.digits;
                }

                /* c8 ignore start -- buffered digits are reset on segment completion. */
                if (digits.length >= segment.length) {
                    digits = key;
                } else {
                    digits += key;
                }
                /* c8 ignore stop */

                bufferedDigitsRef.current = {
                    segmentIndex,
                    digits,
                    timestamp: now,
                };

                if (digits.length === segment.length) {
                    commitSegmentDigits(segmentIndex, digits, true);
                } else {
                    commitSegmentDigits(segmentIndex, digits, false, {
                        clearBuffer: false,
                    });
                }
                return;
            }

            if (key === 'Enter') {
                event.preventDefault();
                finalizeBufferedDigits(segmentIndex, false);
                return;
            }

            if (key.length === 1) {
                event.preventDefault();
            }
        },
        [
            commitSegmentDigits,
            disabled,
            finalizeBufferedDigits,
            formatConfig.literalChars,
            formatConfig.segments,
            isOpen,
            openPicker,
            resolveCurrentSegmentIndex,
            selectSegment,
            selectedDate,
        ],
    );

    const handleInputBlur = useCallback(
        (event: FocusEvent<HTMLInputElement>) => {
            const segmentIndex = resolveCurrentSegmentIndex();
            finalizeBufferedDigits(segmentIndex, false);
            bufferedDigitsRef.current = null;
            onBlur?.(event);
        },
        [finalizeBufferedDigits, onBlur, resolveCurrentSegmentIndex],
    );

    const calendarVisible = type !== 'time';
    const timeVisible = type !== 'date';

    const popupContent =
        isOpen && !disabled && typeof document !== 'undefined'
            ? createPortal(
                  <div
                      ref={popupRef}
                      role="dialog"
                      aria-label={
                          type === 'date'
                              ? 'Date picker popup'
                              : type === 'time'
                                ? 'Time picker popup'
                                : 'Date and time picker popup'
                      }
                      className={joinClassNames(
                          'datepicker-popup',
                          popupPosition?.placement === 'top'
                              ? 'datepicker-popup-top'
                              : 'datepicker-popup-bottom',
                      )}
                      style={{
                          top: popupPosition?.top ?? 0,
                          left: popupPosition?.left ?? 0,
                          minWidth: popupPosition?.minWidth,
                          visibility: popupPosition ? 'visible' : 'hidden',
                      }}
                  >
                      {calendarVisible ? (
                          <div className="datepicker-panel">
                              <div className="datepicker-calendar-nav">
                                  <button
                                      type="button"
                                      className="datepicker-nav-btn"
                                      aria-label="Previous month"
                                      onClick={goToPreviousMonth}
                                  >
                                      <ChevronLeftIcon className="h-4 w-4" aria-hidden="true" />
                                  </button>
                                  <div className="datepicker-heading-controls">
                                      <div className="datepicker-chooser">
                                          <button
                                              type="button"
                                              className="datepicker-chooser-btn"
                                              onClick={() =>
                                                  setActiveChooser((prev) =>
                                                      prev === 'month' ? null : 'month',
                                                  )
                                              }
                                          >
                                              {monthLabels[viewMonth.getMonth()]}
                                          </button>
                                          {activeChooser === 'month' ? (
                                              <div
                                                  className="datepicker-chooser-menu"
                                                  role="listbox"
                                                  aria-label="Choose month"
                                              >
                                                  {monthLabels.map((monthLabel, monthIndex) => (
                                                      <button
                                                          key={monthLabel}
                                                          type="button"
                                                          className={joinClassNames(
                                                              'datepicker-chooser-option',
                                                              monthIndex === viewMonth.getMonth() &&
                                                                  'is-selected',
                                                          )}
                                                          onClick={() => selectMonth(monthIndex)}
                                                      >
                                                          {monthLabel}
                                                      </button>
                                                  ))}
                                              </div>
                                          ) : null}
                                      </div>
                                      <div className="datepicker-chooser">
                                          <button
                                              type="button"
                                              className="datepicker-chooser-btn"
                                              onClick={() =>
                                                  setActiveChooser((prev) =>
                                                      prev === 'year' ? null : 'year',
                                                  )
                                              }
                                          >
                                              {viewMonth.getFullYear()}
                                          </button>
                                          {activeChooser === 'year' ? (
                                              <div
                                                  className="datepicker-chooser-menu"
                                                  role="listbox"
                                                  aria-label="Choose year"
                                              >
                                                  {yearOptions.map((year) => (
                                                      <button
                                                          key={year}
                                                          type="button"
                                                          className={joinClassNames(
                                                              'datepicker-chooser-option',
                                                              year === viewMonth.getFullYear() &&
                                                                  'is-selected',
                                                          )}
                                                          onClick={() => selectYear(year)}
                                                      >
                                                          {year}
                                                      </button>
                                                  ))}
                                              </div>
                                          ) : null}
                                      </div>
                                  </div>
                                  <button
                                      type="button"
                                      className="datepicker-nav-btn"
                                      aria-label="Next month"
                                      onClick={goToNextMonth}
                                  >
                                      <ChevronRightIcon className="h-4 w-4" aria-hidden="true" />
                                  </button>
                              </div>

                              <div className="datepicker-weekdays" role="presentation">
                                  {weekdayLabels.map((weekday) => (
                                      <span key={weekday} className="datepicker-weekday">
                                          {weekday}
                                      </span>
                                  ))}
                              </div>

                              <div className="datepicker-grid" role="grid">
                                  {monthGrid.map((dateCell) => {
                                      const currentMonth =
                                          dateCell.getMonth() === viewMonth.getMonth() &&
                                          dateCell.getFullYear() === viewMonth.getFullYear();
                                      const today = isSameDay(dateCell, startOfDay(new Date()));
                                      const selected = isSameDay(dateCell, selectedDate);
                                      const dayDisabled = !isDateSelectableForRange(
                                          dateCell,
                                          type,
                                          normalizedMinValue,
                                          normalizedMaxValue,
                                      );

                                      return (
                                          <button
                                              key={dateCell.toISOString()}
                                              type="button"
                                              className={joinClassNames(
                                                  'datepicker-day',
                                                  !currentMonth && 'is-outside-month',
                                                  today && 'is-today',
                                                  selected && 'is-selected',
                                              )}
                                              onClick={() => handleDateCommit(dateCell)}
                                              disabled={dayDisabled}
                                          >
                                              {dateCell.getDate()}
                                          </button>
                                      );
                                  })}
                              </div>
                          </div>
                      ) : null}

                      {timeVisible ? (
                          <div className="datepicker-panel">
                              <div className="datepicker-time-root">
                                  <div className="datepicker-time-column">
                                      <span className="datepicker-time-title">Hours</span>
                                      <div
                                          className="datepicker-time-list"
                                          role="listbox"
                                          aria-label="Hours"
                                      >
                                          {HOURS.map((hour) => {
                                              const hourDisabled = !isHourSelectableForRange(
                                                  selectedDate,
                                                  hour,
                                                  type,
                                                  normalizedMinValue,
                                                  normalizedMaxValue,
                                              );

                                              return (
                                                  <button
                                                      key={hour}
                                                      type="button"
                                                      className={joinClassNames(
                                                          'datepicker-time-option',
                                                          hour === selectedHour && 'is-selected',
                                                      )}
                                                      onClick={() => handleHourCommit(hour)}
                                                      disabled={hourDisabled}
                                                  >
                                                      {pad2(hour)}
                                                  </button>
                                              );
                                          })}
                                      </div>
                                  </div>

                                  <div className="datepicker-time-column">
                                      <span className="datepicker-time-title">Minutes</span>
                                      <div
                                          className="datepicker-time-list"
                                          role="listbox"
                                          aria-label="Minutes"
                                      >
                                          {MINUTES.map((minute) => {
                                              const minuteCandidate: PickerValue = {
                                                  date: selectedDate,
                                                  hour: selectedHour,
                                                  minute,
                                              };
                                              const minuteDisabled = !isWithinRange(
                                                  minuteCandidate,
                                                  normalizedMinValue,
                                                  normalizedMaxValue,
                                                  type,
                                              );

                                              return (
                                                  <button
                                                      key={minute}
                                                      type="button"
                                                      className={joinClassNames(
                                                          'datepicker-time-option',
                                                          minute === selectedMinute &&
                                                              'is-selected',
                                                      )}
                                                      onClick={() => handleMinuteCommit(minute)}
                                                      disabled={minuteDisabled}
                                                  >
                                                      {pad2(minute)}
                                                  </button>
                                              );
                                          })}
                                      </div>
                                  </div>
                              </div>
                          </div>
                      ) : null}
                  </div>,
                  document.body,
              )
            : null;

    return (
        <label
            className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerWidthClass} ${className}`.trim()}
        >
            {label ? <span className={labelClass}>{label}</span> : null}
            <div ref={inputWrapperRef} className={inputWrapperClass}>
                <input
                    type="text"
                    value={displayValue}
                    name={name}
                    onChange={handleInputChange}
                    onBlur={handleInputBlur}
                    onFocus={handleInputFocus}
                    onMouseUp={handleInputMouseUp}
                    onClick={handleInputClick}
                    onKeyDown={handleInputKeyDown}
                    onPaste={handleInputPaste}
                    ref={setInputNode}
                    placeholder={placeholder}
                    disabled={disabled}
                    required={required}
                    autoComplete="off"
                    className={`field w-full ${hasTrailingIcon ? 'pr-10' : ''} ${inputSizeClass} ${error ? 'border-red-400/70 focus:border-red-400 focus:ring-red-400/30' : ''} ${inputClassName}`.trim()}
                />
                {resolvedRightIcon ? (
                    <button
                        type="button"
                        className="datepicker-icon-btn"
                        onClick={togglePicker}
                        disabled={disabled}
                        aria-label={isOpen ? 'Close date picker' : 'Open date picker'}
                    >
                        {resolvedRightIcon}
                    </button>
                ) : null}
            </div>
            {error ? (
                <span className="mt-1 block text-xs" style={{ color: 'var(--error-text)' }}>
                    {error}
                </span>
            ) : null}
            {popupContent}
        </label>
    );
}
