"use client";

import { useId, useRef, useState, type ChangeEvent } from "react";
import { Calendar } from "lucide-react";
import { cn } from "@/lib/utils";

type Props = {
  /** Form field name (submitted to server as ISO YYYY-MM-DD). */
  name: string;
  /** Initial value in ISO YYYY-MM-DD. */
  defaultValue?: string;
  id?: string;
  required?: boolean;
  className?: string;
  /** ISO YYYY-MM-DD min, e.g. "1900-01-01". */
  min?: string;
  /** ISO YYYY-MM-DD max. */
  max?: string;
  placeholder?: string;
  ariaLabel?: string;
  onChangeISO?: (iso: string) => void;
};

/**
 * DD/MM/YYYY masked text input with a calendar popup picker.
 *
 * - Visible input is text masked DD/MM/YYYY.
 * - Hidden input with the same `name` carries ISO YYYY-MM-DD to the form.
 * - Calendar icon opens the native date picker (works on desktop + mobile).
 * - Empty / partial / invalid input submits empty string, matching prior
 *   `<input type="date">` behavior for un-filled fields.
 */
export function DateInput({
  name,
  defaultValue = "",
  id,
  required,
  className,
  min,
  max,
  placeholder = "DD/MM/YYYY",
  ariaLabel,
  onChangeISO,
}: Props) {
  const reactId = useId();
  const inputId = id ?? `${name}-${reactId}`;
  const [text, setText] = useState(() => isoToDmy(defaultValue));
  // Reset displayed text if `defaultValue` prop changes (e.g. parent
  // re-renders with a different ISO). Pattern preferred over useEffect:
  // see https://react.dev/learn/you-might-not-need-an-effect
  const [prevDefault, setPrevDefault] = useState(defaultValue);
  if (defaultValue !== prevDefault) {
    setPrevDefault(defaultValue);
    setText(isoToDmy(defaultValue));
  }
  const pickerRef = useRef<HTMLInputElement>(null);

  const iso = dmyToIso(text);

  function onTextChange(e: ChangeEvent<HTMLInputElement>) {
    const next = formatMask(e.target.value);
    setText(next);
    onChangeISO?.(dmyToIso(next));
  }

  function onPickerChange(e: ChangeEvent<HTMLInputElement>) {
    const v = e.target.value; // ISO YYYY-MM-DD
    setText(isoToDmy(v));
    onChangeISO?.(v);
  }

  function openPicker() {
    const el = pickerRef.current;
    if (!el) return;
    // Modern browsers: showPicker() is the only reliable way to open it
    // programmatically. Fallback to focus() if not supported.
    if (typeof el.showPicker === "function") {
      try {
        el.showPicker();
        return;
      } catch {
        // ignore — fall through to focus
      }
    }
    el.focus();
    el.click();
  }

  return (
    <div className={cn("relative", className)}>
      <input
        id={inputId}
        type="text"
        inputMode="numeric"
        autoComplete="off"
        placeholder={placeholder}
        aria-label={ariaLabel}
        value={text}
        onChange={onTextChange}
        maxLength={10}
        required={required}
        // Native pattern + title gives a nicer browser-side validity msg
        // when the form is submitted with a partial value.
        pattern="\d{2}/\d{2}/\d{4}"
        title="Định dạng: DD/MM/YYYY"
        className="bg-bg-inset border-stroke-default text-ink-primary placeholder:text-ink-muted/60 focus:border-brand-gold focus:ring-brand-gold/20 w-full rounded-md border px-3 py-2.5 pr-10 text-sm focus:ring-2 focus:outline-none"
      />

      <button
        type="button"
        onClick={openPicker}
        aria-label="Mở lịch chọn ngày"
        tabIndex={-1}
        className="text-ink-muted hover:text-brand-gold absolute top-1/2 right-2 inline-flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-md transition"
      >
        <Calendar className="h-4 w-4" aria-hidden="true" />
      </button>

      {/* Hidden ISO mirror submitted with the form */}
      <input type="hidden" name={name} value={iso} />

      {/* Visually hidden picker the icon opens. Kept inside the form so
          mobile UA does not detach it; sized 0 so it never renders. */}
      <input
        ref={pickerRef}
        type="date"
        tabIndex={-1}
        aria-hidden="true"
        value={iso}
        onChange={onPickerChange}
        min={min}
        max={max}
        className="pointer-events-none absolute right-2 bottom-0 h-0 w-0 opacity-0"
      />
    </div>
  );
}

// ---------- helpers ----------

/** ISO "YYYY-MM-DD" -> "DD/MM/YYYY". Returns "" on invalid input. */
function isoToDmy(iso: string): string {
  if (!iso) return "";
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
  if (!m) return "";
  const [, y, mm, d] = m;
  return `${d}/${mm}/${y}`;
}

/** "DD/MM/YYYY" -> "YYYY-MM-DD". Returns "" on partial / invalid input. */
function dmyToIso(dmy: string): string {
  const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(dmy);
  if (!m) return "";
  const [, d, mm, y] = m;
  const dn = parseInt(d, 10);
  const mn = parseInt(mm, 10);
  const yn = parseInt(y, 10);
  if (mn < 1 || mn > 12 || dn < 1 || dn > 31 || yn < 1) return "";
  // Reject impossible dates (e.g. 31/02/...). Date constructor would
  // silently roll over, so we cross-check the parts.
  const dt = new Date(Date.UTC(yn, mn - 1, dn));
  if (dt.getUTCFullYear() !== yn || dt.getUTCMonth() !== mn - 1 || dt.getUTCDate() !== dn) {
    return "";
  }
  return `${y}-${mm}-${d}`;
}

/**
 * Progressive mask: as the user types digits, auto-insert "/" so the
 * displayed value walks DD -> DD/MM -> DD/MM/YYYY. Backspace works
 * naturally because we strip non-digits first.
 */
function formatMask(raw: string): string {
  const digits = raw.replace(/\D/g, "").slice(0, 8);
  if (digits.length <= 2) return digits;
  if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
  return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
}
