/**
 * These triple-slash directives defines dependencies to various declaration files that will be
 * loaded when a user imports the StudioCMS plugin in their Astro configuration file. These
 * directives must be first at the top of the file and can only be preceded by this comment.
 */
/// <reference types="./virtual.d.ts" preserve="true" />
/// <reference types="studiocms/v/types" />

import { addVirtualImports } from '@withstudiocms/internal_helpers/astro-integration';
import type { AstroIntegration } from 'astro';
import { Schema } from 'effect';
import { definePlugin } from 'studiocms/plugins';
import type { StudioCMSPluginDef } from 'studiocms/schemas';
import { shared } from './lib/shared.js';
import { MarkdownOptionsSchema, type MarkdownSchemaOptions } from './types.js';

function resolve(path: string) {
	return new URL(path, import.meta.url).toString();
}

const packageIdentifier = '@studiocms/md';

export function internalMarkdownIntegration(options: MarkdownSchemaOptions = {}): AstroIntegration {
	// Resolve the path to the internal renderer
	const internalRenderer = resolve('./lib/markdown-prerender.js');

	// Resolve the options and set defaults if not provided
	const parseResult = Schema.decodeEither(MarkdownOptionsSchema)(options);
	/* v8 ignore start */
	if (parseResult._tag === 'Left') {
		throw new Error(`Invalid markdown options: ${parseResult.left.message}`);
	}
	/* v8 ignore stop */
	const resolvedOptions = parseResult.right;

	// Define the resolved Callout Theme
	let resolvedCalloutTheme: string | undefined;

	// Resolve the callout theme based on the user's configuration
	if (typeof resolvedOptions.callouts === 'string') {
		resolvedCalloutTheme = resolve(`./styles/md-remark-callouts/${resolvedOptions.callouts}.css`);
	} else {
		resolvedCalloutTheme = undefined;
	}
	return {
		name: packageIdentifier,
		hooks: {
			'astro:config:setup': (params) => {
				// Add the virtual imports for the MDX renderer
				addVirtualImports(params, {
					name: packageIdentifier,
					imports: {
						'studiocms:md/config': `
							export const config = ${JSON.stringify(resolvedOptions)};
							export default config;
						`,
						'studiocms:md/pre-render': `
							export { preRender } from '${internalRenderer}';
							`,
						'studiocms:md/styles': `
							import '${resolve('./styles/md-remark-headings.css')}';
							${resolvedCalloutTheme ? `import '${resolvedCalloutTheme}';` : ''}
						`,
					},
				});

				// Inject the StudioCMS-specific styles for the markdown renderer
				params.injectScript('page-ssr', `import "studiocms:md/styles";`);
			},
			'astro:config:done': ({ config }) => {
				// Store the resolved options in the shared context for the renderer
				shared.mdConfig = resolvedOptions;
				shared.astroMDRemark = config.markdown;
			},
		},
	};
}

/**
 * Creates a StudioCMS plugin for Markdown page types.
 *
 * This plugin configures StudioCMS to support Markdown content, including rendering and editing components,
 * integration with Astro, and optional callout themes. It resolves user-provided options, sets up virtual imports,
 * and injects necessary styles and scripts for Markdown rendering.
 *
 * @param options - Optional configuration for Markdown schema and rendering behavior.
 * @returns A StudioCMSPlugin instance configured for Markdown support.
 *
 * @remarks
 * - Supports custom callout themes if enabled in options.
 * - Integrates with Astro via virtual imports and injected scripts.
 * - Stores resolved options and Astro markdown configuration in shared context.
 *
 * @example
 * ```typescript
 * import { studiocmsMD } from '@studiocms/md';
 * const plugin = studiocmsMD({ flavor: 'studiocms', callouts: 'obsidian' });
 * ```
 */
export function studiocmsMD(options: MarkdownSchemaOptions = {}): StudioCMSPluginDef {
	// Resolve the options and set defaults if not provided
	const parseResult = Schema.decodeEither(MarkdownOptionsSchema)(options);
	/* v8 ignore start */
	if (parseResult._tag === 'Left') {
		throw new Error(`Invalid markdown options: ${parseResult.left.message}`);
	}
	/* v8 ignore stop */
	const resolvedOptions = parseResult.right;
	/**
	 * Defines the configuration for the Markdown page type in StudioCMS.
	 *
	 * @property identifier - A unique string identifying the Markdown page type.
	 * @property label - The display label for the Markdown page type.
	 * @property rendererComponent - Path to the Astro component responsible for rendering Markdown content.
	 * @property pageContentComponent - Path to the Astro component used for editing Markdown content.
	 */
	const markdownPageType = {
		identifier: 'studiocms/markdown',
		label: 'Markdown',
		rendererComponent: resolve('./components/render.js'),
		pageContentComponent: resolve('./components/markdown-editor.astro'),
	};

	// Return the plugin configuration
	return definePlugin({
		identifier: packageIdentifier,
		name: 'StudioCMS Markdown',
		hooks: {
			'studiocms:astro-config': async ({ addIntegrations }) => {
				addIntegrations(internalMarkdownIntegration(resolvedOptions));
			},
			'studiocms:rendering': async ({ setRendering }) => {
				setRendering({
					pageTypes: [markdownPageType],
				});
			},
		},
	});
}

export default studiocmsMD;
