import fs from 'node:fs';
import createPathResolver from '@withstudiocms/internal_helpers/pathResolver';
import type { StudioCMSConfig } from '../schemas/index.js';

const { resolve } = createPathResolver(import.meta.url);

/**
 * Builds a string representing a default export of the provided module object.
 *
 * @param mod - The module object to be stringified and exported as default.
 * @returns A string containing a TypeScript/JavaScript default export statement with the serialized module.
 */
// biome-ignore lint/suspicious/noExplicitAny: Match the type input of JSON.stringify
export const buildDefaultOnlyVirtual = (mod: any): string =>
	`export default ${JSON.stringify(mod)};`;

/**
 * Builds a string containing multiple named ES module exports from a record of key-value pairs.
 *
 * Each key in the input object becomes the exported constant's name, and each value is stringified
 * and assigned to the corresponding export.
 *
 * @param items - An object where each key-value pair represents the name and value of an export.
 * @returns A string containing multiple export statements, one for each entry in the input object.
 *
 * @example
 * ```typescript
 * const exports = buildNamedMultiExportVirtual({ foo: "bar", baz: "qux" });
 * // exports:
 * // export const foo = "bar";
 * // export const baz = "qux";
 * ```
 */
export const buildNamedMultiExportVirtual = (items: Record<string, string>) =>
	Object.entries(items)
		.map(([key, val]) => `export const ${key} = ${JSON.stringify(val)};`)
		.join('\n');

/**
 * Builds the virtual configuration file content by injecting the provided StudioCMS options
 * into a configuration stub template.
 *
 * @param options - The configuration options to be injected into the stub template.
 * @returns The resulting configuration file content as a string with the options embedded.
 */
export const buildVirtualConfig = (options: StudioCMSConfig): string =>
	fs
		.readFileSync(resolve('./stubs/config.stub.js'), 'utf-8')
		.replace(/\$\$options\$\$/g, JSON.stringify(options));

/**
 * Factory function to build utilities for generating virtual module code strings.
 *
 * @param resolve - A function that resolves a list of path segments into a string path.
 * @returns An object containing utilities for generating virtual module code:
 * - `dynamicVirtual`: Generates export statements for a list of module paths.
 * - `ambientScripts`: Generates import statements for a list of module paths (for side effects).
 * - `namedVirtual`: Generates code to re-export a named export (and optionally as default) from a module.
 * - `astroComponentVirtual`: Generates export statements for Astro components with custom names.
 * - `dynamicWithAstroVirtual`: Generates combined exports for dynamic modules and Astro components.
 */
export const VirtualModuleBuilder = (resolve: (...path: Array<string>) => string) => {
	/**
	 * Generates export statements for a list of module paths.
	 */
	const dynamicVirtual = (items: Array<string>): string =>
		items.map((item) => `export * from ${JSON.stringify(resolve(item))};`).join('\n');

	/**
	 * Generates import statements for a list of module paths (for side effects).
	 */
	const ambientScripts = (items: Array<string>): string =>
		items.map((item) => `import '${resolve(item)}';`).join('\n');

	/**
	 * Generates code to re-export a named export (and optionally as default) from a module.
	 */
	const namedVirtual = ({
		namedExport,
		path,
		exportDefault,
	}: {
		namedExport: string;
		path: string;
		exportDefault?: boolean;
	}): string => `
import { ${namedExport} } from ${JSON.stringify(resolve(path))};
export { ${namedExport} };
${exportDefault ? `export default ${namedExport};` : ''}
`;

	/**
	 * Generates export statements for Astro components with custom names.
	 */
	const astroComponentVirtual = (items: Record<string, string>): string =>
		Object.entries(items)
			.map(([key, val]) => `export { default as ${key} } from ${JSON.stringify(resolve(val))}`)
			.join('\n');

	/**
	 * Generates combined exports for dynamic modules and Astro components.
	 */
	const dynamicWithAstroVirtual = ({
		dynamicExports,
		astroComponents,
	}: {
		dynamicExports: Array<string>;
		astroComponents: Record<string, string>;
	}): string => [dynamicVirtual(dynamicExports), astroComponentVirtual(astroComponents)].join('\n');

	return {
		dynamicVirtual,
		ambientScripts,
		namedVirtual,
		astroComponentVirtual,
		dynamicWithAstroVirtual,
		buildDefaultOnlyVirtual,
		buildNamedMultiExportVirtual,
		buildVirtualConfig,
	};
};
