"use client";

import type { ReactNode } from "react";
import DatePicker from "react-multi-date-picker";
import DateObject from "react-date-object";
import persian from "react-date-object/calendars/persian";
import persian_fa from "react-date-object/locales/persian_fa";
import gregorian from "react-date-object/calendars/gregorian";
import gregorian_en from "react-date-object/locales/gregorian_en";
import TimePicker from "react-multi-date-picker/plugins/time_picker";
import type { MapDaysProps } from "react-multi-date-picker";

type Props = {
  value?: string;
  onChange: (gregorianDate: string) => void;
  onClose?: () => void;
  invalid?: boolean;
  placeholder?: string;
  maxDate?: string;
  sessionDates?: string[];
  recordedDates?: string[];
  /** When true, only days in sessionDates can be picked (requires sessionDates). */
  restrictToSessionDates?: boolean;
  onlyMonthPicker?: boolean;
  withTime?: boolean;
  onMonthChange?: (gregorianMonth: string) => void;
  className?: string;
  /** Custom trigger instead of the default date input field. */
  renderTrigger?: (openCalendar: () => void) => ReactNode;
};

function toGregorianIso(date: DateObject): string {
  return new DateObject(date).convert(gregorian, gregorian_en).format("YYYY-MM-DD");
}

function toPersianValue(gregorianDate?: string, onlyMonth?: boolean, withTime?: boolean) {
  if (!gregorianDate) return undefined;

  if (withTime) {
    const parsed = new Date(gregorianDate);
    if (Number.isNaN(parsed.getTime())) return undefined;
    return new DateObject(parsed).convert(persian, persian_fa);
  }

  const normalized = onlyMonth && gregorianDate.length === 7
    ? `${gregorianDate}-01`
    : gregorianDate.slice(0, 10);

  return new DateObject({
    date: normalized,
    format: "YYYY-MM-DD",
    calendar: gregorian,
    locale: gregorian_en,
  }).convert(persian, persian_fa);
}

function toMaxDate(maxDate?: string) {
  if (!maxDate) return undefined;
  return toPersianValue(maxDate);
}

export function PersianDatePicker({
  value,
  onChange,
  onClose,
  invalid,
  placeholder,
  maxDate,
  sessionDates = [],
  recordedDates = [],
  restrictToSessionDates = false,
  onlyMonthPicker = false,
  withTime = false,
  onMonthChange,
  className,
  renderTrigger,
}: Props) {
  const sessionSet = new Set(sessionDates);
  const recordedSet = new Set(recordedDates);
  const limitToSessions = restrictToSessionDates && sessionSet.size > 0;
  const resolvedPlaceholder = placeholder ?? (withTime ? "انتخاب تاریخ و ساعت" : "انتخاب تاریخ");

  const mapDays = ({ date }: MapDaysProps) => {
    const iso = toGregorianIso(date);
    const isSession = sessionSet.has(iso);
    const isRecorded = recordedSet.has(iso);

    if (limitToSessions && !isSession) {
      return { disabled: true, className: "rmdp-non-session-day" };
    }

    if (isSession && isRecorded) {
      return { className: "rmdp-session-day rmdp-session-recorded" };
    }
    if (isSession) {
      return { className: "rmdp-session-day" };
    }
    if (isRecorded) {
      return { className: "rmdp-recorded-day" };
    }
    return {};
  };

  const handleMonthChange = (date: DateObject) => {
    if (!onMonthChange) return;
    const gregorianMonth = new DateObject(date)
      .convert(gregorian, gregorian_en)
      .format("YYYY-MM");
    onMonthChange(gregorianMonth);
  };

  const inputClass =
    className ??
    `w-full rounded-xl border bg-white px-3.5 py-2.5 text-sm font-medium text-slate-900 outline-none transition placeholder:text-slate-400 focus:ring-2 ring-primary ${
      invalid
        ? "border-red-400 focus:border-red-500"
        : "border-slate-200 focus:border-primary"
    }`;

  const displayFormat = onlyMonthPicker ? "YYYY/MM" : withTime ? "YYYY/MM/DD HH:mm" : "YYYY/MM/DD";

  const handleChange = (date: DateObject | DateObject[] | null) => {
    if (!date) {
      onChange("");
      return;
    }

    const picked = Array.isArray(date) ? date[0] : date;
    if (withTime) {
      onChange(new DateObject(picked).toDate().toISOString());
      return;
    }

    const gregorianDate = new DateObject(picked)
      .convert(gregorian, gregorian_en)
      .format(onlyMonthPicker ? "YYYY-MM" : "YYYY-MM-DD");
    const iso = onlyMonthPicker ? gregorianDate.slice(0, 7) : gregorianDate;

    if (limitToSessions && !onlyMonthPicker && !sessionSet.has(iso)) {
      return;
    }

    onChange(iso);
  };

  return (
    <DatePicker
      portal
      value={toPersianValue(value, onlyMonthPicker, withTime)}
      onChange={handleChange}
      calendar={persian}
      locale={persian_fa}
      onlyMonthPicker={onlyMonthPicker}
      maxDate={toMaxDate(maxDate)}
      mapDays={limitToSessions || sessionSet.size || recordedSet.size ? mapDays : undefined}
      onMonthChange={onMonthChange ? handleMonthChange : undefined}
      onClose={onClose}
      format={displayFormat}
      plugins={withTime ? [<TimePicker key="time" hideSeconds position="bottom" />] : undefined}
      containerClassName={renderTrigger ? "inline-flex" : "w-full"}
      calendarPosition={renderTrigger ? "bottom-right" : undefined}
      zIndex={3000}
      inputClass={inputClass}
      placeholder={resolvedPlaceholder}
      render={
        renderTrigger
          ? ((_value: string, openCalendar: () => void) => renderTrigger(openCalendar))
          : undefined
      }
    />
  );
}
