import { Head, Link, router } from '@inertiajs/react';
import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import type { FormEvent } from 'react';
import { useMemo, useState } from 'react';
import { syncBookingPayment } from '@/lib/sync-booking-payment';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { useBookingDateTimeFormatters } from '@/hooks/use-booking-timezone';

type BookingPayload = {
    uuid: string;
    reference: string;
    id: number;
    scheduled_at: string | null;
    total: number;
    amount_paid: number;
    balance_due_cents: number;
    payment_method: string | null;
    customer_name: string | null;
    package_name: string | null;
};

type Props = {
    booking: BookingPayload;
    balanceDueCents: number;
    clientSecret: string | null;
    stripePublishableKey: string | null;
    alreadyPaid: boolean;
};

function BalancePaymentForm({
    bookingUuid,
    confirmUrl,
}: {
    bookingUuid: string;
    confirmUrl: string;
}) {
    const stripe = useStripe();
    const elements = useElements();
    const [error, setError] = useState<string | null>(null);
    const [processing, setProcessing] = useState(false);

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

        if (!stripe || !elements) {
            setError('Stripe is still loading.');

            return;
        }

        setProcessing(true);
        setError(null);

        const returnUrl = `${window.location.origin}/pay/${bookingUuid}?paid=1`;
        const result = await stripe.confirmPayment({
            elements,
            confirmParams: { return_url: returnUrl },
            redirect: 'if_required',
        });

        if (result.error) {
            setError(result.error.message ?? 'Payment failed.');
            setProcessing(false);

            return;
        }

        try {
            await syncBookingPayment(confirmUrl);
        } catch (syncError) {
            setError(
                syncError instanceof Error
                    ? syncError.message
                    : 'Payment succeeded but the booking could not be updated.',
            );
            setProcessing(false);

            return;
        }

        router.visit(returnUrl);
    };

    return (
        <form onSubmit={submit} className="grid gap-4">
            <PaymentElement />
            {error ? (
                <Alert variant="destructive">
                    <AlertDescription>{error}</AlertDescription>
                </Alert>
            ) : null}
            <Button type="submit" disabled={processing || !stripe || !elements}>
                {processing ? 'Processing…' : 'Pay balance now'}
            </Button>
        </form>
    );
}

export default function PayBalance({
    booking,
    balanceDueCents,
    clientSecret,
    stripePublishableKey,
    alreadyPaid,
}: Props) {
    const paidFromQuery =
        typeof window !== 'undefined' &&
        new URLSearchParams(window.location.search).get('paid') === '1';

    const stripePromise = useMemo(
        () =>
            stripePublishableKey && clientSecret
                ? loadStripe(stripePublishableKey)
                : null,
        [stripePublishableKey, clientSecret],
    );

    const { formatDateTime } = useBookingDateTimeFormatters();

    const scheduledLabel = booking.scheduled_at
        ? formatDateTime(booking.scheduled_at)
        : 'TBD';

    return (
        <>
            <Head title="Pay balance" />

            <div className="flex flex-col items-center gap-6 py-4">
                <Card className="w-full max-w-lg">
                    <CardHeader>
                        <CardTitle>Booking balance</CardTitle>
                        <CardDescription>
                            {booking.customer_name
                                ? `Hi ${booking.customer_name}, `
                                : ''}
                            booking #{booking.reference}
                            {booking.package_name
                                ? ` · ${booking.package_name}`
                                : ''}
                        </CardDescription>
                    </CardHeader>
                    <CardContent className="grid gap-4">
                        <div className="rounded-lg border bg-muted/40 p-4 text-sm">
                            <p>
                                <span className="text-muted-foreground">
                                    Scheduled:{' '}
                                </span>
                                {scheduledLabel}
                            </p>
                            <p className="mt-1">
                                <span className="text-muted-foreground">
                                    Total:{' '}
                                </span>
                                ${(booking.total / 100).toFixed(2)}
                            </p>
                            <p className="mt-1">
                                <span className="text-muted-foreground">
                                    Paid:{' '}
                                </span>
                                ${(booking.amount_paid / 100).toFixed(2)}
                            </p>
                        </div>

                        {alreadyPaid || paidFromQuery || balanceDueCents <= 0 ? (
                            <Alert>
                                <AlertDescription>
                                    {paidFromQuery
                                        ? 'Thank you. Your payment was received.'
                                        : 'This booking has no balance due.'}
                                </AlertDescription>
                            </Alert>
                        ) : (
                            <>
                                <p className="text-lg font-semibold">
                                    Balance due: $
                                    {(balanceDueCents / 100).toFixed(2)}
                                </p>

                                {stripePromise && clientSecret ? (
                                    <Elements
                                        stripe={stripePromise}
                                        options={{ clientSecret }}
                                    >
                                        <BalancePaymentForm
                                            bookingUuid={booking.uuid}
                                            confirmUrl={`/pay/${booking.uuid}/confirm`}
                                        />
                                    </Elements>
                                ) : (
                                    <p className="text-sm text-muted-foreground">
                                        Online card payment is not available
                                        right now. Pay this balance in person at
                                        your appointment.
                                    </p>
                                )}

                                <p className="text-sm text-muted-foreground">
                                    You can also pay in person when you arrive
                                    {booking.payment_method === 'pay_at_service'
                                        ? ' (pay at service)'
                                        : ''}
                                    .
                                </p>
                            </>
                        )}

                        <Button variant="outline" asChild className="w-full">
                            <Link href={`/track/${booking.uuid}`}>
                                View booking status
                            </Link>
                        </Button>
                    </CardContent>
                </Card>
            </div>
        </>
    );
}
