import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import type { FormEvent, ReactNode } 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';

type StripePaymentFormProps = {
    clientSecret: string;
    stripePublishableKey: string;
    submitLabel?: string;
    onSuccess?: () => void;
    confirmUrl?: string;
    footer?: ReactNode;
};

function InnerStripePaymentForm({
    submitLabel = 'Pay now',
    onSuccess,
    confirmUrl,
    footer,
}: Omit<StripePaymentFormProps, 'clientSecret' | 'stripePublishableKey'>) {
    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 result = await stripe.confirmPayment({
            elements,
            confirmParams: {
                return_url: window.location.href,
            },
            redirect: 'if_required',
        });

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

            return;
        }

        const status = result.paymentIntent?.status;

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

                    return;
                }
            }

            onSuccess?.();

            return;
        }

        setError('Payment could not be completed. Try again.');
        setProcessing(false);
    };

    return (
        <form onSubmit={submit} className="grid gap-4">
            <PaymentElement
                options={{
                    wallets: {
                        applePay: 'auto',
                        googlePay: 'auto',
                    },
                }}
            />
            {error ? (
                <Alert variant="destructive">
                    <AlertDescription>{error}</AlertDescription>
                </Alert>
            ) : null}
            <p className="text-xs text-muted-foreground">
                Apple Pay and Google Pay appear automatically on supported phones and tablets.
            </p>
            <Button type="submit" disabled={processing || !stripe || !elements}>
                {processing ? 'Processing…' : submitLabel}
            </Button>
            {footer}
        </form>
    );
}

export function StripePaymentForm({
    clientSecret,
    stripePublishableKey,
    submitLabel,
    onSuccess,
    confirmUrl,
    footer,
}: StripePaymentFormProps) {
    const stripePromise = useMemo(
        () => loadStripe(stripePublishableKey),
        [stripePublishableKey],
    );

    return (
        <Elements
            key={clientSecret}
            stripe={stripePromise}
            options={{
                clientSecret,
                appearance: { theme: 'stripe' },
            }}
        >
            <InnerStripePaymentForm
                submitLabel={submitLabel}
                onSuccess={onSuccess}
                confirmUrl={confirmUrl}
                footer={footer}
            />
        </Elements>
    );
}
