import { afterAll, beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
import {
	MeetingEndAction,
	MeetRoomDeletionErrorCode,
	MeetRoomDeletionPolicyWithMeeting,
	MeetRoomDeletionPolicyWithRecordings,
	MeetRoomDeletionSuccessCode,
	MeetRoomStatus
} from '../../../../src/typings/ce/room.js';
import { expectSuccessListRecordingResponse, expectValidRoom } from '../../../helpers/assertion-helpers.js';
import {
	createRoom,
	deleteAllRecordings,
	deleteAllRooms,
	deleteRoom,
	disconnectFakeParticipants,
	endMeeting,
	getAllRecordings,
	getRoom,
	startTestServer
} from '../../../helpers/request-helpers.js';
import { setupSingleRoom, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios.js';

describe('Room API Tests', () => {
	beforeAll(() => {
		startTestServer();
	});

	afterAll(async () => {
		// Remove all rooms created
		await disconnectFakeParticipants();
		await deleteAllRooms();
		await deleteAllRecordings();
	});

	describe('Delete Room Tests', () => {
		describe('without active meeting or recordings', () => {
			it('should return 200 with successCode=room_deleted', async () => {
				const { roomId } = await createRoom();

				const response = await deleteRoom(roomId);
				expect(response.status).toBe(200);
				expect(response.body).toHaveProperty('successCode', MeetRoomDeletionSuccessCode.ROOM_DELETED);

				// Check room is deleted
				const getResponse = await getRoom(roomId);
				expect(getResponse.status).toBe(404);
			});
		});

		describe('with active meeting but no recordings', () => {
			let roomId: string;
			let roomName: string;
			let moderatorCookie: string;

			beforeEach(async () => {
				// Create a room with an active meeting
				const { room, moderatorCookie: cookie } = await setupSingleRoom(true);
				roomId = room.roomId;
				roomName = room.roomName;
				moderatorCookie = cookie;
			});

			it('should return 200 with successCode=room_with_active_meeting_deleted when withMeeting=force', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE
				});
				expect(response.status).toBe(200);
				expect(response.body).toHaveProperty(
					'successCode',
					MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_DELETED
				);

				// Check room is deleted
				const getResponse = await getRoom(roomId);
				expect(getResponse.status).toBe(404);
			});

			it('should return 202 with successCode=room_with_active_meeting_scheduled_to_be_deleted when withMeeting=when_meeting_ends', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS
				});
				expect(response.status).toBe(202);
				expect(response.body).toHaveProperty(
					'successCode',
					MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_DELETED
				);
				expectValidRoom(
					response.body.room,
					roomName,
					undefined,
					undefined,
					undefined,
					MeetRoomStatus.ACTIVE_MEETING,
					MeetingEndAction.DELETE
				);

				// End meeting and check the room is deleted
				await endMeeting(roomId, moderatorCookie);
				const getResponse = await getRoom(roomId);
				expect(getResponse.status).toBe(404);
			});

			it('should return 409 with error=room_has_active_meeting when withMeeting=fail', async () => {
				const response = await deleteRoom(roomId, { withMeeting: MeetRoomDeletionPolicyWithMeeting.FAIL });
				expect(response.status).toBe(409);
				expect(response.body).toHaveProperty('error', MeetRoomDeletionErrorCode.ROOM_HAS_ACTIVE_MEETING);
			});
		});

		describe('with recordings but no active meeting', () => {
			let roomId: string;
			let roomName: string;

			beforeEach(async () => {
				// Create a room with recordings and end the meeting
				const { room, moderatorCookie } = await setupSingleRoomWithRecording(true);
				roomId = room.roomId;
				roomName = room.roomName;
				await endMeeting(roomId, moderatorCookie);
			});

			it('should return 200 with successCode=room_and_recordings_deleted when withRecording=force', async () => {
				const response = await deleteRoom(roomId, {
					withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
				});
				expect(response.status).toBe(200);
				expect(response.body).toHaveProperty(
					'successCode',
					MeetRoomDeletionSuccessCode.ROOM_AND_RECORDINGS_DELETED
				);

				// Check the room and recordings are deleted
				const roomResponse = await getRoom(roomId);
				expect(roomResponse.status).toBe(404);
				const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
				expectSuccessListRecordingResponse(recordingsResponse, 0, false, false, 1);
			});

			it('should return 200 with successCode=room_closed when withRecording=close', async () => {
				const response = await deleteRoom(roomId, {
					withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
				});
				expect(response.status).toBe(200);
				expect(response.body).toHaveProperty('successCode', MeetRoomDeletionSuccessCode.ROOM_CLOSED);

				// Check that the room is closed and recordings are not deleted
				expectValidRoom(
					response.body.room,
					roomName,
					undefined,
					undefined,
					undefined,
					MeetRoomStatus.CLOSED,
					MeetingEndAction.NONE
				);

				const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
				expectSuccessListRecordingResponse(recordingsResponse, 1, false, false, 1);
			});

			it('should return 409 with error=room_has_recordings when withRecording=fail', async () => {
				const response = await deleteRoom(roomId, {
					withRecordings: MeetRoomDeletionPolicyWithRecordings.FAIL
				});
				expect(response.status).toBe(409);
				expect(response.body).toHaveProperty('error', MeetRoomDeletionErrorCode.ROOM_HAS_RECORDINGS);
			});
		});

		describe('with active meeting and recordings', () => {
			let roomId: string;
			let roomName: string;
			let moderatorCookie: string;

			beforeEach(async () => {
				// Create a room with recordings, keep the meeting active
				const { room, moderatorCookie: cookie } = await setupSingleRoomWithRecording(true);
				roomId = room.roomId;
				roomName = room.roomName;
				moderatorCookie = cookie;
			});

			it('should return 200 with successCode=room_with_active_meeting_and_recordings_deleted when withMeeting=force and withRecording=force', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
					withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
				});
				expect(response.status).toBe(200);
				expect(response.body).toHaveProperty(
					'successCode',
					MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_DELETED
				);

				// Check the room and recordings are deleted
				const roomResponse = await getRoom(roomId);
				expect(roomResponse.status).toBe(404);
				const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
				expectSuccessListRecordingResponse(recordingsResponse, 0, false, false, 1);
			});

			it('should return 200 with successCode=room_with_active_meeting_closed when withMeeting=force and withRecording=close', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
					withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
				});
				expect(response.status).toBe(200);
				expect(response.body).toHaveProperty(
					'successCode',
					MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_CLOSED
				);
				expectValidRoom(
					response.body.room,
					roomName,
					undefined,
					undefined,
					undefined,
					MeetRoomStatus.ACTIVE_MEETING,
					MeetingEndAction.CLOSE
				);

				// Check that the room is closed and recordings are not deleted
				const roomResponse = await getRoom(roomId);
				expect(roomResponse.status).toBe(200);
				expectValidRoom(
					roomResponse.body,
					roomName,
					undefined,
					undefined,
					undefined,
					MeetRoomStatus.CLOSED,
					MeetingEndAction.NONE
				);

				const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
				expectSuccessListRecordingResponse(recordingsResponse, 1, false, false, 1);
			});

			it('should return 409 with error=room_with_active_meeting_has_recordings when withMeeting=force and withRecording=fail', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
					withRecordings: MeetRoomDeletionPolicyWithRecordings.FAIL
				});
				expect(response.status).toBe(409);
				expect(response.body).toHaveProperty(
					'error',
					MeetRoomDeletionErrorCode.ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS
				);
			});

			it('should return 202 with successCode=room_with_active_meeting_and_recordings_scheduled_to_be_deleted when withMeeting=when_meeting_ends and withRecording=force', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
					withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
				});
				expect(response.status).toBe(202);
				expect(response.body).toHaveProperty(
					'successCode',
					MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_SCHEDULED_TO_BE_DELETED
				);
				expectValidRoom(
					response.body.room,
					roomName,
					undefined,
					undefined,
					undefined,
					MeetRoomStatus.ACTIVE_MEETING,
					MeetingEndAction.DELETE
				);

				// End meeting and check the room and recordings are deleted
				await endMeeting(roomId, moderatorCookie);
				const roomResponse = await getRoom(roomId);
				expect(roomResponse.status).toBe(404);
				const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
				expectSuccessListRecordingResponse(recordingsResponse, 0, false, false, 1);
			});

			it('should return 202 with successCode=room_with_active_meeting_scheduled_to_be_closed when withMeeting=when_meeting_ends and withRecording=close', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
					withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
				});
				expect(response.status).toBe(202);
				expect(response.body).toHaveProperty(
					'successCode',
					MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_CLOSED
				);
				expectValidRoom(
					response.body.room,
					roomName,
					undefined,
					undefined,
					undefined,
					MeetRoomStatus.ACTIVE_MEETING,
					MeetingEndAction.CLOSE
				);

				// End meeting and check that the room is closed and recordings are not deleted
				await endMeeting(roomId, moderatorCookie);
				const roomResponse = await getRoom(roomId);
				expect(roomResponse.status).toBe(200);
				expectValidRoom(
					roomResponse.body,
					roomName,
					undefined,
					undefined,
					undefined,
					MeetRoomStatus.CLOSED,
					MeetingEndAction.NONE
				);

				const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
				expectSuccessListRecordingResponse(recordingsResponse, 1, false, false, 1);
			});

			it('should return 409 with error=room_with_active_meeting_has_recordings_cannot_schedule_deletion when withMeeting=when_meeting_ends and withRecording=fail', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
					withRecordings: MeetRoomDeletionPolicyWithRecordings.FAIL
				});
				expect(response.status).toBe(409);
				expect(response.body).toHaveProperty(
					'error',
					MeetRoomDeletionErrorCode.ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS_CANNOT_SCHEDULE_DELETION
				);
			});

			it('should return 409 with error=room_with_recordings_has_active_meeting when withMeeting=fail', async () => {
				const response = await deleteRoom(roomId, {
					withMeeting: MeetRoomDeletionPolicyWithMeeting.FAIL,
					withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
				});
				expect(response.status).toBe(409);
				expect(response.body).toHaveProperty(
					'error',
					MeetRoomDeletionErrorCode.ROOM_WITH_RECORDINGS_HAS_ACTIVE_MEETING
				);
			});
		});
	});

	describe('Delete Room Validation failures', () => {
		it('should fail when roomId becomes empty after sanitization', async () => {
			const response = await deleteRoom('!!-*!@#$%^&*()_+{}|:"<>?');

			expect(response.status).toBe(422);
			// Expect an error message indicating the resulting roomId is empty.
			expect(response.body.error).toContain('Unprocessable Entity');
			expect(JSON.stringify(response.body.details)).toContain('roomId cannot be empty after sanitization');
		});

		it('should fail when withMeeting parameter is invalid', async () => {
			const response = await deleteRoom('testRoom', { withMeeting: 'invalid_value' });

			expect(response.status).toBe(422);
			expect(response.body.error).toContain('Unprocessable Entity');
			expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
		});

		it('should fail when withRecordings parameter is invalid', async () => {
			const response = await deleteRoom('testRoom', { withRecordings: 'invalid_value' });

			expect(response.status).toBe(422);
			expect(response.body.error).toContain('Unprocessable Entity');
			expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
		});
	});
});
