"use client";

import {
  useEffect,
  useId,
  useMemo,
  useRef,
  useState,
  type CSSProperties,
} from "react";
import { jalaliDatePickerDict } from "@/components/jalali-date-picker/dict";
import { toPersianDigits } from "@/lib/to-english-digits";
import {
  asJalaliViewDate,
  buildCalendarDays,
  buildJalaliYearRange,
  clampJalaliViewDate,
  formatJalaliDisplay,
  getJalaliViewDate,
  isDayDisabled,
  isMonthAfterMax,
  isMonthBeforeMin,
  isSameGregorianDay,
  isTodayJalali,
  isViewAtMaxMonth,
  isViewAtMinMonth,
  isYearAfterMax,
  isYearBeforeMin,
  resolveJalaliYearRange,
  setJalaliViewMonthYear,
  toGregorianIso,
  type JalaliDay,
} from "@/components/jalali-date-picker/utils";

type PickerMode = "calendar" | "month" | "year";

type DateBounds = {
  minDate?: string;
  maxDate?: string;
};

type JalaliDatePickerProps = {
  id?: string;
  value: string;
  onChange: (value: string) => void;
  placeholder?: string;
  disabled?: boolean;
  clearable?: boolean;
  minYear?: number;
  maxYear?: number;
  minDate?: string;
  maxDate?: string;
  className?: string;
};

