import { exportInvoicePdfFromSlug } from '../../woocommerce/invoice/exportInvoicePdf';

interface CheckoutInvoiceConfig {
	restUrl: string;
	nonce: string;
}

declare global {
	interface Window {
		__PDFDRAFT_CHECKOUT_INVOICE__?: CheckoutInvoiceConfig;
		jQuery?: any;
	}
}

function getConfig(): CheckoutInvoiceConfig {
	return window.__PDFDRAFT_CHECKOUT_INVOICE__ ?? { restUrl: '', nonce: '' };
}

// ---- Order ID / key resolution ----------------------------------------------

function extractOrderId( redirectUrl: string ): number {
	try {
		const match = new URL( redirectUrl ).pathname.match( /order-received\/(\d+)/ );
		return match ? parseInt( match[ 1 ]!, 10 ) : 0;
	} catch {
		return 0;
	}
}

function extractOrderKey( redirectUrl: string ): string {
	try {
		return new URL( redirectUrl ).searchParams.get( 'key' ) ?? '';
	} catch {
		return '';
	}
}

// ---- API calls --------------------------------------------------------------

async function fetchContext( orderId: number, orderKey: string, nonce: string, restUrl: string ) {
	const res = await fetch(
		`${ restUrl }/orders/${ orderId }/invoice-context/checkout?order_key=${ encodeURIComponent( orderKey ) }`,
		{ credentials: 'include', headers: { 'X-WP-Nonce': nonce } },
	);
	if ( ! res.ok ) throw new Error( `context ${ res.status }` );
	return res.json() as Promise<{
		designSlug: string;
		mergeTags: Record<string, unknown>;
		emailPending: boolean;
		isGenerated: boolean;
		uploadToken: string;
	}>;
}

async function uploadPdf( orderId: number, blob: Blob, orderKey: string, nonce: string, restUrl: string, uploadToken: string ): Promise<void> {
	const bytes = new Uint8Array( await blob.arrayBuffer() );
	let binary = '';
	for ( let i = 0; i < bytes.length; i++ ) binary += String.fromCharCode( bytes[ i ]! );

	const res = await fetch( `${ restUrl }/orders/${ orderId }/invoice/checkout`, {
		method:      'POST',
		credentials: 'include',
		headers:     { 'X-WP-Nonce': nonce, 'Content-Type': 'application/json' },
		body:        JSON.stringify( { pdf: btoa( binary ), order_key: orderKey, upload_token: uploadToken } ),
	} );
	if ( ! res.ok ) throw new Error( `upload ${ res.status }` );
}

async function sendEmail( orderId: number, orderKey: string, nonce: string, restUrl: string ): Promise<void> {
	const res = await fetch( `${ restUrl }/orders/${ orderId }/send-invoice-email`, {
		method:      'POST',
		credentials: 'include',
		headers:     { 'X-WP-Nonce': nonce, 'Content-Type': 'application/json' },
		body:        JSON.stringify( { order_key: orderKey } ),
	} );
	if ( ! res.ok ) throw new Error( `send-email ${ res.status }` );
}

// ---- Core handler -----------------------------------------------------------

/**
 * Generate and upload the invoice after a successful checkout.
 *
 * Order ID and key are resolved in priority order:
 *   1. knownOrderId / knownOrderKey  — injected by PHP filter or Blocks API response
 *   2. Parsed from redirectUrl       — works for direct gateways (COD, card, etc.)
 *
 * This means redirect gateways (PayPal, Mollie, …) are handled via (1) rather
 * than falling through to the order-received page.
 */
async function handleCheckoutSuccess(
	redirectUrl: string,
	freshNonce?: string,
	knownOrderId?: number,
	knownOrderKey?: string,
): Promise<void> {
	const { restUrl, nonce: configNonce } = getConfig();
	const nonce = freshNonce || configNonce;
	if ( ! restUrl || ! nonce ) return;

	const orderId  = knownOrderId  || extractOrderId( redirectUrl );
	const orderKey = knownOrderKey || extractOrderKey( redirectUrl );
	if ( ! orderId || ! orderKey ) return;

	let ctx: Awaited<ReturnType<typeof fetchContext>>;
	try {
		ctx = await fetchContext( orderId, orderKey, nonce, restUrl );
	} catch {
		return;
	}

	if ( ! ctx.emailPending || ! ctx.designSlug || ctx.isGenerated || ! ctx.uploadToken ) return;

	const designEndpoint = `${ restUrl }/orders/${ orderId }/invoice-design?order_key=${ encodeURIComponent( orderKey ) }`;

	try {
		const { blob } = await exportInvoicePdfFromSlug( ctx.designSlug, ctx.mergeTags, orderId, nonce, designEndpoint );
		await uploadPdf( orderId, blob, orderKey, nonce, restUrl, ctx.uploadToken );
		await sendEmail( orderId, orderKey, nonce, restUrl );
	} catch {
		// generation failed — order-received page fallback will handle it
	}
}

