import { format, isSameDay, startOfDay } from 'date-fns';
import { CalendarIcon, ChevronDownIcon } from 'lucide-react';
import { useMemo, useState } from 'react';
import { Button } from '@/components/ui/button';
import { Calendar } from '@/components/ui/calendar';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
    Popover,
    PopoverContent,
    PopoverTrigger,
} from '@/components/ui/popover';
import { cn } from '@/lib/utils';

type DateTimePickerProps = {
    id?: string;
    name?: string;
    value?: Date;
    onChange?: (value: Date | undefined) => void;
    disabled?: boolean;
    minDate?: Date;
    dateLabel?: string;
    timeLabel?: string;
    className?: string;
};

function toTimeValue(date: Date): string {
    return format(date, 'HH:mm');
}

function combineDateAndTime(date: Date, time: string): Date | undefined {
    const [hours, minutes] = time.split(':');

    if (hours === undefined || minutes === undefined) {
        return undefined;
    }

    const combined = new Date(date);
    combined.setHours(Number.parseInt(hours, 10), Number.parseInt(minutes, 10), 0, 0);

    return combined;
}

export function DateTimePicker({
    id,
    name,
    value,
    onChange,
    disabled = false,
    minDate,
    dateLabel = 'Date',
    timeLabel = 'Time',
    className,
}: DateTimePickerProps) {
    const [open, setOpen] = useState(false);
    const minimumDate = useMemo(() => minDate ?? new Date(), [minDate]);
    const selectedDate = value;
    const time = value ? toTimeValue(value) : '09:00';

    const minimumTime = useMemo(() => {
        if (!selectedDate || !isSameDay(selectedDate, minimumDate)) {
            return undefined;
        }

        return format(minimumDate, 'HH:mm');
    }, [minimumDate, selectedDate]);

    const handleDateSelect = (date: Date | undefined) => {
        setOpen(false);

        if (!date) {
            onChange?.(undefined);

            return;
        }

        onChange?.(combineDateAndTime(date, time));
    };

    const handleTimeChange = (nextTime: string) => {
        if (!selectedDate) {
            return;
        }

        onChange?.(combineDateAndTime(selectedDate, nextTime));
    };

    return (
        <div className={cn('flex flex-col gap-4 sm:flex-row', className)}>
            {name ? (
                <input
                    type="hidden"
                    name={name}
                    value={value?.toISOString() ?? ''}
                    readOnly
                />
            ) : null}

            <div className="flex min-w-0 flex-1 flex-col gap-2">
                <Label htmlFor={id ? `${id}-date` : undefined} className="px-1">
                    {dateLabel}
                </Label>
                <Popover open={open} onOpenChange={setOpen}>
                    <PopoverTrigger asChild>
                        <Button
                            type="button"
                            variant="outline"
                            id={id ? `${id}-date` : id}
                            disabled={disabled}
                            data-empty={!selectedDate}
                            className="w-full justify-between font-normal data-[empty=true]:text-muted-foreground"
                        >
                            <span className="flex items-center gap-2">
                                <CalendarIcon className="size-4" />
                                {selectedDate
                                    ? format(selectedDate, 'PPP')
                                    : 'Select date'}
                            </span>
                            <ChevronDownIcon className="size-4 opacity-50" />
                        </Button>
                    </PopoverTrigger>
                    <PopoverContent
                        className="w-auto overflow-hidden p-0"
                        align="start"
                    >
                        <Calendar
                            mode="single"
                            selected={selectedDate}
                            captionLayout="dropdown"
                            disabled={(date) => date < startOfDay(minimumDate)}
                            onSelect={handleDateSelect}
                        />
                    </PopoverContent>
                </Popover>
            </div>

            <div className="flex min-w-0 flex-1 flex-col gap-2">
                <Label htmlFor={id ? `${id}-time` : undefined} className="px-1">
                    {timeLabel}
                </Label>
                <Input
                    type="time"
                    id={id ? `${id}-time` : undefined}
                    step={60}
                    value={time}
                    min={minimumTime}
                    disabled={disabled || !selectedDate}
                    onChange={(event) => handleTimeChange(event.target.value)}
                    className="appearance-none bg-background [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
                />
            </div>
        </div>
    );
}

export function parseDateTimeValue(value: string | null | undefined): Date | undefined {
    if (!value) {
        return undefined;
    }

    const parsed = new Date(value);

    return Number.isNaN(parsed.valueOf()) ? undefined : parsed;
}
