import { forwardRef, useRef } from "react";
import {
  type AriaDatePickerProps,
  type DateValue,
  useDateField,
  useDateSegment,
} from "@react-aria/datepicker";
import { useLocale } from "@react-aria/i18n";
import {
  type DateFieldState,
  type DateSegment as DateSegmentType,
  useDateFieldState,
} from "@react-stately/datepicker";
import { createCalendar } from "@internationalized/date";
import { twJoin } from "tailwind-merge";
import { useMergeRefs } from "use-callback-ref";

import { twMerge } from "../../../tailwind.config";

export interface DateFieldProps extends AriaDatePickerProps<DateValue> {
  className?: string;
}

/**
 * Date field with focusable individual inputs for segments DD, MM, and YYYY.
 *
 * The ref is forwarded to the first segment.
 */
export const DateField = forwardRef<HTMLSpanElement, DateFieldProps>(
  ({ className, ...props }, externalRef) => {
    const { locale } = useLocale();
    const state = useDateFieldState({
      ...props,
      locale,
      createCalendar,
    });

    const ref = useRef<HTMLSpanElement>(null);
    const { fieldProps } = useDateField(props, state, ref);
    const firstEditableSegment = state.segments.findIndex(
      (segment) => segment.isEditable
    );

    return (
      <span
        {...fieldProps}
        ref={ref}
        className={twMerge(
          !state.value && "inline-flex text-N100 focus-within:text-N700",
          className
        )}
      >
        {state.segments.map((segment, i) => (
          <DateSegment
            ref={i === firstEditableSegment ? externalRef : undefined}
            key={i}
            segment={segment}
            state={state}
          />
        ))}
      </span>
    );
  }
);
DateField.displayName = "DateField";

const DateSegment = forwardRef<
  HTMLSpanElement,
  {
    segment: DateSegmentType;
    state: DateFieldState;
  }
>(({ segment, state }, externalRef) => {
  const ref = useRef<HTMLSpanElement>(null);
  const { segmentProps } = useDateSegment(segment, state, ref);
  const combinedRef = useMergeRefs([ref, externalRef]);

  return (
    <span
      {...segmentProps}
      ref={combinedRef}
      className={twJoin(
        segment.isEditable &&
          "uppercase focus:outline-none focus-visible:bg-B200/30",
        segment.isEditable && !segment.isPlaceholder && "text-N700"
      )}
    >
      {segment.text}
    </span>
  );
});
DateSegment.displayName = "DateSegment";
