/**
 * External dependencies
 */
import debugFactory from 'debug';
/**
 * Internal dependencies
 */
import { BATCH_SIZE, DEBOUNCE_DELAY } from './constants';
import type { ApiEvent, ApiFetchResponse } from './types/shared';

const debug = debugFactory( 'wc-analytics:api-client' );

/**
 * API Client for sending analytics events to the WordPress REST API
 */
export class ApiClient {
	private eventQueue: ApiEvent[] = [];
	private debounceTimer: number | null = null;
	private isInitialized: boolean = false;
	/**
	 * Initialize the API client
	 */
	init = (): void => {
		if ( this.isInitialized ) {
			return;
		}

		debug( 'API client initialized' );
		// Send any pending events when the page is about to unload
		window.addEventListener( 'beforeunload', this.flush );
		window.addEventListener( 'pagehide', this.flush );

		this.isInitialized = true;
	};

	/**
	 * Add an event to the queue for batch sending
	 *
	 * @param eventName  - The name of the event.
	 * @param properties - The properties of the event.
	 */
	addEvent = ( eventName: string, properties: Record< string, unknown > = {} ): void => {
		if ( ! this.isInitialized ) {
			debug( 'API client not initialized, skipping event: %s', eventName );
			return;
		}

		debug( 'Recording event via API: "%s" with props %o', eventName, properties );
		const apiEvent: ApiEvent = {
			event_name: eventName,
			properties,
		};

		this.eventQueue.push( apiEvent );
		debug( 'Event added to queue: %s (queue size: %d)', eventName, this.eventQueue.length );

		// Schedule debounced send
		this.debouncedSend();

		// If queue is full, send immediately
		if ( this.eventQueue.length >= BATCH_SIZE ) {
			this.flush();
		}
	};

	/**
	 * Debounced send function
	 */
	private debouncedSend = (): void => {
		if ( this.debounceTimer ) {
			clearTimeout( this.debounceTimer );
		}

		this.debounceTimer = window.setTimeout( () => {
			this.flush();
		}, DEBOUNCE_DELAY );
	};

	/**
	 * Send events using the Beacon API for guaranteed delivery
	 *
	 * @param events - The events to send.
	 * @return True if beacon was successfully queued, false otherwise.
	 */
	private sendEventsViaBeacon = ( events: ApiEvent[] ): boolean => {
		// Check if beacon API is available
		if ( typeof navigator === 'undefined' || ! navigator.sendBeacon ) {
			debug( 'Beacon API not available' );
			return false;
		}

		try {
			// Convert events to JSON and create a Blob with correct content type
			const data = JSON.stringify( events );
			const blob = new Blob( [ data ], { type: 'application/json' } );

			// Send via beacon - returns true if successfully queued
			return navigator.sendBeacon( window.wcAnalytics.trackEndpoint, blob );
		} catch ( error ) {
			debug( 'Beacon API failed: %o', error );
			return false;
		}
	};

	/**
	 * Flush all pending events immediately
	 */
	flush = (): void => {
		if ( this.debounceTimer ) {
			clearTimeout( this.debounceTimer );
			this.debounceTimer = null;
		}

		if ( this.eventQueue.length === 0 ) {
			return;
		}

		const eventsToSend = [ ...this.eventQueue ];
		this.eventQueue = [];

		if ( ! window.wcAnalytics?.trackEndpoint ) {
			debug( 'Track endpoint not available' );
			return;
		}

		// Try sending via Beacon API first for guaranteed delivery
		const beaconSuccess = this.sendEventsViaBeacon( eventsToSend );

		if ( beaconSuccess ) {
			debug( 'Sent %d events via Beacon API', eventsToSend.length );
		} else {
			debug( 'Failed to send events via Beacon API, falling back to fetch with keepalive' );
			this.sendEvents( eventsToSend );
		}
	};

	/**
	 * Send events to the API
	 *
	 * @param events - The events to send.
	 */
	private sendEvents = async ( events: ApiEvent[] ): Promise< void > => {
		if ( events.length === 0 ) {
			return;
		}

		try {
			debug( 'Sending %d events to API', events.length );

			const response = await fetch( window.wcAnalytics.trackEndpoint, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json',
				},
				body: JSON.stringify( events ),
				keepalive: true,
				credentials: 'same-origin',
			} );

			if ( ! response.ok ) {
				throw new Error( `HTTP error! status: ${ response.status }` );
			}

			const data: ApiFetchResponse = await response.json();
			debug( 'API response received: %o', data );
		} catch ( error ) {
			debug( 'Failed to send events to API: %o', error );
			// Re-add events to queue for potential retry on next event
			this.eventQueue.unshift( ...events );
		}
	};
}
