import { useForm } from '@inertiajs/react';
import type { FormEvent } from 'react';
import { useEffect, useMemo } from 'react';
import {
    type BookingDetail,
} from '@/components/booking/booking-detail-panel';
import { AdminBookingPaymentActions } from '@/components/booking/admin-booking-payment-actions';
import { LocationSearchSelect } from '@/components/booking/location-search-select';
import { DateTimePicker, parseDateTimeValue } from '@/components/date-time-picker';
import { FormField } from '@/components/form-field';
import { FormDrawer } from '@/components/layout/form-drawer';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Field } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { NativeSelect } from '@/components/ui/native-select';
import { SheetFooter } from '@/components/ui/sheet';
import { Textarea } from '@/components/ui/textarea';
import { packagesForLocation, resolvePackageIdAfterLocationChange } from '@/lib/package-location';
import adminBookings from '@/routes/admin/bookings';

export const SERVICE_STATUS_OPTIONS = [
    { value: 'scheduled', label: 'Scheduled' },
    { value: 'arrived', label: 'Arrived' },
    { value: 'in_progress', label: 'In progress' },
    { value: 'completed', label: 'Completed' },
    { value: 'cancelled', label: 'Cancelled' },
    { value: 'no_show', label: 'No show' },
] as const;

export type TechnicianOption = {
    id: number;
    name: string;
};

export type LocationOption = {
    id: number;
    name: string;
    address: string | null;
    city: string | null;
    state: string | null;
    zip: string | null;
};

export type PackageOption = {
    id: number;
    name: string;
    duration_minutes: number;
    prices_by_slug: Record<string, number>;
    location_ids: number[];
};

export type AddonOption = {
    id: number;
    name: string;
    duration_minutes: number;
    price_sedan: number;
    price_suv: number;
    price_truck: number;
    price_van: number;
    price_oversized: number;
};

export type AdminEditableBooking = BookingDetail & {
    technician_id: number | null;
    location_id: number | null;
    package_id: number;
    amount_paid: number;
    addons_total: number;
    stripe_payment_intent_id?: string | null;
};

function packagePriceForVehicle(
    pkg: PackageOption,
    vehicleSize: string,
): number {
    return pkg.prices_by_slug[vehicleSize] ?? Object.values(pkg.prices_by_slug)[0] ?? 0;
}

function addonPriceForSize(addon: AddonOption, vehicleSize: string): number {
    switch (vehicleSize) {
        case 'suv':
            return addon.price_suv;
        case 'truck':
            return addon.price_truck;
        case 'van':
            return addon.price_van;
        case 'oversized':
            return addon.price_oversized;
        default:
            return addon.price_sedan;
    }
}

function addonIdsEqual(left: number[], right: number[]): boolean {
    if (left.length !== right.length) {
        return false;
    }

    const sortedLeft = [...left].sort((a, b) => a - b);
    const sortedRight = [...right].sort((a, b) => a - b);

    return sortedLeft.every((id, index) => id === sortedRight[index]);
}

type AdminBookingEditDrawerProps = {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    booking: AdminEditableBooking | null;
    technicians: TechnicianOption[];
    locations: LocationOption[];
    packages: PackageOption[];
    addons: AddonOption[];
    stripeEnabled: boolean;
    stripePublishableKey: string | null;
};

