import { nanoid } from 'nanoid';

import { test, expect } from '../../../fixtures/base';

test.describe(
	'Credentials',
	{
		annotation: [{ type: 'owner', description: 'Identity & Access' }],
	},
	() => {
		test.beforeEach(async ({ n8n }) => {
			await n8n.goHome();
		});

		test('should create a new credential using empty state', async ({ n8n }) => {
			const projectId = await n8n.start.fromNewProject();
			const credentialName = `My awesome Notion account ${nanoid()}`;

			await n8n.credentialsComposer.createFromList(
				'Notion API',
				{ apiKey: '1234567890' },
				{ name: credentialName, projectId },
			);

			await expect(n8n.credentials.cards.getCredentials()).toHaveCount(1);
			await expect(n8n.credentials.cards.getCredential(credentialName)).toBeVisible();
		});

		test('should sort credentials', async ({ n8n }) => {
			const projectId = await n8n.start.fromNewProject();
			const credentialA = `A Credential ${nanoid()}`;
			const credentialZ = `Z Credential ${nanoid()}`;

			await n8n.api.credentials.createCredential({
				name: credentialA,
				type: 'notionApi',
				data: { apiKey: '1234567890' },
				projectId,
			});

			await n8n.api.credentials.createCredential({
				name: credentialZ,
				type: 'trelloApi',
				data: { apiKey: 'test_api_key', apiToken: 'test_api_token' },
				projectId,
			});

			await n8n.navigate.toCredentials(projectId);
			await n8n.credentials.clearSearch();
			await n8n.credentials.sortByNameDescending();

			const firstCardDescending = n8n.credentials.cards.getCredentials().first();
			await expect(firstCardDescending).toContainText(credentialZ);

			await n8n.credentials.sortByNameAscending();

			const firstCardAscending = n8n.credentials.cards.getCredentials().first();
			await expect(firstCardAscending).toContainText(credentialA);
		});

		test('should create credentials from NDV for node with multiple auth options', async ({
			n8n,
		}) => {
			await n8n.start.fromNewProjectBlankCanvas();
			const credentialName = `My Google Service Account ${nanoid()}`;

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('Gmail', { action: 'Send a message' });

			await n8n.ndv.clickCreateNewCredential();

			// Gmail has 2 auth options (OAuth2 + Service Account), shown as a dropdown
			await expect(n8n.canvas.credentialModal.getModeSelector()).toBeVisible();

			await n8n.canvas.credentialModal.selectAuthTypeFromDropdown(/Service Account/);

			// Fill in the Service Account fields and save
			await n8n.canvas.credentialModal.addCredential(
				{
					email: 'test@project.iam.gserviceaccount.com',
					privateKey: 'test_private_key',
				},
				{ name: credentialName },
			);

			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(credentialName);
		});

		test('should show multiple credential types in the same dropdown', async ({ n8n }) => {
			const projectId = await n8n.start.fromNewProjectBlankCanvas();
			const serviceAccountCredentialName2 = `OAuth2 Credential ${nanoid()}`;
			const serviceAccountCredentialName = `Service Account Credential ${nanoid()}`;

			await n8n.api.credentials.createCredential({
				name: serviceAccountCredentialName2,
				type: 'googleApi',
				data: { email: 'test@service.com', privateKey: 'test_key' },
				projectId,
			});

			await n8n.api.credentials.createCredential({
				name: serviceAccountCredentialName,
				type: 'googleApi',
				data: { email: 'test@service.com', privateKey: 'test_key' },
				projectId,
			});

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('Gmail', { action: 'Send a message' });

			await n8n.ndv.getCredentialSelect().click();
			await expect(n8n.ndv.getCredentialOptionByText(serviceAccountCredentialName2)).toBeVisible();
			await expect(n8n.ndv.getCredentialOptionByText(serviceAccountCredentialName)).toBeVisible();
			await expect(n8n.ndv.credentialDropdownCreateNewCredential()).toBeVisible();
			await expect(n8n.ndv.getCredentialDropdownOptions()).toHaveCount(2);
		});

		test('should correctly render required and optional credentials', async ({ n8n }) => {
			await n8n.start.fromNewProjectBlankCanvas();

			await n8n.canvas.addNode('Pipedrive', { trigger: 'On new Pipedrive event' });
			await n8n.ndv.selectOptionInParameterDropdown('incomingAuthentication', 'Basic Auth');
			await expect(n8n.ndv.getNodeCredentialsEmptyState()).toHaveCount(2);

			await n8n.ndv.clickCreateNewCredential(0);
			// First credential type has multiple auth options → mode selector visible
			await expect(n8n.canvas.credentialModal.getModeSelector()).toBeVisible();
			await n8n.canvas.credentialModal.close();

			await n8n.ndv.clickCreateNewCredential(1);
			await expect(n8n.canvas.credentialModal.getModal()).toBeVisible();
			// Second credential type has single auth option → no mode selector
			await expect(n8n.canvas.credentialModal.getModeSelector()).toBeHidden();
			await n8n.canvas.credentialModal.close();
		});

		test('should create credentials from NDV for node with no auth options', async ({ n8n }) => {
			await n8n.start.fromNewProjectBlankCanvas();
			const credentialName = `My Trello Account ${nanoid()}`;

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('Trello', { action: 'Create a card' });

			await n8n.credentialsComposer.createFromNdv(
				{
					apiKey: 'test_api_key',
					apiToken: 'test_api_token',
				},
				{ name: credentialName },
			);

			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(credentialName);
		});

		test('should delete credentials from NDV', async ({ n8n }) => {
			await n8n.start.fromNewProjectBlankCanvas();
			const credentialName = `Notion Credential ${nanoid()}`;

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('Notion', { action: 'Append a block' });

			await n8n.credentialsComposer.createFromNdv(
				{ apiKey: '1234567890' },
				{ name: credentialName },
			);
			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(credentialName);

			await n8n.canvas.credentialModal.editCredential();
			await n8n.canvas.credentialModal.deleteCredential();
			await n8n.canvas.credentialModal.confirmDelete();

			await expect(
				n8n.notifications.getNotificationByTitleOrContent('Credential deleted'),
			).toBeVisible();

			await expect(n8n.ndv.getNodeCredentialsEmptyState()).toBeVisible();
		});

		test('should rename credentials from NDV', async ({ n8n }) => {
			await n8n.start.fromNewProjectBlankCanvas();
			const initialName = `My Trello Account ${nanoid()}`;
			const renamedName = `Something else ${nanoid()}`;

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('Trello', { action: 'Create a card' });

			await n8n.credentialsComposer.createFromNdv(
				{
					apiKey: 'test_api_key',
					apiToken: 'test_api_token',
				},
				{ name: initialName },
			);

			await n8n.canvas.credentialModal.editCredential();
			await n8n.canvas.credentialModal.renameCredential(renamedName);
			await n8n.canvas.credentialModal.save();
			await n8n.canvas.credentialModal.close();

			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(renamedName);
		});

		test('should edit credential for non-standard credential type', async ({ n8n }) => {
			await n8n.start.fromNewProjectBlankCanvas();
			const initialName = `Adalo Credential ${nanoid()}`;
			const editedName = `Something else ${nanoid()}`;

			await n8n.canvas.addNode('AI Agent', { closeNDV: true });
			await n8n.canvas.addNode('HTTP Request Tool');

			await n8n.ndv.selectOptionInParameterDropdown('authentication', 'Predefined Credential Type');
			await n8n.ndv.selectOptionInParameterDropdown('nodeCredentialType', 'Adalo API');

			await n8n.credentialsComposer.createFromNdv(
				{
					apiKey: 'test_adalo_key',
					appId: 'test_app_id',
				},
				{ name: initialName },
			);

			await n8n.canvas.credentialModal.editCredential();
			await n8n.canvas.credentialModal.renameCredential(editedName);
			await n8n.canvas.credentialModal.save();
			await n8n.canvas.credentialModal.close();

			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(editedName);
		});

		test('should set a default credential when adding nodes', async ({ n8n }) => {
			const projectId = await n8n.start.fromNewProjectBlankCanvas();
			const credentialName = `My awesome Notion account ${nanoid()}`;

			await n8n.api.credentials.createCredential({
				name: credentialName,
				type: 'notionApi',
				data: { apiKey: '1234567890' },
				projectId,
			});

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('Notion', { action: 'Append a block' });
			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(credentialName);

			const credentials = await n8n.api.credentials.getCredentials();
			const credential = credentials.find((c) => c.name === credentialName);
			await n8n.api.credentials.deleteCredential(credential!.id);
		});

		test('should set a default credential when editing a node', async ({ n8n }) => {
			const projectId = await n8n.start.fromNewProjectBlankCanvas();
			const credentialName = `My awesome Notion account ${nanoid()}`;

			await n8n.api.credentials.createCredential({
				name: credentialName,
				type: 'notionApi',
				data: { apiKey: '1234567890' },
				projectId,
			});

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('HTTP Request');

			await n8n.ndv.selectOptionInParameterDropdown('authentication', 'Predefined Credential Type');
			await n8n.ndv.selectOptionInParameterDropdown('nodeCredentialType', 'Notion API');
			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(credentialName);

			const credentials = await n8n.api.credentials.getCredentials();
			const credential = credentials.find((c) => c.name === credentialName);
			await n8n.api.credentials.deleteCredential(credential!.id);
		});

		test('should setup generic authentication for HTTP node', async ({ n8n }) => {
			await n8n.start.fromNewProjectBlankCanvas();
			const credentialName = `Query Auth Credential ${nanoid()}`;

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('HTTP Request');

			await n8n.ndv.selectOptionInParameterDropdown('authentication', 'Generic Credential Type');
			await n8n.ndv.selectOptionInParameterDropdown('genericAuthType', 'Query Auth');

			await n8n.credentialsComposer.createFromNdv(
				{
					name: 'api_key',
					value: 'test_query_value',
				},
				{ name: credentialName },
			);

			await expect(n8n.ndv.getCredentialSelect()).toHaveValue(credentialName);
		});

		test('should not show OAuth redirect URL section when OAuth2 credentials are overridden', async ({
			n8n,
		}) => {
			// Mock credential types response to simulate admin override
			await n8n.page.route('**/rest/types/credentials.json', async (route) => {
				const response = await route.fetch();
				const json = await response.json();

				// Override Slack OAuth2 credential properties
				if (json.slackOAuth2Api) {
					json.slackOAuth2Api.__overwrittenProperties = ['clientId', 'clientSecret'];
				}

				await route.fulfill({ json });
			});

			await n8n.start.fromNewProjectBlankCanvas();

			await n8n.canvas.addNode('Manual Trigger');
			await n8n.canvas.addNode('Slack', { action: 'Get a channel' });

			await n8n.ndv.clickCreateNewCredential();

			// With overridden OAuth2 properties, the mode selector shows a dropdown
			// with Managed OAuth2 selected by default — redirect URL should be hidden
			await expect(n8n.canvas.credentialModal.getModeSelector()).toBeVisible();
			await expect(n8n.canvas.credentialModal.getOAuthRedirectUrl()).toBeHidden();
			await expect(n8n.canvas.credentialModal.getModal()).toBeVisible();
		});

		test('ADO-2583 should show notifications above credential modal overlay', async ({ n8n }) => {
			await n8n.page.route('**/rest/credentials', async (route) => {
				if (route.request().method() === 'POST') {
					await route.abort('failed');
				} else {
					await route.continue();
				}
			});

			const projectId = await n8n.start.fromNewProject();
			await n8n.navigate.toCredentials(projectId);
			await n8n.credentials.addResource.credential();
			await n8n.credentials.selectCredentialType('Notion API');
			await n8n.canvas.credentialModal.fillField('apiKey', '1234567890');

			const saveBtn = n8n.canvas.credentialModal.getSaveButton();
			await saveBtn.click();

			const errorNotification = n8n.notifications.getErrorNotifications();
			await expect(errorNotification).toBeVisible();
			await expect(n8n.canvas.credentialModal.getModal()).toBeVisible();

			await expect(errorNotification).toHaveCSS('z-index', '2100');
			await expect(n8n.notifications.getModalOverlay()).toHaveCSS('z-index', '2001');
		});
	},
);
