import React, { useCallback, useState, useRef, useEffect } from "react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Input } from "@/components/ui/input";
import { Calendar } from "@/components/ui/calendar";
import { cn } from "@/lib/utils/cn";
import { formatDate } from "@/lib/utils/format";
import { Clock3Icon } from "lucide-react";

const pad = (value: number) => value.toString().padStart(2, "0");
const HOURS = Array.from({ length: 24 }, (_, hour) => hour);
const MINUTES = Array.from({ length: 60 }, (_, minute) => minute);

const toLocalTimeValue = (value: Date) => {
    return `${pad(value.getHours())}:${pad(value.getMinutes())}`;
};

const mergeDateAndTime = (date: Date, time: string) => {
    const [hours, minutes] = time.split(":").map((v) => Number(v));
    const nextDate = new Date(date);
    nextDate.setHours(Number.isFinite(hours) ? hours : 0, Number.isFinite(minutes) ? minutes : 0, 0, 0);
    return nextDate;
};

export type DateTimePickerProps = {
    placeholder?: string;
    value?: Date | null;
    onChange?: (value?: Date) => void;
};

type TimePickerPopoverProps = {
    hours: number;
    minutes: number;
    displayValue: string;
    onHoursChange: (value: number) => void;
    onMinutesChange: (value: number) => void;
};

const TimePickerPopover: React.FC<TimePickerPopoverProps> = ({
    hours,
    minutes,
    displayValue,
    onHoursChange,
    onMinutesChange
}) => {
    const [open, setOpen] = useState(false);
    const hoursRef = useRef<HTMLDivElement>(null);
    const minutesRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        if(!open) {
            return;
        }

        const hourItem = hoursRef.current?.querySelector<HTMLButtonElement>(`[data-time-value="${hours}"]`);
        const minuteItem = minutesRef.current?.querySelector<HTMLButtonElement>(`[data-time-value="${minutes}"]`);

        hourItem?.scrollIntoView({ block: "center" });
        minuteItem?.scrollIntoView({ block: "center" });
    }, [open, hours, minutes]);

    return (
        <Popover open={open} onOpenChange={setOpen}>
            <PopoverTrigger asChild>
                <button type="button" className="relative w-full">
                    <Input
                      className="cursor-pointer pr-8"
                      readOnly
                      value={displayValue} />
                    <Clock3Icon className="text-muted-foreground pointer-events-none absolute top-1/2 left-16 size-4 -translate-y-1/2" />
                </button>
            </PopoverTrigger>
            <PopoverContent className="w-fit rounded-md p-2 shadow-md" align="end">
                <div className="grid grid-cols-2 gap-2">
                    <div
                      ref={hoursRef}
                      className="h-44 min-w-14 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
                        {HOURS.map((value) => (
                            <button
                              key={value}
                              type="button"
                              data-time-value={value}
                              className={cn(
                                  "flex h-8 w-full items-center justify-center rounded-sm px-2 text-sm transition-colors",
                                  value === hours
                                      ? "bg-accent text-accent-foreground font-medium"
                                      : "hover:bg-accent/60"
                              )}
                              onClick={() => onHoursChange(value)}>
                                {pad(value)}
                            </button>
                        ))}
                    </div>
                    <div
                      ref={minutesRef}
                      className="h-44 min-w-14 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
                        {MINUTES.map((value) => (
                            <button
                              key={value}
                              type="button"
                              data-time-value={value}
                              className={cn(
                                  "flex h-8 w-full items-center justify-center rounded-sm px-2 text-sm transition-colors",
                                  value === minutes
                                      ? "bg-accent text-accent-foreground font-medium"
                                      : "hover:bg-accent/60"
                              )}
                              onClick={() => onMinutesChange(value)}>
                                {pad(value)}
                            </button>
                        ))}
                    </div>
                </div>
            </PopoverContent>
        </Popover>
    );
};

export const DateTimePicker: React.FC<DateTimePickerProps> = (props) => {
    const {
        placeholder,
        value,
        onChange
    } = props;

    const [open, setOpen] = React.useState(false);

    const handleSelectDate = useCallback((date?: Date) => {
        if(!onChange) {
            return;
        }

        if(!date) {
            onChange(undefined);
            return;
        }

        const nextDate = value ? mergeDateAndTime(date, toLocalTimeValue(value)) : date;
        onChange(nextDate);
    }, [onChange, value]);

    const handleTimeChange = useCallback((time: string) => {
        if(!onChange) {
            return;
        }

        const sourceDate = value ?? new Date();
        onChange(mergeDateAndTime(sourceDate, time));
    }, [onChange, value]);

    const selectedHours = value?.getHours() ?? 0;
    const selectedMinutes = value?.getMinutes() ?? 0;

    const handleHoursChange = useCallback((nextHour: number) => {
        handleTimeChange(`${pad(nextHour)}:${pad(selectedMinutes)}`);
    }, [handleTimeChange, selectedMinutes]);

    const handleMinutesChange = useCallback((nextMinute: number) => {
        handleTimeChange(`${pad(selectedHours)}:${pad(nextMinute)}`);
    }, [handleTimeChange, selectedHours]);

    return (
        <Popover open={open} onOpenChange={setOpen}>
            <PopoverTrigger asChild>
                <div>
                    <Input
                      readOnly
                      placeholder={placeholder}
                      value={value ? formatDate(value, { hour: "2-digit", minute: "2-digit" }) : ""} />
                </div>
            </PopoverTrigger>

            <PopoverContent className="w-auto p-0" align="start">
                <Calendar
                  className="p-4"
                  mode="single"
                  selected={value ?? undefined}
                  defaultMonth={value ?? undefined}
                  onSelect={handleSelectDate} />
                <div className="border-t px-4 py-3">
                    <TimePickerPopover
                      hours={selectedHours}
                      minutes={selectedMinutes}
                      displayValue={value ? toLocalTimeValue(value) : "00:00"}
                      onHoursChange={handleHoursChange}
                      onMinutesChange={handleMinutesChange} />
                </div>
            </PopoverContent>
        </Popover>
    );
};
