import { router } from '@inertiajs/react';
import type { LucideIcon } from 'lucide-react';
import {
    BriefcaseBusiness,
    CalendarDays,
    CalendarClock,
    ChevronDown,
    Clock,
    MapPin,
    Minus,
    Plus,
} from 'lucide-react';
import type { ReactNode } from 'react';
import { useState } from 'react';
import PlaceAutocomplete from '@/components/booking/place-autocomplete';
import InputError from '@/components/input-error';
import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { search } from '@/routes/booking';
import type { PlaceValue, RoutePrefill, ServiceType } from '@/types/booking';
import { emptyPlace } from '@/types/booking';
import { bookingTabs } from './data';

type Errors = Partial<Record<string, string>>;

const fieldInputClass =
    'block w-full truncate bg-transparent text-xs text-gray-600 outline-none placeholder:text-gray-400';

function FieldShell({
    icon: Icon,
    label,
    htmlFor,
    error,
    children,
}: {
    icon: LucideIcon;
    label: string;
    htmlFor?: string;
    error?: string;
    children: ReactNode;
}) {
    return (
        <div>
            <div
                className={cn(
                    'flex items-center gap-3 rounded-lg bg-mist px-4 py-3.5 transition-colors focus-within:ring-2 focus-within:ring-gold/40',
                    error && 'ring-2 ring-red-300',
                )}
            >
                <Icon className="size-4 shrink-0 text-navy" />
                <div className="min-w-0 flex-1">
                    <label
                        htmlFor={htmlFor}
                        className="block text-sm font-semibold text-navy"
                    >
                        {label}
                    </label>
                    {children}
                </div>
            </div>
            <InputError message={error} className="mt-1 text-xs" />
        </div>
    );
}

function CounterRow({
    label,
    hint,
    value,
    min,
    max,
    onChange,
}: {
    label: string;
    hint: string;
    value: number;
    min: number;
    max: number;
    onChange: (value: number) => void;
}) {
    return (
        <div className="flex items-center justify-between gap-4 py-3">
            <div>
                <p className="text-sm font-semibold text-navy">{label}</p>
                <p className="text-xs text-gray-500">{hint}</p>
            </div>
            <div className="flex items-center gap-3">
                <button
                    type="button"
                    aria-label={`Fewer ${label.toLowerCase()}`}
                    disabled={value <= min}
                    onClick={() => onChange(value - 1)}
                    className="flex size-8 items-center justify-center rounded-full border border-navy/15 text-navy transition-colors enabled:hover:border-gold disabled:opacity-30"
                >
                    <Minus className="size-3.5" />
                </button>
                <span className="w-6 text-center text-sm font-semibold text-navy">
                    {value}
                </span>
                <button
                    type="button"
                    aria-label={`More ${label.toLowerCase()}`}
                    disabled={value >= max}
                    onClick={() => onChange(value + 1)}
                    className="flex size-8 items-center justify-center rounded-full border border-navy/15 text-navy transition-colors enabled:hover:border-gold disabled:opacity-30"
                >
                    <Plus className="size-3.5" />
                </button>
            </div>
        </div>
    );
}

