import type { CreateCredentialDto } from '@n8n/api-types';

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

test.describe('Credential API Operations', () => {
	test.describe('Basic CRUD Operations', () => {
		test('should create, retrieve, update, and delete credential', async ({ api }) => {
			const credentialData: CreateCredentialDto = {
				name: 'Test HTTP Basic Auth',
				type: 'httpBasicAuth',
				data: {
					user: 'test_user',
					password: 'test_password',
				},
			};

			const { credentialId, createdCredential } =
				await api.credentials.createCredentialFromDefinition(credentialData);

			expect(credentialId).toBeTruthy();
			expect(createdCredential.type).toBe('httpBasicAuth');
			expect(createdCredential.name).toContain('Test HTTP Basic Auth (Test');

			const retrievedCredential = await api.credentials.getCredential(credentialId);
			expect(retrievedCredential.id).toBe(credentialId);
			expect(retrievedCredential.type).toBe('httpBasicAuth');
			expect(retrievedCredential.name).toBe(createdCredential.name);

			const credentialWithData = await api.credentials.getCredential(credentialId, {
				includeData: true,
			});
			expect(credentialWithData.data).toBeDefined();
			expect(credentialWithData.data?.user).toBe('test_user');

			const updatedName = 'Updated HTTP Basic Auth';
			const updatedCredential = await api.credentials.updateCredential(credentialId, {
				name: updatedName,
				data: {
					user: 'updated_user',
					password: 'updated_password',
				},
			});
			expect(updatedCredential.name).toBe(updatedName);

			const verifyUpdated = await api.credentials.getCredential(credentialId, {
				includeData: true,
			});
			expect(verifyUpdated.name).toBe(updatedName);
			expect(verifyUpdated.data?.user).toBe('updated_user');

			const deleteResult = await api.credentials.deleteCredential(credentialId);
			expect(deleteResult).toBe(true);

			await expect(api.credentials.getCredential(credentialId)).rejects.toThrow();
		});
	});

	test.describe('Credential Listing', () => {
		test('should list credentials with different query options', async ({ api }) => {
			const credential1 = await api.credentials.createCredentialFromDefinition({
				name: 'First Test Credential',
				type: 'httpBasicAuth',
				data: { user: 'user1', password: 'pass1' },
			});

			const credential2 = await api.credentials.createCredentialFromDefinition({
				name: 'Second Test Credential',
				type: 'httpHeaderAuth',
				data: { name: 'Authorization', value: 'Bearer token' },
			});

			const allCredentials = await api.credentials.getCredentials();
			expect(allCredentials.length).toBeGreaterThanOrEqual(2);

			const createdIds = [credential1.credentialId, credential2.credentialId];
			const foundCredentials = allCredentials.filter((c) => createdIds.includes(c.id));
			expect(foundCredentials).toHaveLength(2);

			const credentialsWithScopes = await api.credentials.getCredentials({
				includeGlobal: false,
				includeScopes: true,
			});
			expect(credentialsWithScopes[0].scopes).toBeDefined();
			expect(Array.isArray(credentialsWithScopes[0].scopes)).toBe(true);

			const credentialsWithData = await api.credentials.getCredentials({
				includeGlobal: false,
				includeData: true,
			});
			const foundWithData = credentialsWithData.filter((c) => createdIds.includes(c.id));
			expect(foundWithData.some((c) => c.data)).toBe(true);
		});
	});

	test.describe('Project Integration', () => {
		test('should handle credential-project associations', async ({ api }) => {
			await api.enableFeature('projectRole:admin');
			await api.enableFeature('projectRole:editor');
			await api.setMaxTeamProjectsQuota(-1);

			const project = await api.projects.createProject('Test Project for Credentials');

			const credential = await api.credentials.createCredentialFromDefinition({
				name: 'Project Credential',
				type: 'httpBasicAuth',
				data: { user: 'user', password: 'pass' },
				projectId: project.id,
			});

			const projectCredentials = await api.credentials.getCredentialsForWorkflow({
				projectId: project.id,
			});

			expect(projectCredentials).toBeDefined();
			expect(Array.isArray(projectCredentials)).toBe(true);

			const foundCredential = projectCredentials.find((c) => c.id === credential.credentialId);
			expect(foundCredential).toBeDefined();
		});

		test('should transfer credential between projects', async ({ api }) => {
			await api.enableFeature('projectRole:admin');
			await api.enableFeature('projectRole:editor');
			await api.setMaxTeamProjectsQuota(-1);

			const sourceProject = await api.projects.createProject('Source Project');
			const destinationProject = await api.projects.createProject('Destination Project');

			const credential = await api.credentials.createCredentialFromDefinition({
				name: 'Transfer Test Credential',
				type: 'httpBasicAuth',
				data: { user: 'user', password: 'pass' },
				projectId: sourceProject.id,
			});

			const sourceCredentials = await api.credentials.getCredentialsForWorkflow({
				projectId: sourceProject.id,
			});
			const foundInSource = sourceCredentials.find((c) => c.id === credential.credentialId);
			expect(foundInSource).toBeDefined();

			await api.credentials.transferCredential(credential.credentialId, destinationProject.id);

			const destinationCredentials = await api.credentials.getCredentialsForWorkflow({
				projectId: destinationProject.id,
			});
			const foundInDestination = destinationCredentials.find(
				(c) => c.id === credential.credentialId,
			);
			expect(foundInDestination).toBeDefined();

			const sourceCredentialsAfter = await api.credentials.getCredentialsForWorkflow({
				projectId: sourceProject.id,
			});
			const stillInSource = sourceCredentialsAfter.find((c) => c.id === credential.credentialId);
			expect(stillInSource).toBeUndefined();
		});
	});

	test.describe('Data Persistence', () => {
		test('should maintain credential data across operations', async ({ api }) => {
			const originalData: CreateCredentialDto = {
				name: 'Persistence Test Credential',
				type: 'httpBasicAuth',
				data: {
					user: 'persistent_user',
					password: 'persistent_password',
				},
			};

			const { credentialId } = await api.credentials.createCredentialFromDefinition(originalData);

			const afterCreate = await api.credentials.getCredential(credentialId, {
				includeData: true,
			});
			expect(afterCreate.data?.user).toBe('persistent_user');

			await api.credentials.updateCredential(credentialId, {
				data: {
					user: 'updated_persistent_user',
					password: 'updated_persistent_password',
				},
			});

			const afterUpdate = await api.credentials.getCredential(credentialId, {
				includeData: true,
			});
			expect(afterUpdate.data?.user).toBe('updated_persistent_user');
			expect(afterUpdate.data?.password).toBeDefined();

			const allCredentials = await api.credentials.getCredentials();
			const foundCredential = allCredentials.find((c) => c.id === credentialId);
			expect(foundCredential).toBeDefined();
			expect(foundCredential!.type).toBe('httpBasicAuth');
		});
	});
});
