import { getGlobalThis } from '@wener/utils';
import { createStore } from 'zustand';
import { mutative } from 'zustand-mutative';
import type { MatomoTracker } from './types';

function setupMatomo({
	global = getGlobalThis(),
	url,
	siteId,
	queue = [],
}: {
	global?: any;
	siteId?: string;
	url?: string;
	queue?: any[];
} = {}) {
	if (!url || !siteId) {
		return;
	}
	const G = global;
	if (typeof G === 'undefined' || G['Matomo']?.initialized) {
		return;
	}
	const _paq = (G._paq = G._paq || queue);
	/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
	_paq.push(['trackPageView']);
	_paq.push(['enableLinkTracking']);
	const u = url;
	_paq.push(['setTrackerUrl', u + 'matomo.php']);
	_paq.push(['setSiteId', siteId]);

	return new Promise((resolve, reject) => {
		const doc = document;
		const ele$ = doc.createElement('script');
		const parent$ = doc.getElementsByTagName('script')[0];
		ele$.onload = () => {
			resolve(window.Matomo);
		};
		ele$.onerror = reject;
		ele$.type = 'text/javascript';
		ele$.async = true;
		ele$.defer = true;
		ele$.src = u + 'matomo.js';
		parent$.parentNode?.insertBefore(ele$, parent$);
	});
}

interface TrackerStoreState {
	tracker: MatomoTracker;
	init: (o: { baseUrl?: string; siteId?: string }) => void;
}

export const TrackerStore = createStore<TrackerStoreState>()(
	mutative((setState, getState, store) => {
		const g: any = getGlobalThis();
		const queue: any[] = [];
		let pending;
		return {
			init: ({ baseUrl, siteId }) => {
				pending ||= setupMatomo({
					url: baseUrl,
					siteId: siteId,
				});
			},
			tracker: createProxyTracker({
				queue,
				invoke: ({ method, args }) => {},
			}),
		} as TrackerStoreState;
	}),
);

function createProxyTracker({
	queue = [],
	invoke,
}: {
	queue?: any[];
	invoke?: (o: { method: string; args: any[]; queue: any[] }) => void | boolean;
}): MatomoTracker {
	return new Proxy(
		{
			_paq: queue,
		},
		{
			get(target, prop) {
				switch (prop) {
					case '_paq':
						return target._paq;
				}
				return (...args: any[]) => {
					switch (prop) {
						case 'push':
							queue.push(...args);
							return;
						default:
							if (typeof prop !== 'string') {
								throw new Error(`Invalid tracker method ${String(prop)}`);
							}
					}
					let skip = false;
					if (invoke) {
						skip = invoke?.({ method: prop, args, queue }) === true;
					}
					if (!skip) {
						queue.push([prop, ...args]);
					}
					// Limit queue size
					if (queue.length > 1000) {
						queue.splice(0, 1000 - queue.length);
					}
				};
			},
		},
	) as any;
}

export function getTracker(): MatomoTracker {
	return TrackerStore.getState().tracker;
}
