import crypto from 'crypto';
import { inject, injectable } from '../config/dependency-injector.config.js';
import { Room } from 'livekit-server-sdk';
import { LoggerService } from './logger.service.js';
import { MEET_API_KEY, MEET_WEBHOOK_ENABLED, MEET_WEBHOOK_URL } from '../environment.js';
import { MeetWebhookEvent, MeetWebhookEventType, MeetRecordingInfo, MeetWebhookPayload } from '@typings-ce';

@injectable()
export class OpenViduWebhookService {
	constructor(@inject(LoggerService) protected logger: LoggerService) {}

	// TODO: Implement Room webhooks
	async sendRoomFinishedWebhook(room: Room) {
		// try {
		// 	await this.sendWebhookEvent(MeetWebhookEventType.ROOM_FINISHED, data);
		// } catch (error) {
		// 	this.logger.error(`Error sending room finished webhook: ${error}`);
		// }
	}

	async sendRecordingStartedWebhook(recordingInfo: MeetRecordingInfo) {
		try {
			await this.sendWebhookEvent(MeetWebhookEventType.RECORDING_STARTED, recordingInfo);
		} catch (error) {
			this.logger.error(`Error sending recording started webhook: ${error}`);
		}
	}

	async sendRecordingUpdatedWebhook(recordingInfo: MeetRecordingInfo) {
		try {
			await this.sendWebhookEvent(MeetWebhookEventType.RECORDING_UPDATED, recordingInfo);
		} catch (error) {
			this.logger.error(`Error sending recording updated webhook: ${error}`);
		}
	}

	async sendRecordingEndedWebhook(recordingInfo: MeetRecordingInfo) {
		try {
			await this.sendWebhookEvent(MeetWebhookEventType.RECORDING_ENDED, recordingInfo);
		} catch (error) {
			this.logger.error(`Error sending recording ended webhook: ${error}`);
		}
	}

	private async sendWebhookEvent(event: MeetWebhookEventType, payload: MeetWebhookPayload) {
		if (!this.isWebhookEnabled()) return;

		const creationDate = Date.now();
		const data: MeetWebhookEvent = {
			event,
			creationDate,
			data: payload
		};

		const signature = this.generateWebhookSignature(creationDate, data);

		this.logger.info(`Sending webhook event ${data.event}`);

		try {
			await this.fetchWithRetry(MEET_WEBHOOK_URL, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json',
					'X-Timestamp': creationDate.toString(),
					'X-Signature': signature
				},
				body: JSON.stringify(data)
			});
		} catch (error) {
			this.logger.error(`Error sending webhook event ${data.event}: ${error}`);
			throw error;
		}
	}

	private generateWebhookSignature(timestamp: number, payload: object): string {
		return crypto
			.createHmac('sha256', MEET_API_KEY)
			.update(`${timestamp}.${JSON.stringify(payload)}`)
			.digest('hex');
	}

	private async fetchWithRetry(url: string, options: RequestInit, retries = 5, delay = 300): Promise<void> {
		try {
			const response = await fetch(url, options);

			if (!response.ok) {
				throw new Error(`Request failed with status ${response.status}`);
			}
		} catch (error) {
			if (retries <= 0) {
				throw new Error(`Request failed: ${error}`);
			}

			this.logger.warn(`Retrying in ${delay / 1000} seconds... (${retries} retries left)`);
			await new Promise((resolve) => setTimeout(resolve, delay));
			// Retry the request after a delay with exponential backoff
			return this.fetchWithRetry(url, options, retries - 1, delay * 2);
		}
	}

	private isWebhookEnabled(): boolean {
		return !!MEET_WEBHOOK_URL && MEET_WEBHOOK_ENABLED === 'true';
	}
}
