import { test, expect } from '../../../../fixtures/base';
import type { n8nPage } from '../../../../pages/n8nPage';
import type { TestRequirements } from '../../../../Types';
import allTemplatesSearchResponse from '../../../../workflows/all_templates_search_response.json';
import onboardingWorkflow from '../../../../workflows/Onboarding_workflow.json';
import salesTemplatesSearchResponse from '../../../../workflows/sales_templates_search_response.json';
import workflowTemplate from '../../../../workflows/Workflow_template_write_http_query.json';

const TEMPLATE_HOST = {
	N8N_API: 'https://api.n8n.io/api/',
	CUSTOM: 'random.domain',
} as const;

const URLS = {
	N8N_WORKFLOWS: 'https://n8n.io/workflows',
} as const;

const TEMPLATE_ID = '1';
const TEST_CATEGORY = 'sales';
const SALES_CATEGORY_ID = 3;

const CATEGORIES = [
	{ id: 1, name: 'Engineering' },
	{ id: 2, name: 'Finance' },
	{ id: 3, name: 'Sales' },
];

const COLLECTIONS = [
	{
		id: 1,
		name: 'Test Collection',
		workflows: [{ id: 1 }],
		nodes: [],
	},
];

// Helper functions
/**
 * Prevents the browser's "before unload" confirmation dialog
 * Used when navigating away from workflows with unsaved changes in tests
 */
function preventNavigation(n8n: n8nPage) {
	return n8n.page.evaluate(() => {
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		(window as any).preventNodeViewBeforeUnload = true;
	});
}

/**
 * Extracts the numeric count from UI text (e.g., "5 templates" → 5)
 * @param text - Text containing a number (e.g., "5 templates", "3 collections")
 * @returns The extracted number, or 0 if no number found
 */
function parseCount(text: string | null): number {
	const digits = text?.replace(/\D/g, '') ?? '';
	return parseInt(digits || '0', 10);
}

function createTemplateHostRequirements(): TestRequirements {
	return {
		config: {
			settings: {
				templates: {
					enabled: true,
					host: TEMPLATE_HOST.N8N_API,
				},
			},
		},
	};
}

function createCustomTemplateHostRequirements(hostname: string): TestRequirements {
	return {
		config: {
			settings: {
				templates: {
					enabled: true,
					host: `https://${hostname}/api`,
				},
			},
		},
		intercepts: {
			health: {
				url: `https://${hostname}/api/health`,
				response: { status: 'OK' },
			},
			categories: {
				url: `https://${hostname}/api/templates/categories`,
				response: { categories: CATEGORIES },
			},
			getTemplate: {
				url: `https://${hostname}/api/workflows/templates/${TEMPLATE_ID}`,
				response: {
					id: 1,
					name: onboardingWorkflow.name,
					workflow: onboardingWorkflow,
				},
			},
			getTemplatePreview: {
				url: `https://${hostname}/api/templates/workflows/${TEMPLATE_ID}`,
				response: workflowTemplate,
			},
		},
	};
}

/**
 * Setup dynamic template routes for collections and search
 * This handles query parameter-based routing that cannot be configured statically
 * - Collections API filters by category
 * - Search API returns different results based on category
 */
async function setupDynamicTemplateRoutes(n8n: n8nPage, hostname: string) {
	await n8n.page.route(`https://${hostname}/api/templates/collections*`, (route) => {
		const url = new URL(route.request().url());
		const categoryParam = url.searchParams.get('category[]');
		const response = categoryParam === String(SALES_CATEGORY_ID) ? [] : COLLECTIONS;
		void route.fulfill({
			status: 200,
			contentType: 'application/json',
			body: JSON.stringify({ collections: response }),
		});
	});

	await n8n.page.route(`https://${hostname}/api/templates/search*`, (route) => {
		const url = new URL(route.request().url());
		const category = url.searchParams.get('category');
		const response =
			category === 'Sales' ? salesTemplatesSearchResponse : allTemplatesSearchResponse;
		void route.fulfill({
			status: 200,
			contentType: 'application/json',
			body: JSON.stringify(response),
		});
	});
}

