import { CONFIG_LOCALSTORAGE_KEY, STORAGE_APP_NAME } from '$lib/constants';
import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';

// node env unit project has no DOM, install a minimal localStorage backed by a Map
beforeAll(() => {
	const store = new Map<string, string>();
	const polyfill: Storage = {
		clear: () => store.clear(),
		getItem: (k) => (store.has(k) ? store.get(k)! : null),
		key: (i) => Array.from(store.keys())[i] ?? null,
		get length() {
			return store.size;
		},
		removeItem: (k) => {
			store.delete(k);
		},
		setItem: (k, v) => {
			store.set(k, String(v));
		}
	};

	(globalThis as unknown as { localStorage: Storage }).localStorage = polyfill;
});

/**
 * Migration `mcp-default-overrides-merge-v1` folds the values of the parallel
 * `mcpDefaultServerOverrides` config entry onto `mcpServers[i].enabled` (the
 * single source of truth for new-chat defaults). The legacy key is kept on
 * disk for downgrade compatibility.
 */
describe('mcp-default-overrides-merge-v1 migration', () => {
	const MIGRATION_STATE_KEY = `${STORAGE_APP_NAME}.migration-state`;
	const MCP_DEFAULT_OVERRIDES_KEY = `${STORAGE_APP_NAME}.mcpDefaultServerOverrides`;

	beforeEach(async () => {
		localStorage.clear();
		// Reset the migration run counter so `runAllMigrations` is guaranteed to execute.
		await import('$lib/services/migration.service').then((mod) =>
			mod.MigrationService.resetState()
		);
	});

	afterEach(() => {
		localStorage.clear();
	});

	async function runMigrations() {
		const { MigrationService } = await import('$lib/services/migration.service');

		await MigrationService.runAllMigrations();
	}

	function readConfig(): Record<string, unknown> {
		const raw = localStorage.getItem(CONFIG_LOCALSTORAGE_KEY);

		return raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
	}

	function writeConfig(config: Record<string, unknown>) {
		localStorage.setItem(CONFIG_LOCALSTORAGE_KEY, JSON.stringify(config));
	}

	it('applies matching overrides onto mcpServers[i].enabled and preserves the legacy key', async () => {
		writeConfig({
			[MCP_DEFAULT_OVERRIDES_KEY]: JSON.stringify([
				{ enabled: true, serverId: 'exa' },
				{ enabled: false, serverId: 'hf' }
			]),
			mcpServers: JSON.stringify([
				{ enabled: false, id: 'exa', url: 'https://mcp.exa.ai/mcp' },
				{ enabled: false, id: 'hf', url: 'https://huggingface.co/mcp' }
			])
		});

		await runMigrations();

		const after = readConfig();
		const servers = JSON.parse(after.mcpServers as string) as Array<{
			id: string;
			enabled: boolean;
		}>;

		expect(servers.find((s) => s.id === 'exa')?.enabled).toBe(true);
		expect(servers.find((s) => s.id === 'hf')?.enabled).toBe(false);
		expect(MCP_DEFAULT_OVERRIDES_KEY in after).toBe(true);
	});

	it('skips override ids that do not match any configured server', async () => {
		writeConfig({
			[MCP_DEFAULT_OVERRIDES_KEY]: JSON.stringify([
				{ enabled: true, serverId: 'orphan' },
				{ enabled: true, serverId: 'exa' }
			]),
			mcpServers: JSON.stringify([{ enabled: false, id: 'exa', url: 'https://mcp.exa.ai/mcp' }])
		});

		await runMigrations();

		const after = readConfig();
		const servers = JSON.parse(after.mcpServers as string) as Array<{
			id: string;
			enabled: boolean;
		}>;

		expect(servers).toHaveLength(1);
		expect(servers[0].enabled).toBe(true);
		expect(MCP_DEFAULT_OVERRIDES_KEY in after).toBe(true);
	});

	it('is a no-op when there are no legacy overrides', async () => {
		writeConfig({
			mcpServers: JSON.stringify([{ enabled: true, id: 'exa', url: 'https://mcp.exa.ai/mcp' }])
		});

		await runMigrations();

		const after = readConfig();
		const servers = JSON.parse(after.mcpServers as string) as Array<{
			id: string;
			enabled: boolean;
		}>;

		expect(servers[0].enabled).toBe(true);
		expect(MCP_DEFAULT_OVERRIDES_KEY in after).toBe(false);
	});

	it('does not rewrite mcpServers when override.enabled already matches', async () => {
		const originalServers = JSON.stringify([
			{ enabled: true, id: 'exa', url: 'https://mcp.exa.ai/mcp' }
		]);

		writeConfig({
			[MCP_DEFAULT_OVERRIDES_KEY]: JSON.stringify([{ enabled: true, serverId: 'exa' }]),
			mcpServers: originalServers
		});

		await runMigrations();

		const after = readConfig();

		expect(after.mcpServers).toBe(originalServers);
		expect(MCP_DEFAULT_OVERRIDES_KEY in after).toBe(true);
	});

	it('records itself as completed so subsequent loads do not re-run', async () => {
		writeConfig({
			[MCP_DEFAULT_OVERRIDES_KEY]: JSON.stringify([{ enabled: true, serverId: 'exa' }]),
			mcpServers: JSON.stringify([{ enabled: false, id: 'exa', url: 'https://mcp.exa.ai/mcp' }])
		});

		const { MigrationService } = await import('$lib/services/migration.service');

		await MigrationService.runAllMigrations();

		const stateRaw = localStorage.getItem(MIGRATION_STATE_KEY);

		expect(stateRaw).not.toBeNull();
		const state = JSON.parse(stateRaw!) as { completed: string[]; failed: string[] };

		expect(state.completed).toContain('mcp-default-overrides-merge-v1');
		expect(state.failed).not.toContain('mcp-default-overrides-merge-v1');
	});
});
