import { CalendarDays, ChevronLeft, ChevronRight, Clock } from "lucide-react";
import { useEffect, useMemo, useRef, useState, type InputHTMLAttributes } from "react";
import useOutsideDismiss from "../hooks/useOutsideDismiss";

type BaseProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "onChange" | "min" | "max"> & {
  value: string;
  onChange: (value: string) => void;
  min?: string;
  max?: string;
};

const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const TIME_PATTERN = /^\d{2}:\d{2}$/;

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

function dateString(date: Date): string {
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
}

function parseDate(value: string): Date | null {
  if (!DATE_PATTERN.test(value)) return null;
  const [year, month, day] = value.split("-").map(Number);
  const date = new Date(year, month - 1, day);
  if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) return null;
  return date;
}

function monthLabel(date: Date): string {
  return date.toLocaleDateString(undefined, { month: "long", year: "numeric" });
}

function clampDateValue(value: string, min?: string, max?: string): string {
  if (min && value < min) return min;
  if (max && value > max) return max;
  return value;
}

function datePartsFromDateTime(value: string): {date: string;time: string;} {
  if (!value) return { date: "", time: "" };
  const [date = "", time = ""] = value.split("T", 2);
  return { date, time: time.slice(0, 5) };
}

function combineDateTime(date: string, time: string): string {
  if (!date && !time) return "";
  return `${date || dateString(new Date())}T${time || "00:00"}`;
}

export function DateField({ value, onChange, min, max, disabled, className = "", placeholder = "i18n:govoplan-core.yyyy_mm_dd.d3f8f7b8", ...props }: BaseProps) {
  const selectedDate = parseDate(value);
  const [open, setOpen] = useState(false);
  const [visibleMonth, setVisibleMonth] = useState<Date>(() => selectedDate ?? new Date());
  const rootRef = useOutsideDismiss(open, () => setOpen(false));
  const inputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    if (selectedDate) setVisibleMonth(new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
  }, [value]); // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    const input = inputRef.current;
    if (!input) return;
    let message = "";
    if (value && !parseDate(value)) message = "i18n:govoplan-core.use_yyyy_mm_dd.406d2e4d";else
    if (value && min && value < min) message = `Use ${min} or later.`;else
    if (value && max && value > max) message = `Use ${max} or earlier.`;
    input.setCustomValidity(message);
  }, [value, min, max]);

  const weeks = useMemo(() => {
    const first = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1);
    const start = new Date(first);
    const weekday = (first.getDay() + 6) % 7;
    start.setDate(first.getDate() - weekday);
    return Array.from({ length: 42 }, (_unused, index) => {
      const date = new Date(start);
      date.setDate(start.getDate() + index);
      return date;
    });
  }, [visibleMonth]);

  function selectDate(nextDate: Date) {
    const nextValue = clampDateValue(dateString(nextDate), min, max);
    onChange(nextValue);
    setOpen(false);
  }

  function moveMonth(delta: number) {
    setVisibleMonth((current) => new Date(current.getFullYear(), current.getMonth() + delta, 1));
  }

  return (
    <div ref={rootRef} className={`date-field ${className}`.trim()}>
      <input
        {...props}
        ref={inputRef}
        type="text"
        inputMode="numeric"
        pattern="\d{4}-\d{2}-\d{2}"
        value={value}
        min={undefined}
        max={undefined}
        placeholder={placeholder}
        disabled={disabled}
        onChange={(event) => onChange(event.target.value)} />
      
      <button type="button" className="date-field-trigger" onClick={() => !disabled && setOpen((current) => !current)} disabled={disabled} aria-label="i18n:govoplan-core.choose_date.e7877fd9">
        <CalendarDays size={16} />
      </button>
      {open &&
      <div className="date-field-popover" role="dialog" aria-label="i18n:govoplan-core.calendar_picker.18756418">
          <div className="date-field-monthbar">
            <button type="button" onClick={() => moveMonth(-1)} aria-label="i18n:govoplan-core.previous_month.46a29921"><ChevronLeft size={16} /></button>
            <strong>{monthLabel(visibleMonth)}</strong>
            <button type="button" onClick={() => moveMonth(1)} aria-label="i18n:govoplan-core.next_month.8abf7cf1"><ChevronRight size={16} /></button>
          </div>
          <div className="date-field-weekdays" aria-hidden="true">
            {["i18n:govoplan-core.mo.91e885d8", "i18n:govoplan-core.tu.b44892b7", "i18n:govoplan-core.we.a24ae9fa", "i18n:govoplan-core.th.6929e765", "i18n:govoplan-core.fr.aa77f314", "i18n:govoplan-core.sa.50cf95ce", "i18n:govoplan-core.su.25bfc51c"].map((label) => <span key={label}>{label}</span>)}
          </div>
          <div className="date-field-grid">
            {weeks.map((day) => {
            const dayValue = dateString(day);
            const inMonth = day.getMonth() === visibleMonth.getMonth();
            const isSelected = dayValue === value;
            const unavailable = Boolean(min && dayValue < min || max && dayValue > max);
            return (
              <button
                key={dayValue}
                type="button"
                className={[inMonth ? "" : "is-muted", isSelected ? "is-selected" : ""].filter(Boolean).join(" ")}
                onClick={() => selectDate(day)}
                disabled={unavailable}>
                
                  {day.getDate()}
                </button>);

          })}
          </div>
        </div>
      }
    </div>);

}

export function TimeField({ value, onChange, min, max, className = "", placeholder = "i18n:govoplan-core.hh_mm.a4c7ee9b", ...props }: BaseProps) {
  const inputRef = useRef<HTMLInputElement | null>(null);
  useEffect(() => {
    const input = inputRef.current;
    if (!input) return;
    let message = "";
    if (value && !TIME_PATTERN.test(value)) message = "i18n:govoplan-core.use_hh_mm.e995be3f";else
    if (value && min && value < min) message = `Use ${min} or later.`;else
    if (value && max && value > max) message = `Use ${max} or earlier.`;
    input.setCustomValidity(message);
  }, [value, min, max]);

  return (
    <div className={`time-field ${className}`.trim()}>
      <input
        {...props}
        ref={inputRef}
        type="text"
        inputMode="numeric"
        pattern="\d{2}:\d{2}"
        value={value}
        placeholder={placeholder}
        onChange={(event) => onChange(event.target.value)} />
      
      <Clock className="time-field-icon" size={16} aria-hidden="true" />
    </div>);

}

export function DateTimeField({ value, onChange, min, max, disabled, className = "", ...props }: BaseProps) {
  const parts = datePartsFromDateTime(value);
  const minParts = datePartsFromDateTime(min || "");
  const maxParts = datePartsFromDateTime(max || "");

  function changeDate(nextDate: string) {
    onChange(combineDateTime(nextDate, parts.time));
  }

  function changeTime(nextTime: string) {
    onChange(combineDateTime(parts.date, nextTime));
  }

  return (
    <div className={`date-time-field ${className}`.trim()}>
      <DateField
        {...props}
        value={parts.date}
        min={minParts.date}
        max={maxParts.date}
        disabled={disabled}
        onChange={changeDate} />
      
      <TimeField
        value={parts.time}
        min={minParts.date && minParts.date === parts.date ? minParts.time : undefined}
        max={maxParts.date && maxParts.date === parts.date ? maxParts.time : undefined}
        disabled={disabled}
        onChange={changeTime} />
      
    </div>);

}

export default DateField;
