/// <reference lib="dom" />
/// <reference lib="dom.iterable" />

/* eslint-disable no-console */
/**
 * Source code is adapted from
 * https://github.com/webpack-contrib/webpack-hot-middleware#readme
 * and rewritten in TypeScript. This file is MIT licensed
 */
import type {HotMiddlewareMessage} from '@remotion/studio-shared';
import {hotMiddlewareOptions, stripAnsi} from '@remotion/studio-shared';
import {processUpdate} from './process-update';

declare global {
	interface Window {
		__webpack_hot_middleware_reporter__: Reporter;
		__remotion_processHmrEvent?: (hmrEvent: HotMiddlewareMessage) => void;
	}
}

type Reporter = ReturnType<typeof createReporter>;

function createReporter() {
	const styles = {
		errors: 'color: #ff0000;',
		warnings: 'color: #999933;',
	};
	let previousProblems: string | null = null;

	function log(type: 'errors' | 'warnings', obj: HotMiddlewareMessage) {
		if (obj.action === 'building') {
			console.log('[Fast Refresh] Building');
			return;
		}

		const newProblems = obj[type]
			.map((msg) => {
				return stripAnsi(msg as string);
			})
			.join('\n');
		if (previousProblems === newProblems) {
			return;
		}

		previousProblems = newProblems;

		const style = styles[type];
		const name = obj.name ? "'" + obj.name + "' " : '';
		const title =
			'[Fast Refresh] bundle ' + name + 'has ' + obj[type].length + ' ' + type;
		// NOTE: console.warn or console.error will print the stack trace
		// which isn't helpful here, so using console.log to escape it.
		if (console.group && console.groupEnd) {
			console.group('%c' + title, style);
			console.log('%c' + newProblems, style);
			console.groupEnd();
		} else {
			console.log(
				'%c' + title + '\n\t%c' + newProblems.replace(/\n/g, '\n\t'),
				style + 'font-weight: bold;',
				style + 'font-weight: normal;',
			);
		}
	}

	return {
		cleanProblemsCache() {
			previousProblems = null;
		},
		problems(type: 'errors' | 'warnings', obj: HotMiddlewareMessage) {
			if (hotMiddlewareOptions.warn) {
				log(type, obj);
			}

			return true;
		},
		success: () => undefined,
	};
}

function processMessage(obj: HotMiddlewareMessage) {
	switch (obj.action) {
		case 'building':
			window.remotion_isBuilding?.();

			break;
		case 'sync':
		case 'built': {
			let applyUpdate = true;
			if (obj.errors.length > 0) {
				if (reporter) reporter.problems('errors', obj);
				applyUpdate = false;
			} else if (obj.warnings.length > 0) {
				if (reporter) {
					const overlayShown = reporter.problems('warnings', obj);
					applyUpdate = overlayShown;
				}
			} else if (reporter) {
				reporter.cleanProblemsCache();
				reporter.success();
			}

			if (applyUpdate) {
				window.remotion_finishedBuilding?.();
				processUpdate(obj.hash, obj.modules, hotMiddlewareOptions);
			}

			break;
		}

		default:
			break;
	}
}

let reporter: Reporter;
const singletonKey = '__webpack_hot_middleware_reporter__' as const;

export const enableHotMiddleware = () => {
	if (typeof window !== 'undefined') {
		if (!window[singletonKey]) {
			window[singletonKey] = createReporter();
		}

		reporter = window[singletonKey];
	}

	window.__remotion_processHmrEvent = (hmrEvent: HotMiddlewareMessage) => {
		processMessage(hmrEvent);
	};

	// Create a standalone SSE listener for HMR events immediately.
	// This is needed because lazy-compiled modules require HMR updates
	// to deliver compiled code, but the React-managed /events SSE
	// (in PreviewServerConnection) only connects after React mounts —
	// which itself depends on lazy modules loading first.
	if (typeof window !== 'undefined' && typeof EventSource !== 'undefined') {
		const source = new EventSource('/events');
		source.addEventListener('message', (event) => {
			try {
				const parsed = JSON.parse(event.data);
				if (parsed.type === 'hmr') {
					processMessage(parsed.hmrEvent);
				}
			} catch {
				// Ignore parse errors
			}
		});
	}
};
