import { OAuthHelpers } from '../oauth.helpers';

const issuer = 'https://n8n.example.com';

describe('OAuthHelpers', () => {
	describe('buildSuccessRedirectUrl', () => {
		it('should build redirect URL with authorization code', () => {
			const redirectUri = 'https://example.com/callback';
			const code = 'auth-code-123';
			const state = null;

			const result = OAuthHelpers.buildSuccessRedirectUrl(redirectUri, code, state, issuer);

			expect(result).toBe(
				'https://example.com/callback?code=auth-code-123&iss=https%3A%2F%2Fn8n.example.com',
			);
		});

		it('should include state parameter when provided', () => {
			const redirectUri = 'https://example.com/callback';
			const code = 'auth-code-123';
			const state = 'state-xyz';

			const result = OAuthHelpers.buildSuccessRedirectUrl(redirectUri, code, state, issuer);

			expect(result).toBe(
				'https://example.com/callback?code=auth-code-123&state=state-xyz&iss=https%3A%2F%2Fn8n.example.com',
			);
		});

		it('should preserve existing query parameters', () => {
			const redirectUri = 'https://example.com/callback?foo=bar';
			const code = 'auth-code-123';
			const state = 'state-xyz';

			const result = OAuthHelpers.buildSuccessRedirectUrl(redirectUri, code, state, issuer);

			expect(result).toContain('foo=bar');
			expect(result).toContain('code=auth-code-123');
			expect(result).toContain('state=state-xyz');
		});

		it('should handle redirect URI with port', () => {
			const redirectUri = 'http://localhost:3000/callback';
			const code = 'auth-code-123';
			const state = null;

			const result = OAuthHelpers.buildSuccessRedirectUrl(redirectUri, code, state, issuer);

			expect(result).toBe(
				'http://localhost:3000/callback?code=auth-code-123&iss=https%3A%2F%2Fn8n.example.com',
			);
		});

		it('should URL-encode special characters in code', () => {
			const redirectUri = 'https://example.com/callback';
			const code = 'code+with/special=chars';
			const state = null;

			const result = OAuthHelpers.buildSuccessRedirectUrl(redirectUri, code, state, issuer);

			expect(result).toContain('code=code%2Bwith%2Fspecial%3Dchars');
		});

		it('should include the RFC 9207 iss parameter set to the issuer', () => {
			const result = OAuthHelpers.buildSuccessRedirectUrl(
				'https://example.com/callback',
				'auth-code-123',
				null,
				issuer,
			);

			expect(new URL(result).searchParams.get('iss')).toBe(issuer);
		});
	});

	describe('buildErrorRedirectUrl', () => {
		it('should build redirect URL with error parameters', () => {
			const redirectUri = 'https://example.com/callback';
			const error = 'access_denied';
			const errorDescription = 'User denied the authorization request';
			const state = null;

			const result = OAuthHelpers.buildErrorRedirectUrl(
				redirectUri,
				error,
				errorDescription,
				state,
				issuer,
			);

			expect(result).toContain('error=access_denied');
			expect(result).toContain('error_description=User+denied+the+authorization+request');
		});

		it('should include state parameter when provided', () => {
			const redirectUri = 'https://example.com/callback';
			const error = 'invalid_request';
			const errorDescription = 'Missing required parameter';
			const state = 'state-xyz';

			const result = OAuthHelpers.buildErrorRedirectUrl(
				redirectUri,
				error,
				errorDescription,
				state,
				issuer,
			);

			expect(result).toContain('error=invalid_request');
			expect(result).toContain('state=state-xyz');
		});

		it('should preserve existing query parameters', () => {
			const redirectUri = 'https://example.com/callback?foo=bar';
			const error = 'server_error';
			const errorDescription = 'Internal server error';
			const state = 'state-xyz';

			const result = OAuthHelpers.buildErrorRedirectUrl(
				redirectUri,
				error,
				errorDescription,
				state,
				issuer,
			);

			expect(result).toContain('foo=bar');
			expect(result).toContain('error=server_error');
			expect(result).toContain('state=state-xyz');
		});

		it('should handle common OAuth error codes', () => {
			const testCases = [
				{ error: 'access_denied', description: 'User denied' },
				{ error: 'invalid_request', description: 'Bad request' },
				{ error: 'unauthorized_client', description: 'Client not authorized' },
				{ error: 'invalid_scope', description: 'Invalid scope' },
				{ error: 'server_error', description: 'Server error' },
				{ error: 'temporarily_unavailable', description: 'Service unavailable' },
			];

			testCases.forEach(({ error, description }) => {
				const result = OAuthHelpers.buildErrorRedirectUrl(
					'https://example.com/callback',
					error,
					description,
					null,
					issuer,
				);

				expect(result).toContain(`error=${error}`);
				expect(result).toContain('error_description=');
			});
		});

		it('should URL-encode special characters in error description', () => {
			const redirectUri = 'https://example.com/callback';
			const error = 'access_denied';
			const errorDescription = 'User said "no thanks!"';
			const state = null;

			const result = OAuthHelpers.buildErrorRedirectUrl(
				redirectUri,
				error,
				errorDescription,
				state,
				issuer,
			);

			expect(result).toContain('error_description=');
			expect(result).not.toContain('"'); // Should be encoded
		});

		it('should include the RFC 9207 iss parameter set to the issuer', () => {
			const result = OAuthHelpers.buildErrorRedirectUrl(
				'https://example.com/callback',
				'access_denied',
				'User denied the authorization request',
				null,
				issuer,
			);

			expect(new URL(result).searchParams.get('iss')).toBe(issuer);
		});
	});

	describe('setIssuerParam', () => {
		it('should set iss on an absolute URL without one', () => {
			const result = OAuthHelpers.setIssuerParam('https://example.com/callback', issuer);

			expect(new URL(result).searchParams.get('iss')).toBe(issuer);
		});

		it('should preserve existing query parameters', () => {
			const result = OAuthHelpers.setIssuerParam(
				'https://example.com/callback?error=invalid_request&state=state-xyz',
				issuer,
			);

			const url = new URL(result);
			expect(url.searchParams.get('error')).toBe('invalid_request');
			expect(url.searchParams.get('state')).toBe('state-xyz');
			expect(url.searchParams.get('iss')).toBe(issuer);
		});

		it('should replace a pre-existing iss parameter with the server issuer', () => {
			const result = OAuthHelpers.setIssuerParam(
				'https://example.com/callback?iss=https%3A%2F%2Fother.example.com',
				issuer,
			);

			expect(new URL(result).searchParams.get('iss')).toBe(issuer);
			expect(new URL(result).searchParams.getAll('iss')).toHaveLength(1);
		});

		it('should return relative URLs unchanged', () => {
			expect(OAuthHelpers.setIssuerParam('/oauth/consent', issuer)).toBe('/oauth/consent');
		});
	});
});