// ---- Interceptors -----------------------------------------------------------

function isClassicCheckoutUrl( url: string, data: unknown ): boolean {
	if ( url.includes( 'wc-ajax=checkout' ) ) return true;
	if ( typeof data === 'string' && data.includes( 'woocommerce_checkout' ) ) return true;
	return false;
}

function isBlocksCheckoutUrl( url: string ): boolean {
	return url.includes( '/wc/store/v1/checkout' ) || url.includes( '/wc/store/checkout' );
}

function resolveUrl( input: RequestInfo | URL ): string {
	if ( typeof input === 'string' ) return input;
	if ( input instanceof URL ) return input.toString();
	return ( input as Request ).url;
}

type ClassicCheckoutResult = {
	result?:    string;
	redirect?:  string;
	order_id?:  number;
	order_key?: string;
};

function interceptClassicCheckout(): void {
	const $ = window.jQuery;
	if ( typeof $ === 'undefined' || typeof $.ajaxPrefilter !== 'function' ) return;

	$.ajaxPrefilter( function ( options: any ) {
		if ( ! isClassicCheckoutUrl( options.url ?? '', options.data ) ) return;

		const origSuccess = options.success;
		options.success   = async function ( code: string | object ) {
			let result: ClassicCheckoutResult | null = null;

			if ( typeof code === 'string' ) {
				try { result = JSON.parse( code ) as ClassicCheckoutResult; } catch { /* not JSON */ }
			} else if ( typeof code === 'object' && code !== null ) {
				result = code as ClassicCheckoutResult;
			}

			if ( result?.result === 'success' && result?.redirect ) {
				// Pass injected order_id / order_key from PHP filter (covers PayPal, Mollie, etc.)
				// URL parsing remains the fallback for direct gateways that don't need the filter.
				await handleCheckoutSuccess(
					result.redirect,
					undefined,
					result.order_id,
					result.order_key,
				);
				window.location.href = result.redirect;
			} else {
				origSuccess?.call( this, code );
			}
		};
	} );
}

function interceptBlocksCheckout(): void {
	const origFetch = window.fetch.bind( window );

	window.fetch = async function ( input: RequestInfo | URL, init?: RequestInit ): Promise<Response> {
		const url = resolveUrl( input );

		if ( ! isBlocksCheckoutUrl( url ) || ( init?.method ?? 'GET' ).toUpperCase() !== 'POST' ) {
			return origFetch( input, init );
		}

		const response   = await origFetch( input, init );
		const freshNonce = response.headers.get( 'X-WP-Nonce' ) ?? undefined;

		let data: Record<string, unknown>;
		try {
			data = await response.json() as Record<string, unknown>;
		} catch {
			return new Response( '{}', { status: response.status, statusText: response.statusText, headers: response.headers } );
		}

		const paymentResult = data?.payment_result as Record<string, unknown> | undefined;
		const paymentStatus = paymentResult?.payment_status as string | undefined;
		const redirectUrl   = paymentResult?.redirect_url as string | undefined;

		// Blocks API always includes order_id and order_key at the top level —
		// use them directly so redirect gateways work without URL parsing.
		const orderId  = typeof data?.order_id  === 'number' ? data.order_id  as number : undefined;
		const orderKey = typeof data?.order_key === 'string' ? data.order_key as string : undefined;

		if ( ( paymentStatus === 'success' || paymentStatus === 'pending' ) && redirectUrl ) {
			await handleCheckoutSuccess( redirectUrl, freshNonce, orderId, orderKey );
		}

		return new Response( JSON.stringify( data ), {
			status:     response.status,
			statusText: response.statusText,
			headers:    response.headers,
		} );
	};
}

interceptBlocksCheckout();
interceptClassicCheckout();
