import { Head, router } from '@inertiajs/react';
import type { LucideIcon } from 'lucide-react';
import {
    ArrowRight,
    ArrowUpDown,
    Building2,
    CalendarDays,
    ChevronDown,
    Clock3,
    Headset,
    History,
    House,
    Plane,
    ShieldCheck,
    TrainFront,
    UserRound,
    Wallet,
} from 'lucide-react';
import type { ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { pad } from '@/components/booking/calendar-utils';
import PageIntro from '@/components/booking/page-intro';
import PlaceAutocomplete from '@/components/booking/place-autocomplete';
import type { MapPoint } from '@/components/booking/route-map';
import RouteMap from '@/components/booking/route-map';
import { cardClass } from '@/components/booking/styles';
import TripSummaryBar from '@/components/booking/trip-summary-bar';
import SiteFooter from '@/components/home/site-footer';
import SiteHeader from '@/components/home/site-header';
import InputError from '@/components/input-error';
import { Spinner } from '@/components/ui/spinner';
import type { NearbyHub } from '@/lib/google-maps';
import { geocodeAddress, hasGoogleMaps, hasPlaces, nearbyHubs } from '@/lib/google-maps';
import { cn } from '@/lib/utils';
import { clear } from '@/routes/booking/recent';
import { update } from '@/routes/booking/location';
import type {
    PlaceValue,
    RecentSearch,
    SavedPlace,
    TravelSettings,
    TripSummary,
} from '@/types/booking';
import { emptyPlace } from '@/types/booking';

type Field = 'pickup' | 'destination';

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

type Props = {
    trip: TripSummary;
    pickup: PlaceValue;
    destination: PlaceValue | null;
    savedPlaces: SavedPlace[];
    recentSearches: RecentSearch[];
    travel: TravelSettings;
};

const sectionLabelClass =
    'text-xs font-semibold tracking-[0.08em] text-navy uppercase dark:text-white';

const perks: { icon: LucideIcon; title: string; text: string; tone: string }[] =
    [
        {
            icon: Wallet,
            title: '100% Inclusive',
            text: 'Fuel surcharges',
            tone: 'bg-orange-50 text-orange-600',
        },
        {
            icon: ShieldCheck,
            title: '60 min Free Wait',
            text: 'Flight delay buffer',
            tone: 'bg-emerald-50 text-emerald-600',
        },
        {
            icon: Headset,
            title: 'Terminal Arrivals Hall',
            text: 'Meet & greet',
            tone: 'bg-indigo-50 text-indigo-600',
        },
    ];

function haversineKm(from: PlaceValue, to: { lat: number; lng: number }) {
    const rad = (deg: number) => (deg * Math.PI) / 180;
    const dLat = rad(to.lat - from.lat!);
    const dLng = rad(to.lng - from.lng!);
    const a =
        Math.sin(dLat / 2) ** 2 +
        Math.cos(rad(from.lat!)) *
            Math.cos(rad(to.lat)) *
            Math.sin(dLng / 2) ** 2;

    return 6371 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}

function hasCoordinates(place: PlaceValue | null): place is PlaceValue {
    return (
        place !== null &&
        typeof place.lat === 'number' &&
        Number.isFinite(place.lat) &&
        typeof place.lng === 'number' &&
        Number.isFinite(place.lng)
    );
}

function withInferredCoordinates(place: PlaceValue): PlaceValue {
    if (
        typeof place.lat === 'number' &&
        Number.isFinite(place.lat) &&
        typeof place.lng === 'number' &&
        Number.isFinite(place.lng)
    ) {
        return place;
    }

    const match = place.address.match(
        /Current location \(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)/i,
    );

    return match
        ? { ...place, lat: Number(match[1]), lng: Number(match[2]) }
        : place;
}

function todayValue(): string {
    const today = new Date();

    return `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())}`;
}

function PlaceRow({
    icon: Icon,
    title,
    subtitle,
    meta,
    onSelect,
    highlighted = false,
}: {
    icon: LucideIcon;
    title: string;
    subtitle?: string;
    meta?: ReactNode;
    onSelect: () => void;
    highlighted?: boolean;
}) {
    return (
        <li>
            <button
                type="button"
                onClick={onSelect}
                className={cn(
                    'flex w-full items-center gap-3.5 rounded-xl px-3 py-3 text-left transition-colors hover:bg-gold/10 dark:hover:bg-white/5',
                    highlighted && 'bg-mist dark:bg-white/5',
                )}
            >
                <span className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-mist text-navy dark:bg-ink dark:text-white">
                    <Icon className="size-4" />
                </span>
                <span className="min-w-0 flex-1">
                    <span className="block truncate text-sm font-semibold">
                        {title}
                    </span>
                    {subtitle && (
                        <span className="block truncate text-xs text-navy/70 dark:text-white/70">
                            {subtitle}
                        </span>
                    )}
                </span>
                {meta && (
                    <span className="shrink-0 text-right text-xs font-medium text-navy/70 dark:text-white/70">
                        {meta}
                    </span>
                )}
            </button>
        </li>
    );
}

function PlaceSection({
    title,
    aside,
    children,
}: {
    title: string;
    aside?: ReactNode;
    children: ReactNode;
}) {
    return (
        <section className="border-b border-navy/10 py-5 first:pt-0 last:border-b-0 dark:border-white/10">
            <div className="mb-2 flex items-center justify-between gap-4 px-3">
                <h2 className={sectionLabelClass}>{title}</h2>
                {aside}
            </div>
            <ul className="space-y-1">{children}</ul>
        </section>
    );
}

export default function BookingLocation({
    trip,
    pickup: initialPickup,
    destination: initialDestination,
    savedPlaces,
    recentSearches,
    travel,
}: Props) {
    const hourly = trip.hours !== null;
    const [pickup, setPickup] = useState(() =>
        withInferredCoordinates(initialPickup),
    );
    const [destination, setDestination] = useState(
        () =>
            withInferredCoordinates(initialDestination ?? emptyPlace()),
    );
    const [active, setActive] = useState<Field>(
        hourly ? 'pickup' : 'destination',
    );
    const [passengers, setPassengers] = useState(trip.passengers);
    const [date, setDate] = useState(trip.pickup_date ?? '');
    const [recent, setRecent] = useState(recentSearches);
    const [hubs, setHubs] = useState<NearbyHub[]>([]);
    const [errors, setErrors] = useState<Errors>({});
    const [processing, setProcessing] = useState(false);

    const origin = hasCoordinates(pickup)
        ? pickup
        : hasCoordinates(destination)
          ? destination
          : null;

    useEffect(() => {
        if (!hasPlaces || origin === null) {
            setHubs([]);

            return;
        }

        let cancelled = false;

        void nearbyHubs(origin.lat!, origin.lng!)
            .then((results) => !cancelled && setHubs(results))
            .catch(() => !cancelled && setHubs([]));

        return () => {
            cancelled = true;
        };
    }, [origin?.lat, origin?.lng]);

    // Routes selected from the home page can contain a name without a
    // place id. Resolve that name so the Google map can still preview it.
    useEffect(() => {
        if (!hasGoogleMaps) {
            return;
        }

        const unresolved = [
            { place: pickup, setPlace: setPickup },
            ...(!hourly
                ? [{ place: destination, setPlace: setDestination }]
                : []),
        ].filter(
            ({ place }) =>
                place.address.trim() !== '' &&
                (place.lat === null || place.lng === null),
        );

        if (unresolved.length === 0) {
            return;
        }

        let cancelled = false;

        void Promise.all(
            unresolved.map(async ({ place, setPlace }) => {
                try {
                    const result = await geocodeAddress(place.address);

                    if (!cancelled && result) {
                        setPlace({
                            ...place,
                            lat: result.lat,
                            lng: result.lng,
                            address: place.address || result.formattedAddress,
                        });
                    }
                } catch {
                    // A missing/invalid key must not block booking.
                }
            }),
        );

        return () => {
            cancelled = true;
        };
    }, [pickup.address, pickup.lat, pickup.lng, destination.address, destination.lat, destination.lng, hourly]);

    const etaMinutes = (to: { lat: number; lng: number }) =>
        origin === null
            ? null
            : Math.max(
                  10,
                  Math.round(
                      ((haversineKm(origin, to) * travel.road_factor) /
                          travel.average_speed_kmh) *
                          60,
                  ),
              );

    const fill = (place: PlaceValue) => {
        if (active === 'pickup' || hourly) {
            setPickup(place);
            setActive('destination');
        } else {
            setDestination(place);
        }
    };

    const swap = () => {
        setPickup(destination);
        setDestination(pickup);
    };

    const pickupPoint = hasCoordinates(pickup)
        ? pickup
        : trip.pickup_lat !== null && trip.pickup_lng !== null
          ? { lat: trip.pickup_lat, lng: trip.pickup_lng }
          : null;
    const destinationPoint = hasCoordinates(destination)
        ? destination
        : trip.destination_lat !== null && trip.destination_lng !== null
          ? { lat: trip.destination_lat, lng: trip.destination_lng }
          : null;

    const points: MapPoint[] = [
        pickupPoint && {
            label: 'A',
            lat: pickupPoint.lat,
            lng: pickupPoint.lng,
            color: '#0b1f33',
        },
        !hourly &&
            destinationPoint && {
                label: 'B',
                lat: destinationPoint.lat,
                lng: destinationPoint.lng,
                color: '#c08a35',
            },
    ].filter((point): point is MapPoint => Boolean(point));

    const title = trip.destination
        ? `${trip.pickup} to ${trip.destination}`
        : trip.pickup;

    const submit = () => {
        router.put(
            update.url(),
            {
                pickup,
                destination: hourly ? null : destination,
                passengers,
                bags: trip.bags,
                date: date || null,
            },
            {
                onStart: () => setProcessing(true),
                onFinish: () => setProcessing(false),
                onError: (validationErrors) => setErrors(validationErrors),
            },
        );
    };

    const selectRecentSearch = (search: RecentSearch) => {
        router.put(
            update.url(),
            {
                pickup: search.pickup,
                destination: hourly ? null : search.destination,
                passengers,
                bags: trip.bags,
                date: date || null,
            },
            {
                onStart: () => setProcessing(true),
                onFinish: () => setProcessing(false),
                onError: (validationErrors) => setErrors(validationErrors),
            },
        );
    };

    const fieldInputClass =
        'block w-full truncate bg-transparent text-sm font-medium text-navy outline-none placeholder:font-normal placeholder:text-gray-400 dark:text-white';

    return (
        <>
            <Head title="Location Select" />
            <div className="min-h-screen bg-mist font-google text-navy dark:bg-ink dark:text-white">
                <SiteHeader variant="solid" />

                <main className="mx-auto max-w-7xl px-4 pt-10 pb-20 sm:px-6 lg:px-10 lg:pt-16">
                    <PageIntro
                        title="Location Select"
                        breadcrumbs={['Booking', title]}
                    />

                    <TripSummaryBar trip={trip} />

                    <div className="mt-12 grid min-w-0 items-start gap-5 lg:grid-cols-[minmax(0,0.85fr)_minmax(0,1.15fr)]">
                        <div className="min-w-0 space-y-4">
                            <form
                                onSubmit={(event) => {
                                    event.preventDefault();
                                    submit();
                                }}
                                className={`${cardClass} p-5 sm:p-6`}
                            >
                                <p className="text-sm font-medium tracking-wide text-navy/80 uppercase dark:text-white/80">
                                    Step 1 of 4: Route selection
                                </p>

                                <div className="mt-5 rounded-xl border border-navy/10 p-2 dark:border-white/10">
                                    <div className="relative px-3 py-2">
                                        <div className="flex gap-3">
                                            <span className="mt-1.5 flex flex-col items-center">
                                                <span className="size-2 rounded-full bg-navy ring-4 ring-navy/10 dark:bg-white" />
                                                {!hourly && (
                                                    <span className="my-1 h-14 border-l border-dashed border-navy/40 dark:border-white/40" />
                                                )}
                                                {!hourly && (
                                                    <span className="size-2 rounded-full bg-gold-dark" />
                                                )}
                                            </span>
                                            <div className="min-w-0 flex-1">
                                                <label
                                                    htmlFor="pickup"
                                                    className="text-xs text-navy/60 dark:text-white/60"
                                                >
                                                    {hourly
                                                        ? 'Pickup'
                                                        : 'Arrival from'}
                                                </label>
                                                <PlaceAutocomplete
                                                    id="pickup"
                                                    value={pickup}
                                                    onChange={setPickup}
                                                    onFocus={() =>
                                                        setActive('pickup')
                                                    }
                                                    placeholder="Airport, hotel or address"
                                                    inputClassName={
                                                        fieldInputClass
                                                    }
                                                    currentLocation
                                                />
                                                <InputError
                                                    message={
                                                        errors['pickup.address']
                                                    }
                                                    className="mt-1 text-xs"
                                                />

                                                {!hourly && (
                                                    <>
                                                        <div className="my-3 h-px bg-navy/10 dark:bg-white/10" />
                                                        <label
                                                            htmlFor="destination"
                                                            className="text-xs text-navy/60 dark:text-white/60"
                                                        >
                                                            Destination
                                                        </label>
                                                        <PlaceAutocomplete
                                                            id="destination"
                                                            value={destination}
                                                            onChange={
                                                                setDestination
                                                            }
                                                            onFocus={() =>
                                                                setActive(
                                                                    'destination',
                                                                )
                                                            }
                                                            placeholder="Where are you going?"
                                                            inputClassName={
                                                                fieldInputClass
                                                            }
                                                        />
                                                        <InputError
                                                            message={
                                                                errors[
                                                                    'destination.address'
                                                                ]
                                                            }
                                                            className="mt-1 text-xs"
                                                        />
                                                    </>
                                                )}
                                            </div>
                                        </div>

                                        {!hourly && (
                                            <button
                                                type="button"
                                                onClick={swap}
                                                aria-label="Swap pickup and destination"
                                                className="absolute top-1/2 right-12 flex size-10 -translate-y-1/2 items-center justify-center rounded-full border border-navy/10 bg-white text-navy shadow-sm transition-colors hover:border-gold dark:border-white/10 dark:bg-ocean dark:text-white"
                                            >
                                                <ArrowUpDown className="size-4" />
                                            </button>
                                        )}
                                    </div>

                                    <div className="mt-2 grid gap-2 sm:grid-cols-2">
                                        <label className="relative flex items-center gap-3 rounded-xl border border-navy/10 px-3 py-2.5 focus-within:ring-2 focus-within:ring-gold/40 dark:border-white/10">
                                            <UserRound className="size-4 shrink-0" />
                                            <span className="min-w-0 flex-1">
                                                <span className="block text-xs text-navy/60 dark:text-white/60">
                                                    Passengers
                                                </span>
                                                <select
                                                    value={passengers}
                                                    onChange={(event) =>
                                                        setPassengers(
                                                            Number(
                                                                event.target
                                                                    .value,
                                                            ),
                                                        )
                                                    }
                                                    className="w-full cursor-pointer appearance-none bg-transparent text-sm font-medium outline-none"
                                                >
                                                    {Array.from(
                                                        { length: 16 },
                                                        (_, i) => i + 1,
                                                    ).map((count) => (
                                                        <option
                                                            key={count}
                                                            value={count}
                                                            className="text-navy"
                                                        >
                                                            {count}{' '}
                                                            {count === 1
                                                                ? 'Adult'
                                                                : 'Adults'}
                                                        </option>
                                                    ))}
                                                </select>
                                            </span>
                                            <ChevronDown className="pointer-events-none size-4 text-navy/50 dark:text-white/50" />
                                        </label>

                                        <label className="flex items-center gap-3 rounded-xl border border-navy/10 px-3 py-2.5 focus-within:ring-2 focus-within:ring-gold/40 dark:border-white/10">
                                            <CalendarDays className="size-4 shrink-0" />
                                            <span className="min-w-0 flex-1">
                                                <span className="block text-xs text-navy/60 dark:text-white/60">
                                                    Date
                                                </span>
                                                <input
                                                    type="date"
                                                    value={date}
                                                    min={todayValue()}
                                                    onChange={(event) =>
                                                        setDate(
                                                            event.target.value,
                                                        )
                                                    }
                                                    className="w-full bg-transparent text-sm font-medium outline-none dark:[color-scheme:dark]"
                                                />
                                            </span>
                                        </label>
                                    </div>
                                    <InputError
                                        message={
                                            errors.passengers ?? errors.date
                                        }
                                        className="mt-1 px-1 text-xs"
                                    />

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

                            <div className={`${cardClass} p-3 sm:p-5`}>
                                {savedPlaces.length > 0 && (
                                    <PlaceSection title="Saved & frequent places">
                                        {savedPlaces.map((place, index) => (
                                            <PlaceRow
                                                key={place.address}
                                                icon={
                                                    index === 0
                                                        ? Building2
                                                        : House
                                                }
                                                title={place.address}
                                                meta={`${place.trips} ${place.trips === 1 ? 'trip' : 'trips'}`}
                                                onSelect={() => fill(place)}
                                            />
                                        ))}
                                    </PlaceSection>
                                )}

                                {recent.length > 0 && (
                                    <PlaceSection
                                        title="Recent searches"
                                        aside={
                                            <button
                                                type="button"
                                                onClick={() =>
                                                    router.delete(clear.url(), {
                                                        preserveScroll: true,
                                                        preserveState: true,
                                                        onSuccess: () =>
                                                            setRecent([]),
                                                    })
                                                }
                                                className="text-xs font-medium text-navy/60 hover:text-gold-dark dark:text-white/60"
                                            >
                                                Clear
                                            </button>
                                        }
                                    >
                                        {recent.map((search) => (
                                            <PlaceRow
                                                key={`${search.pickup.address}|${search.destination?.address}`}
                                                icon={History}
                                                title={
                                                    search.destination
                                                        ? `${search.pickup.address} → ${search.destination.address}`
                                                        : search.pickup.address
                                                }
                                                meta={search.searched_at}
                                                onSelect={() =>
                                                    selectRecentSearch(search)
                                                }
                                            />
                                        ))}
                                    </PlaceSection>
                                )}

                                {hubs.length > 0 && (
                                    <PlaceSection
                                        title="Airports & transit hubs"
                                        aside={
                                            <span className="text-xs text-navy/60 dark:text-white/60">
                                                Nearby
                                            </span>
                                        }
                                    >
                                        {hubs.map((hub, index) => {
                                            const eta = etaMinutes(hub);

                                            return (
                                                <PlaceRow
                                                    key={hub.id}
                                                    icon={
                                                        hub.type === 'airport'
                                                            ? Plane
                                                            : TrainFront
                                                    }
                                                    title={hub.name}
                                                    subtitle={hub.address}
                                                    highlighted={index === 0}
                                                    meta={
                                                        eta !== null && (
                                                            <span className="flex items-center gap-1">
                                                                <Clock3 className="size-3" />
                                                                ~{eta} min
                                                            </span>
                                                        )
                                                    }
                                                    onSelect={() =>
                                                        fill({
                                                            address: `${hub.name}, ${hub.address}`,
                                                            lat: hub.lat,
                                                            lng: hub.lng,
                                                            place_id: hub.id,
                                                        })
                                                    }
                                                />
                                            );
                                        })}
                                    </PlaceSection>
                                )}

                                {savedPlaces.length === 0 &&
                                    recent.length === 0 &&
                                    hubs.length === 0 && (
                                        <p className="px-3 pb-2 text-sm text-navy/70 dark:text-white/70">
                                            Start typing an address above — your
                                            saved places, recent searches and
                                            nearby airports will show up here.
                                        </p>
                                    )}

                                <button
                                    type="button"
                                    onClick={submit}
                                    disabled={processing}
                                    className="mt-4 flex w-full items-center justify-center gap-2 rounded-lg bg-gold px-6 py-3.5 text-sm font-semibold text-white transition-colors hover:bg-gold-dark disabled:opacity-70"
                                >
                                    {processing && <Spinner />}
                                    Continue to Date & Time
                                    <ArrowRight className="size-4" />
                                </button>
                            </div>
                        </div>

                        <div className="min-w-0 w-full space-y-4 lg:sticky lg:top-6">
                            <RouteMap
                                points={points}
                                className="h-80 overflow-hidden rounded-2xl sm:h-[30rem] lg:h-[37rem]"
                            />

                            <div className="grid min-w-0 gap-4 sm:grid-cols-3">
                                {perks.map(
                                    ({
                                        icon: Icon,
                                        title: perk,
                                        text,
                                        tone,
                                    }) => (
                                        <div
                                            key={perk}
                                            className={`${cardClass} min-w-0 p-5`}
                                        >
                                            <span
                                                className={cn(
                                                    'flex size-9 items-center justify-center rounded-lg dark:bg-white/10 dark:text-white',
                                                    tone,
                                                )}
                                            >
                                                <Icon className="size-4" />
                                            </span>
                                            <p className="mt-6 break-words text-lg font-semibold">
                                                {perk}
                                            </p>
                                            <p className="mt-1 text-sm text-navy/70 dark:text-white/70">
                                                {text}
                                            </p>
                                        </div>
                                    ),
                                )}
                            </div>
                        </div>
                    </div>
                </main>

                <SiteFooter className="bg-mist dark:bg-ink" />
            </div>
        </>
    );
}