function RidersField({
    passengers,
    bags,
    onPassengersChange,
    onBagsChange,
    error,
}: {
    passengers: number;
    bags: number;
    onPassengersChange: (value: number) => void;
    onBagsChange: (value: number) => void;
    error?: string;
}) {
    const [open, setOpen] = useState(false);
    const summary = `${passengers} ${passengers === 1 ? 'Guest' : 'Guests'}, ${bags} ${bags === 1 ? 'Bag' : 'Bags'}`;

    return (
        <div
            className="relative"
            onBlur={(event) => {
                if (!event.currentTarget.contains(event.relatedTarget)) {
                    setOpen(false);
                }
            }}
            onKeyDown={(event) => event.key === 'Escape' && setOpen(false)}
        >
            <button
                type="button"
                aria-expanded={open}
                aria-haspopup="dialog"
                onClick={() => setOpen(!open)}
                className={cn(
                    'flex w-full items-center gap-3 rounded-lg bg-mist px-4 py-3.5 text-left transition-colors focus-visible:ring-2 focus-visible:ring-gold/40 focus-visible:outline-none',
                    error && 'ring-2 ring-red-300',
                )}
            >
                <BriefcaseBusiness className="size-4 shrink-0 text-navy" />
                <span className="min-w-0 flex-1">
                    <span className="block text-sm font-semibold text-navy">
                        Riders &amp; Bags
                    </span>
                    <span className="block truncate text-xs text-gray-600">
                        {summary}
                    </span>
                </span>
                <ChevronDown
                    className={cn(
                        'size-4 shrink-0 text-gray-500 transition-transform',
                        open && 'rotate-180',
                    )}
                />
            </button>

            {open && (
                <div
                    role="dialog"
                    aria-label="Riders and bags"
                    className="absolute top-full right-0 left-0 z-40 mt-2 min-w-64 rounded-xl border border-navy/10 bg-white px-4 py-2 shadow-[0_16px_40px_-12px_rgba(11,31,51,0.35)] sm:left-auto"
                >
                    <CounterRow
                        label="Guests"
                        hint="Passengers travelling"
                        value={passengers}
                        min={1}
                        max={16}
                        onChange={onPassengersChange}
                    />
                    <div className="h-px bg-navy/10" />
                    <CounterRow
                        label="Bags"
                        hint="Suitcases and large bags"
                        value={bags}
                        min={0}
                        max={20}
                        onChange={onBagsChange}
                    />
                    <button
                        type="button"
                        onClick={() => setOpen(false)}
                        className="mt-1 mb-2 w-full rounded-lg bg-navy py-2 text-sm font-medium text-white transition-colors hover:bg-navy-light"
                    >
                        Done
                    </button>
                </div>
            )}

            <InputError message={error} className="mt-1 text-xs" />
        </div>
    );
}

function minimumPickup(): string {
    const date = new Date(Date.now() + 60 * 60 * 1000);
    date.setMinutes(date.getMinutes() - date.getTimezoneOffset());

    return date.toISOString().slice(0, 16);
}

function minimumDate(): string {
    const date = new Date();
    date.setMinutes(date.getMinutes() - date.getTimezoneOffset());

    return date.toISOString().slice(0, 10);
}