export function AdminBookingEditDrawer({
    open,
    onOpenChange,
    booking,
    technicians,
    locations,
    packages,
    addons,
    stripeEnabled,
    stripePublishableKey,
}: AdminBookingEditDrawerProps) {
    const editForm = useForm({
        technician_id: '' as number | '',
        package_id: '' as number | '',
        location_id: '' as number | '',
        service_address: '',
        service_city: '',
        service_state: '',
        service_zip: '',
        scheduled_at: '',
        service_status: 'scheduled',
        internal_notes: '',
        addon_ids: [] as number[],
    });

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

        editForm.setData({
            technician_id: booking.technician_id ?? '',
            package_id: booking.package_id,
            location_id: booking.location_id ?? '',
            service_address: booking.service_address ?? '',
            service_city: booking.service_city ?? '',
            service_state: booking.service_state ?? '',
            service_zip: booking.service_zip ?? '',
            scheduled_at: booking.scheduled_at,
            service_status: booking.service_status,
            internal_notes: booking.internal_notes ?? '',
            addon_ids: (booking.addons ?? []).map((addon) => addon.id),
        });
        editForm.clearErrors();
        // eslint-disable-next-line react-hooks/exhaustive-deps -- reset when the selected booking changes
    }, [booking?.id]);

    const closeEdit = () => {
        onOpenChange(false);
        editForm.reset();
        editForm.clearErrors();
    };

    const toggleAddon = (addonId: number, checked: boolean) => {
        if (checked) {
            editForm.setData('addon_ids', [...editForm.data.addon_ids, addonId]);
            return;
        }

        editForm.setData(
            'addon_ids',
            editForm.data.addon_ids.filter((id) => id !== addonId),
        );
    };

    const serviceType =
        booking?.service_type === 'mobile' ? 'mobile' : 'fixed';

    const availablePackages = useMemo(
        () =>
            packagesForLocation(
                packages,
                editForm.data.location_id,
                serviceType,
            ),
        [packages, editForm.data.location_id, serviceType],
    );

    const handleLocationChange = (locationId: number | '') => {
        editForm.setData({
            ...editForm.data,
            location_id: locationId,
            package_id: resolvePackageIdAfterLocationChange(
                packages,
                locationId,
                serviceType,
                editForm.data.package_id,
            ),
        });
    };

    const submitEdit = (event: FormEvent<HTMLFormElement>) => {
        event.preventDefault();

        if (!booking) {
            return;
        }

        editForm.transform((data) => ({
            technician_id:
                data.technician_id === '' ? null : Number(data.technician_id),
            package_id: Number(data.package_id),
            location_id:
                booking.service_type === 'fixed'
                    ? data.location_id === ''
                        ? null
                        : Number(data.location_id)
                    : null,
            service_address:
                booking.service_type === 'mobile'
                    ? data.service_address || null
                    : null,
            service_city:
                booking.service_type === 'mobile'
                    ? data.service_city || null
                    : null,
            service_state:
                booking.service_type === 'mobile'
                    ? data.service_state || null
                    : null,
            service_zip:
                booking.service_type === 'mobile'
                    ? data.service_zip || null
                    : null,
            scheduled_at: data.scheduled_at
                ? new Date(data.scheduled_at).toISOString()
                : null,
            service_status: data.service_status,
            internal_notes: data.internal_notes || null,
            addon_ids: data.addon_ids,
        }));

        editForm.post(adminBookings.update.url({ booking: booking.id }), {
            preserveScroll: true,
            onSuccess: () => closeEdit(),
        });
    };

    const vehicleSize = booking?.vehicle?.size ?? 'sedan';
    const selectedPackage = availablePackages.find(
        (pkg) => pkg.id === Number(editForm.data.package_id),
    ) ?? packages.find((pkg) => pkg.id === Number(editForm.data.package_id));
    const packageChanged =
        booking !== null &&
        Number(editForm.data.package_id) !== booking.package_id;
    const initialAddonIds = (booking?.addons ?? []).map((addon) => addon.id);
    const addonsChanged =
        booking !== null &&
        !addonIdsEqual(initialAddonIds, editForm.data.addon_ids);

    const selectedAddonsTotal = editForm.data.addon_ids.reduce((carry, addonId) => {
        const addon = addons.find((item) => item.id === addonId);

        if (!addon) {
            return carry;
        }

        return carry + addonPriceForSize(addon, vehicleSize);
    }, 0);

    const projectedSubtotal =
        selectedPackage != null
            ? packagePriceForVehicle(selectedPackage, vehicleSize)
            : null;

    const projectedTotalPreview =
        projectedSubtotal !== null
            ? projectedSubtotal + selectedAddonsTotal
            : null;

    const hasUnsavedPriceChange = packageChanged || addonsChanged;
    const effectiveTotal =
        hasUnsavedPriceChange && projectedTotalPreview !== null
            ? projectedTotalPreview
            : (booking?.total ?? 0);
    const balanceDueCents = Math.max(
        0,
        effectiveTotal - (booking?.amount_paid ?? 0),
    );
    const showPaymentPanel = booking !== null && balanceDueCents > 0;

    return (
        <FormDrawer
            open={open}
            onOpenChange={(nextOpen) => {
                if (!nextOpen) {
                    closeEdit();
                    return;
                }

                onOpenChange(nextOpen);
            }}
            title={`Edit booking #${booking?.id ?? ''}`}
            description={
                booking?.customer
                    ? booking.customer.name
                    : 'Update appointment details'
            }
            direction="right"
            className="w-full sm:max-w-lg"
        >
            {booking ? (
                <form onSubmit={submitEdit} className="grid gap-4">
                    <FormField
                        label="Service status"
                        htmlFor="edit-service-status"
                        error={editForm.errors.service_status}
                    >
                        <NativeSelect
                            id="edit-service-status"
                            value={editForm.data.service_status}
                            onChange={(event) =>
                                editForm.setData(
                                    'service_status',
                                    event.target.value,
                                )
                            }
                        >
                            {SERVICE_STATUS_OPTIONS.map((option) => (
                                <option key={option.value} value={option.value}>
                                    {option.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </FormField>

                    <FormField
                        label="Technician"
                        htmlFor="edit-technician"
                        error={editForm.errors.technician_id}
                    >
                        <NativeSelect
                            id="edit-technician"
                            value={editForm.data.technician_id}
                            onChange={(event) =>
                                editForm.setData(
                                    'technician_id',
                                    event.target.value === ''
                                        ? ''
                                        : Number(event.target.value),
                                )
                            }
                        >
                            <option value="">Unassigned</option>
                            {technicians.map((technician) => (
                                <option key={technician.id} value={technician.id}>
                                    {technician.name}
                                </option>
                            ))}
                        </NativeSelect>
                    </FormField>

                    {booking.service_type === 'fixed' ? (
                        <FormField
                            label="Service location"
                            htmlFor="edit-location"
                            error={editForm.errors.location_id}
                        >
                            <LocationSearchSelect
                                locations={locations}
                                value={editForm.data.location_id}
                                onValueChange={handleLocationChange}
                                inputId="edit-location"
                            />
                        </FormField>
                    ) : (
                        <>
                            <FormField
                                label="Service address"
                                htmlFor="edit-service-address"
                                error={editForm.errors.service_address}
                            >
                                <Input
                                    id="edit-service-address"
                                    value={editForm.data.service_address}
                                    onChange={(event) =>
                                        editForm.setData(
                                            'service_address',
                                            event.target.value,
                                        )
                                    }
                                />
                            </FormField>
                            <div className="grid gap-4 sm:grid-cols-3">
                                <FormField
                                    label="City"
                                    htmlFor="edit-service-city"
                                    error={editForm.errors.service_city}
                                >
                                    <Input
                                        id="edit-service-city"
                                        value={editForm.data.service_city}
                                        onChange={(event) =>
                                            editForm.setData(
                                                'service_city',
                                                event.target.value,
                                            )
                                        }
                                    />
                                </FormField>
                                <FormField
                                    label="State"
                                    htmlFor="edit-service-state"
                                    error={editForm.errors.service_state}
                                >
                                    <Input
                                        id="edit-service-state"
                                        value={editForm.data.service_state}
                                        onChange={(event) =>
                                            editForm.setData(
                                                'service_state',
                                                event.target.value,
                                            )
                                        }
                                    />
                                </FormField>
                                <FormField
                                    label="ZIP"
                                    htmlFor="edit-service-zip"
                                    error={editForm.errors.service_zip}
                                >
                                    <Input
                                        id="edit-service-zip"
                                        value={editForm.data.service_zip}
                                        onChange={(event) =>
                                            editForm.setData(
                                                'service_zip',
                                                event.target.value,
                                            )
                                        }
                                    />
                                </FormField>
                            </div>
                        </>
                    )}

                    <FormField
                        label="Package"
                        htmlFor="edit-package"
                        error={editForm.errors.package_id}
                    >
                        <NativeSelect
                            id="edit-package"
                            value={editForm.data.package_id}
                            onChange={(event) =>
                                editForm.setData(
                                    'package_id',
                                    Number(event.target.value),
                                )
                            }
                        >
                            {availablePackages.map((pkg) => (
                                <option key={pkg.id} value={pkg.id}>
                                    {pkg.name} ({pkg.duration_minutes} min)
                                </option>
                            ))}
                        </NativeSelect>
                    </FormField>

                    {addons.length > 0 ? (
                        <Field className="gap-2">
                            <Label>Add-ons</Label>
                            <div className="grid gap-2 rounded-lg border p-3 sm:grid-cols-2">
                                {addons.map((addon) => {
                                    const checked = editForm.data.addon_ids.includes(
                                        addon.id,
                                    );
                                    const price = addonPriceForSize(
                                        addon,
                                        vehicleSize,
                                    );

                                    return (
                                        <label
                                            key={addon.id}
                                            className="flex cursor-pointer items-start gap-3"
                                        >
                                            <Checkbox
                                                checked={checked}
                                                onCheckedChange={(value) =>
                                                    toggleAddon(
                                                        addon.id,
                                                        value === true,
                                                    )
                                                }
                                            />
                                            <span className="text-sm leading-snug">
                                                <span className="font-medium">
                                                    {addon.name}
                                                </span>
                                                <span className="text-muted-foreground">
                                                    {' '}
                                                    (+$
                                                    {(price / 100).toFixed(2)})
                                                </span>
                                            </span>
                                        </label>
                                    );
                                })}
                            </div>
                            {editForm.errors.addon_ids ? (
                                <p className="text-sm text-destructive">
                                    {editForm.errors.addon_ids}
                                </p>
                            ) : null}
                        </Field>
                    ) : null}

                    {showPaymentPanel ? (
                        <AdminBookingPaymentActions
                            booking={{
                                id: booking.id,
                                uuid: booking.uuid,
                                total: effectiveTotal,
                                amount_paid: booking.amount_paid,
                                payment_status: booking.payment_status,
                                stripe_payment_intent_id:
                                    booking.stripe_payment_intent_id ?? null,
                            }}
                            stripeEnabled={stripeEnabled}
                            stripePublishableKey={stripePublishableKey}
                            subtotalCents={
                                selectedAddonsTotal > 0 ? projectedSubtotal : null
                            }
                            addonsTotalCents={
                                selectedAddonsTotal > 0
                                    ? selectedAddonsTotal
                                    : null
                            }
                            collectActionsDisabled={hasUnsavedPriceChange}
                            collectActionsDisabledReason={
                                hasUnsavedPriceChange
                                    ? 'Save changes before charging the updated balance.'
                                    : undefined
                            }
                        />
                    ) : null}

                    <FormField
                        label="Date & time"
                        htmlFor="edit-scheduled-at"
                        error={editForm.errors.scheduled_at}
                    >
                        <DateTimePicker
                            id="edit-scheduled-at"
                            value={parseDateTimeValue(editForm.data.scheduled_at)}
                            onChange={(date) =>
                                editForm.setData(
                                    'scheduled_at',
                                    date?.toISOString() ?? '',
                                )
                            }
                        />
                    </FormField>

                    <FormField
                        label="Internal notes"
                        htmlFor="edit-internal-notes"
                        error={editForm.errors.internal_notes}
                    >
                        <Textarea
                            id="edit-internal-notes"
                            value={editForm.data.internal_notes}
                            onChange={(event) =>
                                editForm.setData(
                                    'internal_notes',
                                    event.target.value,
                                )
                            }
                        />
                    </FormField>

                    <SheetFooter className="px-0 pb-0 sm:flex-row">
                        <Button type="button" variant="outline" onClick={closeEdit}>
                            Close
                        </Button>
                        <Button type="submit" disabled={editForm.processing}>
                            Save changes
                        </Button>
                    </SheetFooter>
                </form>
            ) : null}
        </FormDrawer>
    );
}
