import { join } from 'path';
import { Main } from './Main';
import { pathExists, readJSON, writeJSON } from 'fs-extra';

const PREFIX = '[Twilio]';
export class TwilioHandler {
	private _Main: Main;

	counter: {
		date: string;
		count: number;
	} = {
		date: new Date().toLocaleDateString('nl-NL'),
		count: 0,
	};

	client;

	constructor(Main: Main) {
		this._Main = Main;
	}

	canSendMessage() {
		const maxMessages = this._Main.Config.twilio.maxMessagesPerDay;
		return this.counter.count < maxMessages;
	}

	async incrementCounter() {
		const today = new Date().toLocaleDateString('nl-NL');
		if (this.counter.date !== today) {
			this.counter.date = today;
			this.counter.count = 0;
		}
		this.counter.count++;
		await writeJSON(join(this._Main.dataPath, 'twilio.json'), this.counter);
	}

	async load() {
		const twilioConfigPath = join(this._Main.dataPath, 'twilio.json');
		const twilioConfigExists = await pathExists(twilioConfigPath);
		if (twilioConfigExists) {
			this.counter = await readJSON(twilioConfigPath);
		}

		this.client = require('twilio')(
			this._Main.Config.twilio.accountSid,
			this._Main.Config.twilio.authToken,
		);
	}

	async resetError(category: TwilioCategories) {
		if (!this.lastErrors.has(category)) return;
		this.lastErrors.delete(category);
	}

	private lastErrors: Map<TwilioCategories, string> = new Map();
	private errorLog: string[] = [];
	private errorTimeout: NodeJS.Timeout;
	async sendError(category: TwilioCategories, error: string) {
		if (this.lastErrors.get(category) === error) return;

		this.lastErrors.set(category, error);
		if (error == null) return;

		this.errorLog.push(`${category}: ${error}`);
		clearTimeout(this.errorTimeout);
		this.errorTimeout = setTimeout(async () => {
			const errorMessage = this.errorLog
				.map((error) => `- ${error}`)
				.join('\n');
			this.errorLog = [];

			if (!this.canSendMessage())
				return console.log(PREFIX, 'Max messages per day reached');
			await this.incrementCounter();

			console.log(PREFIX, `Sending to Twilio:\n`, errorMessage);

			const promises = this._Main.Config.twilio.toNumbers.map(
				(toNumber) => this.sendMessage(toNumber, errorMessage),
			);
			await Promise.all(promises);
		}, this._Main.Config.twilio.aggregateTimeout);
	}

	sendMessage(to: string, message: string) {
		return new Promise<boolean>((resolve) => {
			this.client.messages
				.create({
					body: message,
					from: `${this._Main.Config.twilio.fromNumber}`,
					to: to,
				})
				.then((message: any) => {
					console.log(`Twilio message sent with SID: ${message.sid}`);
					resolve(true);
				})
				.catch((error: any) => {
					console.error('Error sending Twilio message:', error);
					resolve(false);
				});
		});
	}
}

type TwilioCategories =
	| 'CameraRunner'
	| 'UnityRunner'
	| 'UnityWebSocket'
	| 'Status'
	| 'Audio';