export default function BookingCard({
    prefill,
    autoDetectLocation,
}: {
    prefill: RoutePrefill | null;
    autoDetectLocation: boolean;
}) {
    const [serviceType, setServiceType] = useState<ServiceType>('airport');
    const [pickup, setPickup] = useState<PlaceValue>(
        {
            ...emptyPlace(prefill?.origin),
            lat: prefill?.originLat ?? null,
            lng: prefill?.originLng ?? null,
        },
    );
    const [destination, setDestination] = useState<PlaceValue>(
        {
            ...emptyPlace(prefill?.destination),
            lat: prefill?.destinationLat ?? null,
            lng: prefill?.destinationLng ?? null,
        },
    );
    const [routeId, setRouteId] = useState(prefill?.routeId ?? null);
    const [pickupAt, setPickupAt] = useState('');
    const [departureDate, setDepartureDate] = useState('');
    const [returnDate, setReturnDate] = useState('');
    const [hours, setHours] = useState(4);
    const [passengers, setPassengers] = useState(2);
    const [bags, setBags] = useState(2);
    const [errors, setErrors] = useState<Errors>({});
    const [processing, setProcessing] = useState(false);

    const hourly = serviceType === 'hourly';
    const holiday = serviceType === 'holidays';

    const submit = () => {
        router.post(
            search.url(),
            {
                service_type: serviceType,
                pickup,
                destination: hourly ? null : destination,
                hours: hourly ? hours : null,
                pickup_at: holiday ? null : pickupAt || null,
                pickup_date: holiday ? departureDate || null : null,
                return_date: holiday ? returnDate || null : null,
                passengers,
                bags,
                route_id: hourly ? null : routeId,
            },
            {
                onStart: () => setProcessing(true),
                onFinish: () => setProcessing(false),
                onError: (validationErrors) => setErrors(validationErrors),
                onSuccess: () => setErrors({}),
            },
        );
    };

    return (
        <div id="book" className="relative z-10 scroll-mt-28">
            <div
                role="tablist"
                className="flex [scrollbar-width:none] overflow-x-auto rounded-t-xl bg-white/95 sm:w-fit"
            >
                {bookingTabs.map(({ id, label, icon: Icon }) => (
                    <button
                        key={id}
                        type="button"
                        role="tab"
                        aria-selected={serviceType === id}
                        onClick={() => setServiceType(id)}
                        className={cn(
                            'flex shrink-0 items-center gap-2 px-5 py-4 text-xs font-medium whitespace-nowrap transition-colors first:rounded-tl-xl last:rounded-tr-xl sm:px-6',
                            serviceType === id
                                ? 'bg-navy text-white'
                                : 'text-navy hover:bg-mist',
                        )}
                    >
                        <Icon className="size-4" />
                        {label}
                    </button>
                ))}
            </div>

            <form
                onSubmit={(event) => {
                    event.preventDefault();
                    submit();
                }}
                className="rounded-b-xl bg-white p-5 shadow-[0_20px_50px_-20px_rgba(11,31,51,0.25)] sm:rounded-tr-xl sm:p-8"
            >
                <h2 className="font-google text-2xl font-semibold text-navy">
                    Find your next Trip
                </h2>

                <div className="mt-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-4 lg:gap-5">
                    <FieldShell
                        icon={MapPin}
                        label="Pickup Location"
                        htmlFor="pickup"
                        error={errors['pickup.address']}
                    >
                        <PlaceAutocomplete
                            id="pickup"
                            value={pickup}
                            onChange={(value) => {
                                setPickup(value);
                                setRouteId(null);
                            }}
                            placeholder="Airport, hotel or address"
                            inputClassName={fieldInputClass}
                            currentLocation
                            autoLocate={autoDetectLocation && !prefill}
                        />
                    </FieldShell>

                    {hourly ? (
                        <FieldShell
                            icon={Clock}
                            label="Duration"
                            htmlFor="hours"
                            error={errors.hours}
                        >
                            <select
                                id="hours"
                                value={hours}
                                onChange={(event) =>
                                    setHours(Number(event.target.value))
                                }
                                className={cn(
                                    fieldInputClass,
                                    'cursor-pointer appearance-none',
                                )}
                            >
                                {Array.from(
                                    { length: 11 },
                                    (_, i) => i + 2,
                                ).map((value) => (
                                    <option key={value} value={value}>
                                        {value} hours
                                    </option>
                                ))}
                            </select>
                        </FieldShell>
                    ) : (
                        <FieldShell
                            icon={MapPin}
                            label="Destination"
                            htmlFor="destination"
                            error={errors['destination.address']}
                        >
                            <PlaceAutocomplete
                                id="destination"
                                value={destination}
                                onChange={(value) => {
                                    setDestination(value);
                                    setRouteId(null);
                                }}
                                placeholder="Where are you going?"
                                inputClassName={fieldInputClass}
                            />
                        </FieldShell>
                    )}

                    {holiday ? (
                        <>
                            <FieldShell
                                icon={CalendarDays}
                                label="Departure Date"
                                htmlFor="departure_date"
                                error={errors.pickup_date}
                            >
                                <input
                                    id="departure_date"
                                    type="date"
                                    value={departureDate}
                                    min={minimumDate()}
                                    onChange={(event) => {
                                        setDepartureDate(event.target.value);
                                        if (
                                            returnDate &&
                                            event.target.value > returnDate
                                        ) {
                                            setReturnDate('');
                                        }
                                    }}
                                    className={fieldInputClass}
                                />
                            </FieldShell>
                            <FieldShell
                                icon={CalendarDays}
                                label="Return Date"
                                htmlFor="return_date"
                                error={errors.return_date}
                            >
                                <input
                                    id="return_date"
                                    type="date"
                                    value={returnDate}
                                    min={departureDate || minimumDate()}
                                    onChange={(event) =>
                                        setReturnDate(event.target.value)
                                    }
                                    className={fieldInputClass}
                                />
                            </FieldShell>
                        </>
                    ) : (
                        <FieldShell
                            icon={CalendarClock}
                            label="Date & Time"
                            htmlFor="pickup_at"
                            error={errors.pickup_at}
                        >
                            <input
                                id="pickup_at"
                                type="datetime-local"
                                value={pickupAt}
                                min={minimumPickup()}
                                onChange={(event) =>
                                    setPickupAt(event.target.value)
                                }
                                className={fieldInputClass}
                            />
                        </FieldShell>
                    )}

                    <RidersField
                        passengers={passengers}
                        bags={bags}
                        onPassengersChange={setPassengers}
                        onBagsChange={setBags}
                        error={errors.passengers ?? errors.bags}
                    />
                </div>

                <button
                    type="submit"
                    disabled={processing}
                    className="mt-6 flex items-center gap-2 rounded-md bg-gold px-6 py-3 text-sm font-medium text-white transition-colors hover:bg-gold-dark disabled:opacity-70"
                >
                    {processing && <Spinner />}
                    Search Trips
                </button>
            </form>
        </div>
    );
}
