/**
 * Tests for Test Notification API Endpoint
 *
 * Verifies /api/notifications/test endpoint functionality including:
 * - Type validation
 * - Payload structure
 * - PushNotificationService integration
 */

import { describe, test, expect, beforeEach, vi } from 'vitest';
import { POST } from '../routes/api/notifications/test/+server';
import { pushNotificationService } from '$lib/server/notifications/PushNotificationService';

describe('POST /api/notifications/test', () => {
	let sendNotificationSpy: any;
	let getSubscriptionCountSpy: any;

	beforeEach(() => {
		vi.clearAllMocks();

		// Spy on pushNotificationService methods
		sendNotificationSpy = vi.spyOn(pushNotificationService, 'sendNotification').mockResolvedValue();
		getSubscriptionCountSpy = vi
			.spyOn(pushNotificationService, 'getSubscriptionCount')
			.mockReturnValue(2);
	});

	test('should validate notification type - reject invalid type', async () => {
		const request = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'invalid' })
		});

		const response = await POST({ request } as any);
		const data = await response.json();

		expect(response.status).toBe(400);
		expect(data.error).toContain('Invalid notification type');
		expect(sendNotificationSpy).not.toHaveBeenCalled();
	});

	test('should validate notification type - reject missing type', async () => {
		const request = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({})
		});

		const response = await POST({ request } as any);
		const data = await response.json();

		expect(response.status).toBe(400);
		expect(data.error).toContain('Invalid notification type');
		expect(sendNotificationSpy).not.toHaveBeenCalled();
	});

	test('should send test success notification', async () => {
		const request = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'success' })
		});

		const response = await POST({ request } as any);
		const data = await response.json();

		expect(response.status).toBe(200);
		expect(data.success).toBe(true);
		expect(data.message).toContain('success');
		expect(data.subscriberCount).toBe(2);

		expect(sendNotificationSpy).toHaveBeenCalledWith(
			expect.objectContaining({
				type: 'success',
				body: expect.stringContaining('Test recipe'),
				recipeName: 'Test Recipe',
				itemId: expect.stringMatching(/^test_\d+$/),
				tag: expect.stringMatching(/^recipe-success-test_\d+$/),
				requireInteraction: false
			})
		);
	});

	test('should send test error notification', async () => {
		const request = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'error' })
		});

		const response = await POST({ request } as any);
		const data = await response.json();

		expect(response.status).toBe(200);
		expect(data.success).toBe(true);
		expect(data.message).toContain('error');

		expect(sendNotificationSpy).toHaveBeenCalledWith(
			expect.objectContaining({
				type: 'error',
				body: expect.stringContaining('test error'),
				itemId: expect.stringMatching(/^test_\d+$/),
				tag: expect.stringMatching(/^recipe-error-test_\d+$/),
				requireInteraction: true
			})
		);
	});

	test('should send test progress notification', async () => {
		const request = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'progress' })
		});

		const response = await POST({ request } as any);
		const data = await response.json();

		expect(response.status).toBe(200);
		expect(data.success).toBe(true);
		expect(data.message).toContain('progress');

		expect(sendNotificationSpy).toHaveBeenCalledWith(
			expect.objectContaining({
				type: 'progress',
				body: expect.stringContaining('parsing phase'),
				itemId: expect.stringMatching(/^test_\d+$/),
				tag: expect.stringMatching(/^recipe-progress-test_\d+$/),
				requireInteraction: false
			})
		);
	});

	test('should return subscriber count in response', async () => {
		getSubscriptionCountSpy.mockReturnValue(5);

		const request = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'success' })
		});

		const response = await POST({ request } as any);
		const data = await response.json();

		expect(data.subscriberCount).toBe(5);
		expect(getSubscriptionCountSpy).toHaveBeenCalled();
	});

	test('should handle sendNotification errors', async () => {
		sendNotificationSpy.mockRejectedValue(new Error('Push service error'));

		const request = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'success' })
		});

		const response = await POST({ request } as any);
		const data = await response.json();

		expect(response.status).toBe(500);
		expect(data.error).toContain('Failed to send test notification');
	});

	test('should generate unique itemId for each request', async () => {
		const request1 = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'success' })
		});

		const request2 = new Request('http://localhost/api/notifications/test', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ type: 'success' })
		});

		await POST({ request: request1 } as any);
		const call1 = sendNotificationSpy.mock.calls[0][0];

		// Wait a bit to ensure different timestamp
		await new Promise((resolve) => setTimeout(resolve, 2));

		await POST({ request: request2 } as any);
		const call2 = sendNotificationSpy.mock.calls[1][0];

		expect(call1.itemId).not.toBe(call2.itemId);
		expect(call1.tag).not.toBe(call2.tag);
	});
});
