/**
 * Card payment inside the booking form (Stripe Payment Element).
 * The Stripe libraries are loaded on demand, only when a customer pays by card.
 */
import React, { useEffect, useState } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { Alert, Button, Skeleton, SkeletonGroup } from '../../../shared/ui';
import { api } from '../api';
import { front } from '../config';

function returnUrlFor(bookingId, key) {
	const url = new URL(window.location.href);
	['pointlybooking_payment', 'booking_id', 'key', 'payment_intent', 'payment_intent_client_secret', 'redirect_status'].forEach((k) => url.searchParams.delete(k));
	url.hash = '';
	url.searchParams.set('pointlybooking_payment', 'stripe_success');
	url.searchParams.set('booking_id', String(bookingId));
	url.searchParams.set('key', key);
	return url.toString();
}

function PayForm({ mods, bookingId, bookingKey, amountLabel, onPaid }) {
	const stripe = mods.useStripe();
	const elements = mods.useElements();
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState('');
	const [ready, setReady] = useState(false);

	const pay = async (e) => {
		e.preventDefault();
		if (!stripe || !elements || busy) return;
		setBusy(true);
		setError('');
		try {
			const { error: stripeError, paymentIntent } = await stripe.confirmPayment({
				elements,
				confirmParams: { return_url: returnUrlFor(bookingId, bookingKey) },
				redirect: 'if_required',
			});
			if (stripeError) {
				setError(stripeError.message || __('The payment failed. Please try again.', 'pointly-booking'));
				setBusy(false);
				return;
			}
			try {
				await api.stripeConfirm({
					booking_id: bookingId,
					key: bookingKey,
					payment_intent_id: paymentIntent?.id || '',
					status: paymentIntent?.status || '',
				});
			} catch (confirmError) {
				// The card was charged; the confirmation screen polls the booking
				// status (the webhook confirms it too), so carry on.
			}
			onPaid();
		} catch (err) {
			setError(err?.message || __('The payment failed. Please try again.', 'pointly-booking'));
			setBusy(false);
		}
	};

	return (
		<form onSubmit={pay} className="bp-bw-stripe__form">
			<mods.PaymentElement onReady={() => setReady(true)} options={{ layout: 'tabs' }} />
			{error ? (
				<Alert tone="danger" live>
					{error}
				</Alert>
			) : null}
			<Button type="submit" variant="primary" size="lg" block loading={busy} disabled={!stripe || !ready} icon="lock">
				{
					/* translators: %s: amount, e.g. "$30". */
					amountLabel ? sprintf(__('Pay %s', 'pointly-booking'), amountLabel) : __('Pay now', 'pointly-booking')
				}
			</Button>
		</form>
	);
}

export default function StripePay({ publishableKey, clientSecret, bookingId, bookingKey, amountLabel, dark, onPaid }) {
	const [mods, setMods] = useState(null);
	const [loadError, setLoadError] = useState('');

	useEffect(() => {
		let alive = true;
		Promise.all([import(/* webpackChunkName: "stripe" */ '@stripe/stripe-js'), import(/* webpackChunkName: "stripe" */ '@stripe/react-stripe-js')])
			.then(([stripeJs, reactStripe]) => {
				if (!alive) return;
				setMods({
					stripePromise: stripeJs.loadStripe(publishableKey),
					Elements: reactStripe.Elements,
					PaymentElement: reactStripe.PaymentElement,
					useStripe: reactStripe.useStripe,
					useElements: reactStripe.useElements,
				});
			})
			.catch(() => alive && setLoadError(__('The card form could not be loaded. Please check your connection and try again.', 'pointly-booking')));
		return () => {
			alive = false;
		};
	}, [publishableKey]);

	if (loadError) return <Alert tone="danger">{loadError}</Alert>;
	if (!mods || !clientSecret) {
		return (
			<SkeletonGroup label={__('Loading secure card form…', 'pointly-booking')} className="bp-bw-stripe__loading">
				<Skeleton height={44} />
				<Skeleton height={44} />
				<Skeleton height={48} />
			</SkeletonGroup>
		);
	}

	const brand = front().brand || {};
	const appearance = {
		theme: dark ? 'night' : 'stripe',
		variables: {
			...(brand.primary ? { colorPrimary: brand.primary } : {}),
			...(brand.radius !== null && brand.radius !== undefined ? { borderRadius: `${Number(brand.radius) || 0}px` } : {}),
		},
	};

	return (
		<div className="bp-bw-stripe">
			<mods.Elements stripe={mods.stripePromise} options={{ clientSecret, appearance }}>
				<PayForm mods={mods} bookingId={bookingId} bookingKey={bookingKey} amountLabel={amountLabel} onPaid={onPaid} />
			</mods.Elements>
			<p className="bp-bw-hint bp-bw-hint--center">{__('Payments are processed securely by Stripe. Card details never touch our server.', 'pointly-booking')}</p>
		</div>
	);
}
