"use client";
import { CalendarIcon } from "@/assets/icons";
import { cn } from "@/lib/utils";
import { useMemo } from "react";
import {
  Control,
  Controller,
  FieldValues,
  Path,
  RegisterOptions,
} from "react-hook-form";
import MultiDatePicker, { DateObject } from "react-multi-date-picker";
import TimePicker from "react-multi-date-picker/plugins/time_picker";

interface IProps<T extends FieldValues> {
  control: Control<T>;
  name: Path<T>;
  label: string;
  placeholder?: string;
  required?: boolean;
  multiple?: boolean;
  range?: boolean;
  onlyMonthPicker?: boolean;
  onlyYearPicker?: boolean;
  minDate?: number;
  maxDate?: number;
  timePicker?: boolean;
  clearable?: boolean;
  /** Renders the calendar in a portal (recommended inside modals / overflow containers). */
  portal?: boolean;
  rules?: Omit<
    RegisterOptions<T, Path<T>>,
    "valueAsNumber" | "valueAsDate" | "setValueAs" | "disabled"
  >;
}

const toUnixSeconds = (v: unknown): number | null => {
  if (v === "" || v === undefined || v === null) return null;
  const n = typeof v === "number" ? v : Number(v);
  return Number.isFinite(n) ? n : null;
};

const convertFormValueToPickerValue = (
  value: unknown,
  range: boolean,
  multiple: boolean,
): DateObject | DateObject[] | undefined => {
  if (value === "" || value === undefined || value === null) {
    return undefined;
  }

  if (multiple && Array.isArray(value)) {
    const secs = value
      .map(toUnixSeconds)
      .filter((s): s is number => s !== null);
    if (!secs.length) return undefined;
    return secs.map((unix) => new DateObject(new Date(unix * 1000)));
  }

  if (range && Array.isArray(value)) {
    const secs = value
      .map(toUnixSeconds)
      .filter((s): s is number => s !== null);
    if (!secs.length) return undefined;
    return secs.map((unix) => new DateObject(new Date(unix * 1000)));
  }

  if (range && !Array.isArray(value)) {
    return undefined;
  }

  const unix = toUnixSeconds(value);
  if (unix === null) return undefined;
  return new DateObject(new Date(unix * 1000));
};

type PickerInputProps<T extends FieldValues> = {
  name: Path<T>;
  value: unknown;
  onChange: (...event: unknown[]) => void;
  error?: { message?: string };
  range: boolean;
  multiple: boolean;
  onlyMonthPicker: boolean;
  onlyYearPicker: boolean;
  minDate?: number;
  maxDate?: number;
  timePicker: boolean;
  clearable: boolean;
  portal: boolean;
  placeholder?: string;
};

