import { b } from 'code-red';

/**
 * @param {any} program
 * @param {import('estree').Identifier} name
 * @param {string} banner
 * @param {any} svelte_path
 * @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
 * @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
 * @param {import('estree').ImportDeclaration[]} imports
 * @param {Export[]} module_exports
 * @param {import('estree').ExportNamedDeclaration[]} exports_from
 */
export default function create_module(
	program,
	name,
	banner,
	svelte_path = 'svelte',
	helpers,
	globals,
	imports,
	module_exports,
	exports_from
) {
	const internal_path = `${svelte_path}/internal`;
	helpers.sort((a, b) => (a.name < b.name ? -1 : 1));
	globals.sort((a, b) => (a.name < b.name ? -1 : 1));
	return esm(
		program,
		name,
		banner,
		svelte_path,
		internal_path,
		helpers,
		globals,
		imports,
		module_exports,
		exports_from
	);
}

/**
 * @param {any} source
 * @param {any} svelte_path
 */
function edit_source(source, svelte_path) {
	return source === 'svelte' || source.startsWith('svelte/')
		? source.replace('svelte', svelte_path)
		: source;
}

/**
 * @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
 * @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
 */
function get_internal_globals(globals, helpers) {
	return (
		globals.length > 0 && {
			type: 'VariableDeclaration',
			kind: 'const',
			declarations: [
				{
					type: 'VariableDeclarator',
					id: {
						type: 'ObjectPattern',
						properties: globals.map((g) => ({
							type: 'Property',
							method: false,
							shorthand: false,
							computed: false,
							key: { type: 'Identifier', name: g.name },
							value: g.alias,
							kind: 'init'
						}))
					},
					init: helpers.find(({ name }) => name === 'globals').alias
				}
			]
		}
	);
}

/**
 * @param {any} program
 * @param {import('estree').Identifier} name
 * @param {string} banner
 * @param {string} svelte_path
 * @param {string} internal_path
 * @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
 * @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
 * @param {import('estree').ImportDeclaration[]} imports
 * @param {Export[]} module_exports
 * @param {import('estree').ExportNamedDeclaration[]} exports_from
 */
function esm(
	program,
	name,
	banner,
	svelte_path,
	internal_path,
	helpers,
	globals,
	imports,
	module_exports,
	exports_from
) {
	const import_declaration = {
		type: 'ImportDeclaration',
		specifiers: helpers.map((h) => ({
			type: 'ImportSpecifier',
			local: h.alias,
			imported: { type: 'Identifier', name: h.name }
		})),
		source: { type: 'Literal', value: internal_path }
	};
	const internal_globals = get_internal_globals(globals, helpers);
	// edit user imports

	/** @param {any} node */
	function rewrite_import(node) {
		const value = edit_source(node.source.value, svelte_path);
		if (node.source.value !== value) {
			node.source.value = value;
			node.source.raw = null;
		}
	}
	imports.forEach(rewrite_import);
	exports_from.forEach(rewrite_import);
	const exports = module_exports.length > 0 && {
		type: 'ExportNamedDeclaration',
		specifiers: module_exports.map((x) => ({
			type: 'Specifier',
			local: { type: 'Identifier', name: x.name },
			exported: { type: 'Identifier', name: x.as }
		}))
	};
	program.body = b`
		/* ${banner} */

		${import_declaration}
		${internal_globals}
		${imports}
		${exports_from}

		${program.body}

		export default ${name};
		${exports}
	`;
}

/**
 * @typedef {Object} Export
 * @property {string} name
 * @property {string} as
 */
