import * as allure from 'allure-js-commons';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { studiocmsMD } from '../src/index.js';
import {
	cleanupGlobalThis,
	createMockMarkdownOptions,
	parentSuiteName,
	sharedTags,
} from './test-utils.js';

const localSuiteName = 'studiocmsMD Plugin Tests';

vi.mock('studiocms/plugins', () => ({
	definePlugin: vi.fn((config) => config),
}));

describe(parentSuiteName, () => {
	beforeEach(() => {
		vi.clearAllMocks();
		cleanupGlobalThis();
	});

	afterEach(() => {
		vi.restoreAllMocks();
	});

	test('Plugin Creation - Validates Plugin Structure and Options', async () => {
		await allure.parentSuite(parentSuiteName);
		await allure.suite(localSuiteName);
		await allure.subSuite('Plugin Creation Tests');
		await allure.tags(...sharedTags);

		await allure.step('Should create plugin with correct metadata and structure', async (ctx) => {
			const plugin = studiocmsMD();
			await ctx.parameter('pluginIdentifier', plugin.identifier);
			await ctx.parameter('pluginName', plugin.name);

			expect(plugin).toBeDefined();
			expect(plugin.identifier).toBe('@studiocms/md');
			expect(plugin.name).toBe('StudioCMS Markdown');
			expect(plugin.hooks).toBeDefined();
		});

		await allure.step('Should create plugin with StudioCMS flavor options', async (ctx) => {
			const options = createMockMarkdownOptions({
				callouts: 'github',
				autoLinkHeadings: false,
				discordSubtext: false,
			});

			const plugin = studiocmsMD(options);
			await ctx.parameter('pluginIdentifier', plugin.identifier);
			await ctx.parameter('pluginName', plugin.name);

			expect(plugin).toBeDefined();
			expect(plugin.identifier).toBe('@studiocms/md');
			expect(plugin.name).toBe('StudioCMS Markdown');
		});

		await allure.step('Should handle StudioCMS flavor with callouts disabled', async (ctx) => {
			const options = createMockMarkdownOptions({
				callouts: false,
			});

			const plugin = studiocmsMD(options);
			await ctx.parameter('pluginIdentifier', plugin.identifier);

			expect(plugin).toBeDefined();
			expect(plugin.identifier).toBe('@studiocms/md');
		});
	});

	['github' as const, 'obsidian' as const, 'vitepress' as const].forEach((theme) => {
		test(`Should handle StudioCMS flavor with '${theme}' callout theme`, async () => {
			await allure.parentSuite(parentSuiteName);
			await allure.suite(localSuiteName);
			await allure.subSuite('Plugin Creation Tests');
			await allure.tags(...sharedTags);

			await allure.step(`Creating plugin with callout theme: ${theme}`, async (ctx) => {
				await ctx.parameter('calloutTheme', theme);
				const options = createMockMarkdownOptions({
					callouts: theme,
				});

				const plugin = studiocmsMD(options);

				expect(plugin).toBeDefined();
				expect(plugin.identifier).toBe('@studiocms/md');
			});
		});
	});

	test('Plugin Hooks - Validates hook definitions', async () => {
		await allure.parentSuite(parentSuiteName);
		await allure.suite(localSuiteName);
		await allure.subSuite('Plugin Hooks Tests');
		await allure.tags(...sharedTags);

		await allure.step('Should have defined hooks for Astro config and setup', async (ctx) => {
			const plugin = studiocmsMD();
			await ctx.parameter('definedHooks', String(Object.keys(plugin.hooks || {})));

			expect(plugin.hooks).toBeDefined();
			expect(plugin.hooks['studiocms:astro-config']).toBeDefined();
			expect(plugin.hooks['studiocms:rendering']).toBeDefined();
		});

		await allure.step(
			'Should call studiocms:astro-config hook with addIntegrations',
			async (ctx) => {
				const plugin: ReturnType<typeof studiocmsMD> = studiocmsMD();
				const mockAddIntegrations = vi.fn();

				const hook = plugin.hooks['studiocms:astro-config'];
				if (!hook) throw new Error('Hook not found');
				// @ts-expect-error -- ignore
				hook({ addIntegrations: mockAddIntegrations });

				await ctx.parameter('calledWith', String(mockAddIntegrations.mock.calls[0]?.[0]));

				expect(mockAddIntegrations).toHaveBeenCalledWith({
					name: '@studiocms/md',
					hooks: {
						'astro:config:setup': expect.any(Function),
						'astro:config:done': expect.any(Function),
					},
				});
			}
		);

		await allure.step(
			'Should store resolved options in shared context on astro:config:done',
			async (ctx) => {
				const customOptions = {
					callouts: 'vitepress' as const,
					autoLinkHeadings: false,
					discordSubtext: false,
					sanitize: {},
				};

				const plugin: ReturnType<typeof studiocmsMD> = studiocmsMD(customOptions);
				const mockConfig = { markdown: { remarkPlugins: [] } };
				const mockAddIntegrations = vi.fn();

				const hook = plugin.hooks['studiocms:astro-config'];
				if (!hook) throw new Error('Hook not found');
				// @ts-expect-error -- ignore
				hook({ addIntegrations: mockAddIntegrations });

				// Get the integration that was added
				const integrationArg = mockAddIntegrations.mock.calls[0]?.[0];
				if (!integrationArg) throw new Error('Integration not found');

				// Call the astro:config:done hook
				const doneHook = integrationArg.hooks['astro:config:done'];
				doneHook({ config: mockConfig });

				// Verify the shared context was updated
				const { shared } = await import('../src/lib/shared.js');
				await ctx.parameter('sharedMdConfig', JSON.stringify(shared.mdConfig));
				await ctx.parameter('sharedAstroMDRemark', JSON.stringify(shared.astroMDRemark));

				expect(shared.mdConfig).toEqual(customOptions);
				expect(shared.astroMDRemark).toEqual(mockConfig.markdown);
			}
		);
	});
});