test.describe(
	'Workflow templates',
	{
		annotation: [{ type: 'owner', description: 'Adore' }],
	},
	() => {
		test.describe('For api.n8n.io', () => {
			test('Opens website when clicking templates sidebar link', async ({
				n8n,
				setupRequirements,
			}) => {
				await setupRequirements(createTemplateHostRequirements());
				await n8n.navigate.toWorkflows();

				const templatesLink = n8n.sideBar.getTemplatesLink();
				await expect(templatesLink).toBeVisible();

				const href = await templatesLink.getAttribute('href');
				expect(href).toContain(URLS.N8N_WORKFLOWS);

				const url = new URL(href!);
				const origin = await n8n.page.evaluate(() => window.location.origin);

				const utmInstance = url.searchParams.get('utm_instance');
				expect(utmInstance).toBeTruthy();
				expect(decodeURIComponent(utmInstance!)).toContain(origin);

				const utmVersion = url.searchParams.get('utm_n8n_version');
				expect(utmVersion).toBeTruthy();
				expect(utmVersion).toMatch(/[0-9]+\.[0-9]+\.[0-9]+/);

				const utmAwc = url.searchParams.get('utm_awc');
				expect(utmAwc).toBeTruthy();
				expect(utmAwc).toMatch(/[0-9]+/);

				await expect(templatesLink).toHaveAttribute('target', '_blank');
			});

			test('Redirects to website when visiting templates page directly', async ({
				n8n,
				setupRequirements,
			}) => {
				await setupRequirements(createTemplateHostRequirements());
				await n8n.navigate.toTemplates();

				await expect(n8n.templates.getPageHeading()).toBeVisible({ timeout: 10000 });
			});
		});

		test.describe('For a custom template host', () => {
			const hostname = TEMPLATE_HOST.CUSTOM;

			test.beforeEach(async ({ n8n, setupRequirements }) => {
				await setupRequirements(createCustomTemplateHostRequirements(hostname));
				await setupDynamicTemplateRoutes(n8n, hostname);
			});

			test('can open onboarding flow', async ({ n8n }) => {
				await preventNavigation(n8n);

				await Promise.all([
					n8n.page.waitForResponse(`https://${hostname}/api/workflows/templates/${TEMPLATE_ID}`),
					n8n.page.waitForResponse('**/rest/workflows'),
					n8n.navigate.toOnboardingTemplate(TEMPLATE_ID),
				]);

				await expect(n8n.page).toHaveURL(/.*\/workflow\/.*onboardingId=1$/);

				const workflowNameOnboarding = await n8n.canvas.getWorkflowName().getAttribute('title');
				expect(workflowNameOnboarding).toContain(`Demo: ${onboardingWorkflow.name}`);

				await expect(n8n.canvas.getCanvasNodes()).toHaveCount(4);
				await expect(n8n.canvas.sticky.getStickies()).toHaveCount(1);
			});

			test('can import template', async ({ n8n }) => {
				await preventNavigation(n8n);

				await Promise.all([
					n8n.page.waitForResponse(`https://${hostname}/api/workflows/templates/${TEMPLATE_ID}`),
					n8n.page.waitForResponse('**/rest/workflows/**'),
					n8n.navigate.toTemplateImport(TEMPLATE_ID),
				]);

				// New workflows redirect to /workflow/<id>?new=true&templateId=1
				await expect(n8n.page).toHaveURL(/\/workflow\/[a-zA-Z0-9_-]+\?.*templateId=1/);
				await expect(n8n.canvas.getCanvasNodes()).toHaveCount(4);
				await expect(n8n.canvas.sticky.getStickies()).toHaveCount(1);

				const workflowName = await n8n.canvas.getWorkflowName().getAttribute('title');
				expect(workflowName).toContain(onboardingWorkflow.name);
			});

			test('should save template id with the workflow', async ({ n8n }) => {
				await n8n.templatesComposer.importFirstTemplate();

				// Execute workflow to trigger autosave (imported templates don't auto-save immediately)
				await n8n.canvas.hitExecuteWorkflow();

				const saveResponsePromise = n8n.canvas.waitForSaveWorkflowCompleted();
				const saveResponse = await saveResponsePromise;

				const requestBody = saveResponse.request().postDataJSON();
				expect(requestBody.meta.templateId).toBe(TEMPLATE_ID);
			});

			test('can open template with images and hides workflow screenshots', async ({ n8n }) => {
				await n8n.navigate.toTemplate(TEMPLATE_ID);
				await expect(n8n.templates.getDescription()).toBeVisible();
				await expect(n8n.templates.getDescriptionImages()).toHaveCount(1);
			});

			test('renders search elements correctly', async ({ n8n }) => {
				await n8n.navigate.toTemplates();

				await expect(n8n.templates.getSearchInput()).toBeVisible();
				await expect(n8n.templates.getAllCategoriesFilter()).toBeVisible();

				const categoryFilterCount = await n8n.templates.getCategoryFilters().count();
				expect(categoryFilterCount).toBeGreaterThan(1);

				const templateCardCount = await n8n.templates.getTemplateCards().count();
				expect(templateCardCount).toBeGreaterThan(0);
			});

			test('can filter templates by category', async ({ n8n }) => {
				await n8n.navigate.toTemplates();
				await expect(n8n.templates.getTemplatesLoadingContainer()).toBeHidden();
				await expect(n8n.templates.getCategoryFilter(TEST_CATEGORY)).toBeVisible();

				const initialTemplateText = await n8n.templates.getTemplateCountLabel().textContent();
				const initialTemplateCount = parseCount(initialTemplateText);

				const initialCollectionText = await n8n.templates.getCollectionCountLabel().textContent();
				const initialCollectionCount = parseCount(initialCollectionText);

				await n8n.templates.clickCategoryFilter(TEST_CATEGORY);
				await expect(n8n.templates.getTemplatesLoadingContainer()).toBeHidden();

				const finalTemplateText = await n8n.templates.getTemplateCountLabel().textContent();
				const finalTemplateCount = parseCount(finalTemplateText);
				expect(finalTemplateCount).toBeLessThan(initialTemplateCount);

				const finalCollectionText = await n8n.templates.getCollectionCountLabel().textContent();
				const finalCollectionCount = parseCount(finalCollectionText);
				expect(finalCollectionCount).toBeLessThan(initialCollectionCount);
			});

			test('should preserve search query in URL', async ({ n8n }) => {
				await n8n.navigate.toTemplates();
				await expect(n8n.templates.getTemplatesLoadingContainer()).toBeHidden();
				await expect(n8n.templates.getCategoryFilter(TEST_CATEGORY)).toBeVisible();

				await n8n.templates.clickCategoryFilter(TEST_CATEGORY);
				await n8n.templates.getSearchInput().fill('auto');

				await expect(n8n.page).toHaveURL(/\?categories=/);
				await expect(n8n.page).toHaveURL(/&search=/);

				await n8n.page.reload();

				await expect(n8n.page).toHaveURL(/\?categories=/);
				await expect(n8n.page).toHaveURL(/&search=/);

				const salesFilterLabel = n8n.templates.getCategoryFilter(TEST_CATEGORY);
				await expect(salesFilterLabel).toBeChecked();
				await expect(n8n.templates.getSearchInput()).toHaveValue('auto');

				await expect(n8n.templates.getCategoryFilters().nth(1)).toHaveText('Sales');
			});
		});
	},
);