export function JalaliDatePicker({
  id,
  value,
  onChange,
  placeholder = jalaliDatePickerDict.placeholder,
  disabled = false,
  clearable = false,
  minYear,
  maxYear,
  minDate,
  maxDate,
  className = "",
}: JalaliDatePickerProps) {
  const generatedId = useId();
  const inputId = id ?? generatedId;
  const containerRef = useRef<HTMLDivElement>(null);
  const panelRef = useRef<HTMLDivElement>(null);
  const selectedYearRef = useRef<HTMLButtonElement>(null);

  const dateBounds = useMemo<DateBounds>(
    () => ({
      ...(minDate ? { minDate } : {}),
      ...(maxDate ? { maxDate } : {}),
    }),
    [minDate, maxDate],
  );

  const { minYear: resolvedMinYear, maxYear: resolvedMaxYear } = useMemo(
    () =>
      resolveJalaliYearRange({
        minYear,
        maxYear,
        minDate,
        maxDate,
      }),
    [minYear, maxYear, minDate, maxDate],
  );

  const yearOptions = buildJalaliYearRange(resolvedMinYear, resolvedMaxYear);

  const [open, setOpen] = useState(false);
  const [pickerMode, setPickerMode] = useState<PickerMode>("calendar");
  const [panelStyle, setPanelStyle] = useState<CSSProperties>({});
  const [viewDate, setViewDate] = useState<JalaliDay>(() =>
    getJalaliViewDate(value, dateBounds),
  );

  const updatePanelPosition = () => {
    const rect = containerRef.current?.getBoundingClientRect();

    if (!rect) {
      return;
    }

    setPanelStyle({
      top: rect.bottom + 8,
      left: rect.left,
      width: Math.max(rect.width, 280),
    });
  };

  useEffect(() => {
    if (!open) {
      return;
    }

    setViewDate(getJalaliViewDate(value, dateBounds));
    setPickerMode("calendar");
  }, [open, value, dateBounds]);

  useEffect(() => {
    if (!open) {
      return;
    }

    updatePanelPosition();

    const handlePointerDown = (event: MouseEvent) => {
      const target = event.target as Node;

      if (
        !containerRef.current?.contains(target) &&
        !panelRef.current?.contains(target)
      ) {
        setOpen(false);
      }
    };

    const handleReposition = () => {
      updatePanelPosition();
    };

    document.addEventListener("mousedown", handlePointerDown);
    window.addEventListener("resize", handleReposition);
    window.addEventListener("scroll", handleReposition, true);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      window.removeEventListener("resize", handleReposition);
      window.removeEventListener("scroll", handleReposition, true);
    };
  }, [open]);

  useEffect(() => {
    if (pickerMode !== "year" || !open) {
      return;
    }

    selectedYearRef.current?.scrollIntoView({ block: "center" });
  }, [pickerMode, open, viewDate]);

  const togglePicker = () => {
    if (disabled) {
      return;
    }

    if (open) {
      setOpen(false);
      return;
    }

    setViewDate(getJalaliViewDate(value, dateBounds));
    setPickerMode("calendar");
    updatePanelPosition();
    setOpen(true);
  };

  const selectDay = (day: number) => {
    const current = asJalaliViewDate(viewDate);

    if (isDayDisabled(current, day, dateBounds)) {
      return;
    }

    onChange(toGregorianIso(current.year(), current.month(), day));
    setOpen(false);
  };

  const selectMonth = (month: number) => {
    const current = asJalaliViewDate(viewDate);

    if (
      (maxDate && isMonthAfterMax(current.year(), month, maxDate)) ||
      (minDate && isMonthBeforeMin(current.year(), month, minDate))
    ) {
      return;
    }

    setViewDate((previous) =>
      setJalaliViewMonthYear(previous, current.year(), month, dateBounds),
    );
    setPickerMode("calendar");
  };

  const selectYear = (year: number) => {
    if (
      (maxDate && isYearAfterMax(year, maxDate)) ||
      (minDate && isYearBeforeMin(year, minDate))
    ) {
      return;
    }

    setViewDate((current) =>
      setJalaliViewMonthYear(current, year, current.month(), dateBounds),
    );
    setPickerMode("calendar");
  };

  const goToPreviousMonth = () => {
    if (minDate && isViewAtMinMonth(viewDate, minDate)) {
      return;
    }

    setViewDate((current) =>
      clampJalaliViewDate(current.subtract(1, "month"), dateBounds),
    );
  };

  const goToNextMonth = () => {
    if (maxDate && isViewAtMaxMonth(viewDate, maxDate)) {
      return;
    }

    setViewDate((current) =>
      clampJalaliViewDate(current.add(1, "month"), dateBounds),
    );
  };

  const calendarDays = buildCalendarDays(viewDate);
  const jalaliViewDate = asJalaliViewDate(viewDate);
  const monthName = jalaliDatePickerDict.months[jalaliViewDate.month()];
  const yearLabel = toPersianDigits(jalaliViewDate.year());
  const displayValue = formatJalaliDisplay(value);
  const isNextMonthDisabled = maxDate ? isViewAtMaxMonth(viewDate, maxDate) : false;
  const isPreviousMonthDisabled = minDate ? isViewAtMinMonth(viewDate, minDate) : false;

  const pickerCellClassName =
    "inline-flex items-center justify-center rounded-xl text-sm transition-colors hover:bg-accent hover:text-accent-foreground";

  const disabledPickerCellClassName =
    "cursor-not-allowed opacity-40 hover:bg-transparent hover:text-muted-foreground";

  const activePickerCellClassName =
    "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground";

  return (
    <>
      <div ref={containerRef} className={`relative ${className}`}>
        <button
          id={inputId}
          type="button"
          disabled={disabled}
          aria-haspopup="dialog"
          aria-expanded={open}
          onClick={togglePicker}
          className="flex h-12 w-full items-center justify-between gap-3 rounded-2xl bg-input px-4 text-sm shadow-soft-sm ring-1 ring-border outline-none transition-shadow focus:ring-2 focus:ring-primary disabled:cursor-not-allowed disabled:opacity-60"
        >
          <span className={displayValue ? "text-foreground" : "text-muted-foreground"}>
            {displayValue || placeholder}
          </span>
          <svg
            aria-hidden="true"
            className="size-4 shrink-0 text-muted-foreground"
            fill="none"
            viewBox="0 0 24 24"
            stroke="currentColor"
            strokeWidth={2}
          >
            <rect x="3" y="4" width="18" height="18" rx="3" />
            <path d="M16 2v4M8 2v4M3 10h18" />
          </svg>
        </button>
      </div>

      {open ? (
        <div
          ref={panelRef}
          role="dialog"
          aria-label={jalaliDatePickerDict.placeholder}
          style={panelStyle}
          className="fixed z-[105] overflow-hidden rounded-2xl border border-border bg-popover p-3 shadow-soft"
        >
          <div className="mb-3 flex items-center justify-between gap-2">
            {pickerMode === "calendar" ? (
              <button
                type="button"
                aria-label={jalaliDatePickerDict.prevMonth}
                disabled={isPreviousMonthDisabled}
                onClick={goToPreviousMonth}
                className="inline-flex size-9 shrink-0 items-center justify-center rounded-xl transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent"
              >
                <svg
                  aria-hidden="true"
                  className="size-4"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth={2}
                >
                  <path d="m9 18 6-6-6-6" />
                </svg>
              </button>
            ) : (
              <span className="size-9 shrink-0" />
            )}

            <div className="flex min-w-0 flex-1 items-center justify-center gap-2">
              <button
                type="button"
                aria-label={jalaliDatePickerDict.selectMonth}
                aria-pressed={pickerMode === "month"}
                onClick={() =>
                  setPickerMode((current) =>
                    current === "month" ? "calendar" : "month",
                  )
                }
                className={`rounded-xl px-2 py-1 text-sm font-semibold transition-colors ${
                  pickerMode === "month"
                    ? "bg-primary/15 text-primary"
                    : "text-foreground hover:bg-accent"
                }`}
              >
                {monthName}
              </button>

              <button
                type="button"
                aria-label={jalaliDatePickerDict.selectYear}
                aria-pressed={pickerMode === "year"}
                onClick={() =>
                  setPickerMode((current) =>
                    current === "year" ? "calendar" : "year",
                  )
                }
                className={`rounded-xl px-2 py-1 text-sm font-semibold transition-colors ${
                  pickerMode === "year"
                    ? "bg-primary/15 text-primary"
                    : "text-foreground hover:bg-accent"
                }`}
              >
                {yearLabel}
              </button>
            </div>

            {pickerMode === "calendar" ? (
              <button
                type="button"
                aria-label={jalaliDatePickerDict.nextMonth}
                disabled={isNextMonthDisabled}
                onClick={goToNextMonth}
                className="inline-flex size-9 shrink-0 items-center justify-center rounded-xl transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent"
              >
                <svg
                  aria-hidden="true"
                  className="size-4"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth={2}
                >
                  <path d="m15 18-6-6 6-6" />
                </svg>
              </button>
            ) : (
              <span className="size-9 shrink-0" />
            )}
          </div>

          {pickerMode === "month" ? (
            <div className="grid grid-cols-3 gap-2">
              {jalaliDatePickerDict.months.map((label, monthIndex) => {
                const selected = jalaliViewDate.month() === monthIndex;
                const isDisabled =
                  (maxDate !== undefined &&
                    isMonthAfterMax(jalaliViewDate.year(), monthIndex, maxDate)) ||
                  (minDate !== undefined &&
                    isMonthBeforeMin(jalaliViewDate.year(), monthIndex, minDate));

                return (
                  <button
                    key={label}
                    type="button"
                    disabled={isDisabled}
                    onClick={() => selectMonth(monthIndex)}
                    className={`${pickerCellClassName} h-10 px-2 ${
                      selected ? activePickerCellClassName : ""
                    } ${isDisabled ? disabledPickerCellClassName : ""}`}
                  >
                    {label}
                  </button>
                );
              })}
            </div>
          ) : null}

          {pickerMode === "year" ? (
            <div className="grid max-h-56 grid-cols-4 gap-2 overflow-y-auto pe-1">
              {yearOptions.map((year) => {
                const selected = jalaliViewDate.year() === year;
                const isDisabled =
                  (maxDate !== undefined && isYearAfterMax(year, maxDate)) ||
                  (minDate !== undefined && isYearBeforeMin(year, minDate));

                return (
                  <button
                    key={year}
                    ref={selected ? selectedYearRef : undefined}
                    type="button"
                    disabled={isDisabled}
                    onClick={() => selectYear(year)}
                    className={`${pickerCellClassName} h-10 ${
                      selected ? activePickerCellClassName : ""
                    } ${isDisabled ? disabledPickerCellClassName : ""}`}
                  >
                    {toPersianDigits(year)}
                  </button>
                );
              })}
            </div>
          ) : null}

          {pickerMode === "calendar" ? (
            <>
              <div className="mb-2 grid grid-cols-7 gap-1">
                {jalaliDatePickerDict.weekdays.map((weekday) => (
                  <span
                    key={weekday}
                    className="py-1 text-center text-xs font-medium text-muted-foreground"
                  >
                    {weekday}
                  </span>
                ))}
              </div>

              <div className="grid grid-cols-7 gap-1">
                {calendarDays.map((cell, index) => {
                  if (cell.type === "empty") {
                    return <span key={`empty-${index}`} className="size-9" />;
                  }

                  const selected = isSameGregorianDay(value, viewDate, cell.day);
                  const today = isTodayJalali(viewDate, cell.day);
                  const isDisabled = isDayDisabled(viewDate, cell.day, dateBounds);

                  return (
                    <button
                      key={cell.day}
                      type="button"
                      disabled={isDisabled}
                      onClick={() => selectDay(cell.day)}
                      className={`inline-flex size-9 items-center justify-center rounded-xl text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent ${
                        isDisabled
                          ? "text-muted-foreground"
                          : selected
                            ? "bg-primary text-primary-foreground"
                            : today
                              ? "bg-accent text-accent-foreground"
                              : "hover:bg-accent hover:text-accent-foreground"
                      }`}
                    >
                      {cell.day.toLocaleString("fa-IR")}
                    </button>
                  );
                })}
              </div>
            </>
          ) : null}

          {clearable && value ? (
            <button
              type="button"
              onClick={() => {
                onChange("");
                setOpen(false);
              }}
              className="mt-3 w-full rounded-xl py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
            >
              {jalaliDatePickerDict.clear}
            </button>
          ) : null}
        </div>
      ) : null}
    </>
  );
}

export type { JalaliDatePickerProps };
