/**
 * BraintreePayment: fetches client token, dynamically loads braintree-web-drop-in,
 * mounts the drop-in UI, handles 3D Secure and payment submission.
 */
import { useState, useCallback, useEffect, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Button, Flex, Notice } from '@wordpress/components';
import { Spinner } from '@woocommerce/components';
import apiFetch from '@wordpress/api-fetch';
import type {
	PaymentProvider,
	BraintreeTokenResponse,
	GenericPaymentResponse,
	GenericErrorResponse,
} from '../../../types';
import type { DropinInstance } from 'braintree-web-drop-in';
import { PaymentOrderResponse } from '../../../types/payment';
import { addBreadcrumb, captureException } from '../../sentry';

let BraintreeWebDropIn: typeof import('braintree-web-drop-in').default | null = null;

interface BraintreePaymentProps {
	paymentOrder: PaymentOrderResponse;
	providers: PaymentProvider[];
	onSuccess: (response: GenericPaymentResponse, paymentOrder: PaymentOrderResponse) => void;
}


export default function BraintreePayment({
	paymentOrder,
	providers,
	onSuccess,
}: BraintreePaymentProps) {
	const starting = useRef<boolean>(false);
	const braintreeInstance = useRef<DropinInstance | null>(null);
	const [error, setError] = useState<string | null>(null);
	const [paying, setPaying] = useState(false);
	const [showButton, setShowButton] = useState(false);
	const [tokenLoading, setTokenLoading] = useState(true);
	const [tokenData, setTokenData] = useState<BraintreeTokenResponse | null>(null);
	const [success, setSuccess] = useState(false);

	const { orderId, hash } = paymentOrder.orderResult;
	const refs = paymentOrder.order?.items?.map(
		(item) => item.ref,
	) ?? 'woo-payment';
	
	// Fetch Braintree client token
	useEffect(() => {
		let cancelled = false;

		const fetchToken = async () => {
			setTokenLoading(true);
			try {
				const data = (await apiFetch({
					path: '/parcel2go-shipping/v1/payment/braintree/token',
					method: 'POST',
					data: { orderId, hash },
				})) as {success: boolean; result: BraintreeTokenResponse; validationErrors: GenericErrorResponse[] | null};
				if (!cancelled && data.success && data.result) setTokenData(data.result);
			} catch (err: unknown) {
				captureException(err instanceof Error ? err : new Error(String(err)));
				if (!cancelled) {
					setError(String((err as { message: string }).message));
				} else {
					setError(__('Failed to load payment system. Please try again', 'parcel2go-shipping'));
				}
			} finally {
				if (!cancelled) setTokenLoading(false);
			}
		};

		fetchToken();
		return () => {
			cancelled = true;
		};
	}, [orderId, hash]);

	// Initialize Braintree drop-in once token is available
	useEffect(() => {
		if (tokenLoading || !tokenData?.clientToken || braintreeInstance.current || starting.current) {
			return;
		}

		starting.current = true;

		const loadBraintree = async () => {
			if (!BraintreeWebDropIn) {
				BraintreeWebDropIn = (await import('braintree-web-drop-in')).default;
			}

			BraintreeWebDropIn.create(
				{
					authorization: tokenData.clientToken,
					container: '#p2g-dropin-container',
					dataCollector: true,
					threeDSecure: true,
					card: providers.some((p) => p.type === 'creditcard')
						? { cardholderName: { required: true } }
						: undefined,
					googlePay:
						tokenData.metadata?.googlePay?.enabled &&
						tokenData.metadata?.googlePay?.merchantId &&
						providers.some((p) => p.type === 'googlepay')
							? {
									merchantId: tokenData.metadata.googlePay.merchantId,
									googlePayVersion: 2,
									transactionInfo: {
										currencyCode: tokenData.currency,
										totalPriceStatus: 'FINAL',
										totalPrice: (tokenData.totalInUnits / 100).toFixed(2),
										transactionId: orderId || undefined,
									},
								}
							: undefined,
					applePay:
						tokenData.metadata?.applePay?.enabled &&
						providers.some((p) => p.type === 'applepay')
							? {
									displayName: 'Parcel2Go',
									paymentRequest: {
										countryCode: tokenData.billingAddress?.iso2Code,
										merchantCapabilities: ['supports3DS'],
										supportedNetworks: ['visa', 'masterCard'],
										currencyCode: tokenData.currency,
										total: {
											amount: (tokenData.totalInUnits / 100).toFixed(2),
											label: 'Total',
										},
										requiredBillingContactFields: ['postalAddress'],
									},
								}
							: undefined,
					paypal: providers.some((p) => p.type === 'paypal')
						? {
								flow: 'checkout',
								amount: tokenData.totalInUnits / 100,
								currency: tokenData.currency,
							}
						: undefined,
				},
				(createErr, instance) => {
					starting.current = false;

					if (createErr) {
						setError(__('Failed to initialize payment form.', 'parcel2go-shipping'));
						captureException(createErr instanceof Error ? createErr : new Error(String(createErr)));
						addBreadcrumb('Braintree create error', {createErr: createErr}, 'payment');
						return;
					}

					setError(null);
					braintreeInstance.current = instance || null;

					if (instance?.isPaymentMethodRequestable()) {
						setShowButton(true);
					}

					instance?.on('paymentMethodRequestable', (event) => {
						if (event?.type === 'CreditCard' || event?.paymentMethodIsSelected) {
							setShowButton(true);
						}
					});

					instance?.on('noPaymentMethodRequestable', () => {
						setShowButton(false);
					});
				}
			);
		};

		loadBraintree().catch((loadErr) => {
			captureException(loadErr instanceof Error ? loadErr : new Error(String(loadErr)));
			addBreadcrumb('Braintree load error', {loadErr: loadErr}, 'payment');
			setError(__('Failed to load payment system. Please try again', 'parcel2go-shipping'));
			starting.current = false;
		});
	}, [tokenData, tokenLoading, orderId, providers]);

	// Teardown on unmount
	useEffect(() => {
		return () => {
			braintreeInstance.current?.teardown();
			braintreeInstance.current = null;
		};
	}, []);

	const handlePayment = useCallback(async () => {
		if (!braintreeInstance.current || !tokenData) return;

		setPaying(true);
		const payload = {
			amount: (tokenData.totalInUnits / 100).toFixed(2),
			mobilePhoneNumber: tokenData.billingAddress?.telephone,
			email: tokenData.billingAddress?.email,
			billingAddress: {
				givenName: tokenData.billingAddress?.contactName.split(' ')[0],
				surname: tokenData.billingAddress?.contactName.split(' ')[1],
				phoneNumber: tokenData.billingAddress?.telephone,
				streetAddress: tokenData.billingAddress.street,
				locality: tokenData.billingAddress?.town,
				countryCodeAlpha2: tokenData.billingAddress?.iso2Code,
			},
		};

		braintreeInstance.current.requestPaymentMethod(
			{
				threeDSecure: payload,
			},
			async (err, payload) => {
				try {
					if (err) {
						captureException(err instanceof Error ? err : new Error(String(err)));
						addBreadcrumb('Braintree payment error', {err: err}, 'payment');
						const errMsg = err.message || __('Payment verification failed. Please try again or use a different payment method.', 'parcel2go-shipping');
						setError(errMsg);
						setPaying(false);
						return;
					}

					const response = (await apiFetch({
						path: '/parcel2go-shipping/v1/payment/braintree',
						method: 'POST',
						data: {
							orderId,
							hash,
							nonce: payload.nonce,
							deviceData: payload.deviceData,
							refs,
						},
					})) as {success: boolean; result: GenericPaymentResponse; validationErrors: GenericErrorResponse[] | null};

					if (response.success && response.result && response.result.completeHash) {
						setError(null);
						setSuccess(true);
						onSuccess({
							orderId: response.result.orderId ?? orderId,
							success: response.success,
							error: null,
							completeHash: response.result.completeHash,
						}, paymentOrder);
					} else if (response.validationErrors?.length > 0) {
						setError(response.validationErrors?.[0]?.detail || '');
					} else {
						setError(__('An unexpected error occurred while processing your payment. Please try again.', 'parcel2go-shipping'));
						captureException(new Error('An unexpected error occurred while processing your payment. Please try again.'));
						addBreadcrumb('Braintree payment error', {error: 'An unexpected error occurred while processing your payment. Please try again.'}, 'payment');
					}
				} catch (submitErr: unknown) {
					captureException(submitErr instanceof Error ? submitErr : new Error(String(submitErr)));
					const msg = formatPaymentErrorMessage(submitErr);
				setError(msg);
				} finally {
					setPaying(false);
				}
			}
		);
	}, [tokenData, orderId, hash, onSuccess]);

	if (tokenLoading) {
		return (
			<Flex direction="column" gap={3} align="center" style={{ padding: 16 }}>
				<Spinner />
			</Flex>
		);
	}

	return (
		<Flex direction="column" gap={3}>
			{error && (
				<Notice status="error" isDismissible={false}>
					{error}
				</Notice>
			)}

			{success && (
				<Notice status="success" isDismissible={false}>
				<Flex direction="column" gap={3} align="center" style={{ padding: 16 }}>
					<Spinner />
					<p style={{ color: '#757575', textAlign: 'center', margin: 0 }}>
						{__('Payment successful. Saving booking details to your order…', 'parcel2go-shipping')}
					</p>
				</Flex>
				</Notice>
			)}

			{starting.current && (
				<Flex direction="column" gap={3} align="center" style={{ padding: 16 }}>
					<Spinner />
				</Flex>
			)}

			<div
				id="p2g-dropin-wrapper"
				style={{ visibility: paying ? 'hidden' : 'visible' }}
			>
				<div id="p2g-dropin-container" />
			</div>

			{showButton && !paying && (
				<Button variant="primary" onClick={handlePayment} disabled={paying}>
					{__('Pay', 'parcel2go-shipping')}
				</Button>
			)}
		</Flex>
	);
}


type ApiError = {
	code?: string;
	data?: {
		status?: number;
	};
	message?: string;
  };
  
function formatPaymentErrorMessage(error:ApiError): string {
	if (!error || typeof error !== "object") {
	  return "Something went wrong. Please try again.";
	}
  
	const statusMessages: Record<number, string> = {
	  400: "Invalid request. Please check your input.",
	  401: "You are not authorised. Please log in again.",
	  403: "You do not have permission to perform this action.",
	  404: "The requested resource was not found.",
	  409: "There was a conflict with your request.",
	  422: "Some fields are invalid. Please review and try again.",
	  500: "Something went wrong on our side. Please try again later.",
	  503: "The service is temporarily unavailable. Please try again later.",
	};
  
	if(error.data?.status && statusMessages[error.data.status]){
		return statusMessages[error.data.status];
	}

	return "Unexpected error. Please contact support if this continues.";
}