/**
 * @jest-environment jsdom
 */
import { render } from '@testing-library/svelte';
import Icon, { loadIcons, iconExists } from '../../';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';

const iconData = {
	body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
	width: 24,
	height: 24,
};

describe('Rendering icon', () => {
	test('rendering icon after loading it', (done) => {
		const prefix = nextPrefix();
		const name = 'render-test';
		const iconName = `@${provider}:${prefix}:${name}`;
		const className = `iconify iconify--${prefix} iconify--${provider}`;
		let onLoadCalled = false;

		mockAPIData({
			type: 'icons',
			provider,
			prefix,
			response: {
				prefix,
				icons: {
					[name]: iconData,
				},
			},
		});

		// Check if icon has been loaded
		expect(iconExists(iconName)).toBe(false);

		// Load icon
		loadIcons([iconName], (loaded, missing, pending) => {
			// Make sure icon has been loaded
			expect(loaded).toMatchObject([
				{
					provider,
					prefix,
					name,
				},
			]);
			expect(missing).toMatchObject([]);
			expect(pending).toMatchObject([]);
			expect(iconExists(iconName)).toBe(true);

			// Render component
			const component = render(Icon, {
				icon: iconName,
				onLoad: (name: string) => {
					expect(name).toBe(iconName);
					expect(onLoadCalled).toBe(false);
					onLoadCalled = true;
				},
			});
			const node = component.container.querySelector('svg')!;
			const html = (node.parentNode as HTMLDivElement).innerHTML;

			// Check HTML
			expect(html).toBe(
				'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="' +
					className +
					'"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
			);

			// Make sure onLoad has been called
			expect(onLoadCalled).toBe(true);

			done();
		});
	});

	test('rendering icon before loading it', (done) => {
		const prefix = nextPrefix();
		const name = 'mock-test';
		const iconName = `@${provider}:${prefix}:${name}`;
		const className = `iconify iconify--${prefix} iconify--${provider}`;
		let onLoadCalled = false;

		mockAPIData({
			type: 'icons',
			provider,
			prefix,
			response: {
				prefix,
				icons: {
					[name]: iconData,
				},
			},
			delay: (next) => {
				// Icon should not have loaded yet
				expect(iconExists(iconName)).toBe(false);

				// onLoad should not have been called yet
				expect(onLoadCalled).toBe(false);

				// Send icon data
				next();

				// Test it again
				expect(iconExists(iconName)).toBe(true);

				// Check if state was changed
				// Wrapped in double setTimeout() because re-render takes 2 ticks
				setTimeout(() => {
					setTimeout(() => {
						const node = component.container.querySelector('svg')!;
						const html = (node.parentNode as HTMLDivElement)
							.innerHTML;

						// Check HTML
						expect(html).toBe(
							'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="test ' +
								className +
								'" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
						);

						// onLoad should have been called
						expect(onLoadCalled).toBe(true);

						done();
					}, 0);
				}, 0);
			},
		});

		// Check if icon has been loaded
		expect(iconExists(iconName)).toBe(false);

		// Render component
		const component = render(Icon, {
			icon: iconName,
			// Also testing simple class
			class: 'test',
			onLoad: (name: string) => {
				expect(name).toBe(iconName);
				expect(onLoadCalled).toBe(false);
				onLoadCalled = true;
			},
		});

		// Should render empty icon
		const html = component.container.innerHTML;
		expect(html).toBe('<div></div>');

		// onLoad should not have been called yet
		expect(onLoadCalled).toBe(false);
	});

	test('missing icon', (done) => {
		const prefix = nextPrefix();
		const name = 'missing-icon';
		const iconName = `@${provider}:${prefix}:${name}`;

		mockAPIData({
			type: 'icons',
			provider,
			prefix,
			response: 404,
			delay: (next) => {
				// Icon should not have loaded yet
				expect(iconExists(iconName)).toBe(false);

				// Send icon data
				next();

				// Test it again
				expect(iconExists(iconName)).toBe(false);

				// Check if state was changed
				// Wrapped in double setTimeout() because re-render takes 2 ticks
				setTimeout(() => {
					setTimeout(() => {
						const html = component.container.innerHTML;
						expect(html).toBe('<div></div>');

						done();
					}, 0);
				}, 0);
			},
		});

		// Check if icon has been loaded
		expect(iconExists(iconName)).toBe(false);

		// Render component
		const component = render(Icon, {
			icon: iconName,
			onLoad: () => {
				throw new Error('onLoad called for empty icon!');
			},
		});

		// Should render empty icon
		const html = component.container.innerHTML;
		expect(html).toBe('<div></div>');
	});
});