import { RenderResultPluginAsset } from '@joplin/renderer/types';

type PluginAssetRecord = {
	element: HTMLElement;
};
const pluginAssetsAdded_: Record<string, PluginAssetRecord> = {};

// Note that this function keeps track of what's been added so as not to
// add the same CSS files multiple times.
//
// Shared with app-desktop/gui-note-viewer.
//
// TODO: If possible, refactor such that this function is not duplicated.
const addPluginAssets = (assets: RenderResultPluginAsset[]) => {
	if (!assets) return;

	const pluginAssetsContainer = document.getElementById('joplin-container-pluginAssetsContainer');

	const processedAssetIds = [];

	for (let i = 0; i < assets.length; i++) {
		const asset = assets[i];

		// # and ? can be used in valid paths and shouldn't be treated as the start of a query or fragment
		const encodedPath = asset.path
			.replace(/#/g, '%23')
			.replace(/\?/g, '%3F');

		const assetId = asset.name ? asset.name : encodedPath;

		processedAssetIds.push(assetId);

		if (pluginAssetsAdded_[assetId]) continue;

		let element = null;

		if (asset.mime === 'application/javascript') {
			element = document.createElement('script');
			element.src = encodedPath;
			pluginAssetsContainer.appendChild(element);
		} else if (asset.mime === 'text/css') {
			element = document.createElement('link');
			element.rel = 'stylesheet';
			element.href = encodedPath;
			pluginAssetsContainer.appendChild(element);
		}

		pluginAssetsAdded_[assetId] = {
			element,
		};
	}

	// Once we have added the relevant assets, we also remove those that
	// are no longer needed. It's necessary in particular for the CSS
	// generated by noteStyle - if we don't remove it, we might end up
	// with two or more stylesheet and that will create conflicts.
	//
	// It was happening for example when automatically switching from
	// light to dark theme, and then back to light theme - in that case
	// the viewer would remain dark because it would use the dark
	// stylesheet that would still be in the DOM.
	for (const [assetId, asset] of Object.entries(pluginAssetsAdded_)) {
		if (!processedAssetIds.includes(assetId)) {
			try {
				asset.element.remove();
			} catch (error) {
				// We don't throw an exception but we log it since
				// it shouldn't happen
				console.warn('Tried to remove an asset but got an error', error);
			}
			pluginAssetsAdded_[assetId] = null;
		}
	}
};

export default addPluginAssets;