const DatePickerRHFInput = <T extends FieldValues>({
  name,
  value,
  onChange,
  error,
  range,
  multiple,
  onlyMonthPicker,
  onlyYearPicker,
  minDate,
  maxDate,
  timePicker,
  clearable,
  portal,
  placeholder,
}: PickerInputProps<T>) => {
  const valueFingerprint = useMemo(
    () =>
      JSON.stringify({
        value,
        range,
        multiple,
      }),
    [value, range, multiple],
  );

  const pickerValue = useMemo(
    () => convertFormValueToPickerValue(value, range, multiple),
    [valueFingerprint, range, multiple],
  );

  const hasValue =
    pickerValue !== undefined &&
    (Array.isArray(pickerValue) ? pickerValue.length > 0 : true);

  return (
    <>
      <div className="relative">
        <MultiDatePicker
          range={range}
          minDate={minDate}
          maxDate={maxDate}
          onlyMonthPicker={onlyMonthPicker}
          plugins={
            timePicker ? [<TimePicker key={String(name)} position="bottom" />] : []
          }
          onlyYearPicker={onlyYearPicker}
          multiple={multiple}
          {...(portal ? { portal: true, zIndex: 99999 } : {})}
          value={pickerValue}
          onChange={(date: DateObject | DateObject[] | null) => {
            if (!date) {
              onChange("");
              return;
            }

            if (Array.isArray(date)) {
              const unixTimestamps = date.map((d) => d.toUnix());
              onChange(unixTimestamps);
            } else if (date instanceof DateObject) {
              onChange(date.isValid ? date.toUnix() : "");
            }
          }}
          format={`YYYY/MM/DD ${timePicker ? "HH:mm" : ""}`}
          containerClassName="w-full"
          inputClass={cn(
            "w-full rounded-xl border border-stroke/70 bg-white/90 px-5.5 py-3 pl-12.5 text-dark shadow-[0_1px_2px_rgba(16,24,40,0.04),0_8px_20px_rgba(16,24,40,0.04)] outline-none backdrop-blur-[1px] transition-all duration-200 focus:border-primary/70 focus:bg-white focus:shadow-[0_0_0_2px_rgba(59,130,246,0.14),0_10px_24px_rgba(59,130,246,0.08)] disabled:cursor-default disabled:bg-gray-2/70 dark:border-dark-3/80 dark:bg-dark-2/85 dark:text-white dark:focus:border-primary/80 dark:focus:shadow-[0_0_0_2px_rgba(59,130,246,0.2),0_10px_24px_rgba(15,23,42,0.45)] dark:disabled:bg-dark",
            clearable && hasValue && "pr-11",
            error &&
              "border-red/80 focus:border-red focus:shadow-[0_0_0_2px_rgba(239,68,68,0.16),0_10px_24px_rgba(239,68,68,0.1)] dark:border-red/80 dark:focus:shadow-[0_0_0_2px_rgba(239,68,68,0.24),0_10px_24px_rgba(127,29,29,0.45)]",
          )}
          placeholder={placeholder}
        />
        <span className="pointer-events-none absolute left-4.5 top-1/2 -translate-y-1/2 text-dark-5 transition-colors dark:text-dark-6">
          <CalendarIcon />
        </span>
        {clearable && hasValue && (
          <button
            type="button"
            aria-label="Clear date"
            className="absolute right-3 top-1/2 z-10 -translate-y-1/2 rounded-md p-1 text-dark-5 transition hover:bg-gray-2 hover:text-dark dark:text-dark-6 dark:hover:bg-dark-3 dark:hover:text-white"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              onChange("");
            }}
          >
            <svg
              width="16"
              height="16"
              viewBox="0 0 16 16"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
              aria-hidden
            >
              <path
                d="M12 4L4 12M4 4L12 12"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </button>
        )}
      </div>

      {error && (
        <p className="mt-1.5 text-sm font-medium text-error">
          {error.message as string}
        </p>
      )}
    </>
  );
};

const DatePicker = <T extends FieldValues>({
  control,
  name,
  label,
  multiple = false,
  placeholder,
  required,
  range = false,
  timePicker = false,
  onlyMonthPicker = false,
  onlyYearPicker = false,
  rules,
  maxDate,
  minDate,
  clearable = false,
  portal = false,
}: IProps<T>) => {
  return (
    <div className="space-y-3">
      <label
        htmlFor={String(name)}
        className="block text-body-sm font-semibold capitalize tracking-[0.01em] text-dark dark:text-white"
      >
        {label}
        {required && <span className="ml-1 select-none text-error">*</span>}
      </label>

      <div>
        <Controller
          control={control}
          name={name}
          rules={rules}
          render={({ field: { onChange, name: fieldName, value }, formState: { errors } }) => (
            <DatePickerRHFInput<T>
              name={fieldName}
              value={value}
              onChange={onChange}
              error={errors[fieldName] as { message?: string } | undefined}
              range={range}
              multiple={multiple}
              onlyMonthPicker={onlyMonthPicker}
              onlyYearPicker={onlyYearPicker}
              minDate={minDate}
              maxDate={maxDate}
              timePicker={timePicker}
              clearable={clearable}
              portal={portal}
              placeholder={placeholder}
            />
          )}
        />
      </div>
    </div>
  );
};

export default DatePicker;
