"use strict";

const path = require("path");

module.exports = ({ outputDirectory }) =>
	class Worker {
		constructor(resource, options = {}) {
			const isFileURL = /^file:/i.test(resource);
			const isBlobURL = /^blob:/i.test(resource);

			if (!isFileURL && !isBlobURL) {
				expect(resource.origin).toBe("https://test.cases");
				expect(resource.pathname.startsWith("/path/")).toBe(true);
			}

			this.url = resource;
			const file = isFileURL
				? resource
				: path.resolve(
						outputDirectory,
						isBlobURL
							? options.originalURL.pathname.slice(6)
							: resource.pathname.slice(6)
					);

			const workerBootstrap = `
const { parentPort } = require("worker_threads");
const { URL, fileURLToPath } = require("url");
const path = require("path");
const fs = require("fs");
global.self = global;
self.URL = URL;
self.location = new URL(${JSON.stringify(
				isBlobURL
					? resource.toString().replace("nodedata:", "https://test.cases/path/")
					: resource.toString()
			)});
const urlToPath = url => {
  if (/^file:/i.test(url)) return fileURLToPath(url);
	if (url.startsWith("https://test.cases/path/")) url = url.slice(24);
	return path.resolve(${JSON.stringify(outputDirectory)}, \`./\${url}\`);
};
self.importScripts = url => {
	${
		options.type === "module"
			? 'throw new Error("importScripts is not supported in module workers")'
			: "require(urlToPath(url))"
	};
};
self.fetch = async url => {
	try {
		const buffer = await new Promise((resolve, reject) =>
			fs.readFile(urlToPath(url), (err, b) =>
				err ? reject(err) : resolve(b)
			)
		);
		return {
		  headers: { get(name) { } },
			status: 200,
			ok: true,
			arrayBuffer() { return buffer; },
			json: async () => JSON.parse(buffer.toString("utf-8"))
		};
	} catch(err) {
		if(err.code === "ENOENT") {
			return {
				status: 404,
				ok: false
			};
		}
		throw err;
	}
};

self.postMessage = data => {
	parentPort.postMessage(data);
};
if (${options.type === "module"}) {
	import(${JSON.stringify(file)}).then(() => {
		parentPort.on("message", data => {
			if(self.onmessage) self.onmessage({
				data
			});
		});
	});
} else {
	parentPort.on("message", data => {
		if(self.onmessage) self.onmessage({
			data
		});
	});
	require(${JSON.stringify(file)});
}
`;
			this.worker = new (require("worker_threads").Worker)(workerBootstrap, {
				eval: true
			});

			this._onmessage = undefined;
		}

		// eslint-disable-next-line accessor-pairs
		set onmessage(value) {
			if (this._onmessage) this.worker.off("message", this._onmessage);
			this.worker.on(
				"message",
				(this._onmessage = (data) => {
					value({
						data
					});
				})
			);
		}

		postMessage(data) {
			this.worker.postMessage(data);
		}

		terminate() {
			return this.worker.terminate();
		}
	};
