/*
 * Announcements chat plugin
 * By Spandamn
 */

export interface AnnouncementOptions {
	activityNumber?: number;
	source: string;
	timeoutMins?: number;
	timerEnd?: number;
}

export interface AnnouncementData extends AnnouncementOptions {
	readonly activityid: 'announcement';
}

export class Announcement extends Rooms.MinorActivity {
	readonly activityid = 'announcement' as ID;
	name = 'Announcement';
	activityNumber: number;
	source: string;
	constructor(room: Room, options: AnnouncementOptions) {
		super(room);
		this.activityNumber = options.activityNumber || room.nextGameNumber();
		this.source = options.source;
		this.setTimer(options);
	}

	generateAnnouncement() {
		return `<div class="broadcast-blue"><p style="margin: 2px 0 5px 0"><strong style="font-size:11pt">${this.source}</strong></p></div>`;
	}

	displayTo(user: User, connection: Connection | null = null) {
		const recipient = connection || user;
		recipient.sendTo(this.room, `|uhtml|announcement${this.activityNumber}|${this.generateAnnouncement()}`);
	}

	display() {
		const announcement = this.generateAnnouncement();
		for (const id in this.room.users) {
			const thisUser = this.room.users[id];
			thisUser.sendTo(this.room, `|uhtml|announcement${this.activityNumber}|${announcement}`);
		}
	}

	override onConnect(user: User, connection: Connection | null = null) {
		this.displayTo(user, connection);
	}

	destroy() {
		this.room.send(`|uhtmlchange|announcement${this.activityNumber}|<div class="infobox">(${this.room.tr`The announcement has ended.`})</div>`);
		this.room.setMinorActivity(null);
	}

	toJSON(): AnnouncementData {
		return {
			source: this.source,
			activityNumber: this.activityNumber,
			timeoutMins: this.timeoutMins,
			timerEnd: this.timerEnd,
			activityid: 'announcement',
		};
	}

	save() {
		this.room.settings.minorActivity = this.toJSON();
		this.room.saveSettings();
	}
}

export const commands: Chat.ChatCommands = {
	announcement: {
		htmlcreate: 'new',
		create: 'new',
		new(target, room, user, connection, cmd, message) {
			room = this.requireRoom();
			if (!target) return this.parse('/help announcement new');
			target = target.trim();
			if (room.battle) throw new Chat.ErrorMessage(this.tr`Battles do not support announcements.`);

			const text = this.filter(target);
			if (target !== text) throw new Chat.ErrorMessage(this.tr`You are not allowed to use filtered words in announcements.`);

			const supportHTML = cmd === 'htmlcreate';

			this.checkCan('minigame', null, room);
			if (supportHTML) this.checkCan('declare', null, room);
			this.checkChat();
			if (room.minorActivity) {
				throw new Chat.ErrorMessage(this.tr`There is already a poll or announcement in progress in this room.`);
			}

			const source = supportHTML ? this.checkHTML(Chat.collapseLineBreaksHTML(target)) : Chat.formatText(target, true);

			room.setMinorActivity(new Announcement(room, { source }));

			this.roomlog(`${user.name} used ${message}`);
			this.modlog('ANNOUNCEMENT');
			return this.privateModAction(room.tr`An announcement was started by ${user.name}.`);
		},
		newhelp: [`/announcement create [announcement] - Creates an announcement. Requires: % @ # ~`],

		htmledit: 'edit',
		edit(target, room, user, connection, cmd, message) {
			room = this.requireRoom();
			const announcement = this.requireMinorActivity(Announcement);

			if (!target) return this.parse('/help announcement edit');
			target = target.trim();
			const text = this.filter(target);
			if (target !== text) throw new Chat.ErrorMessage(this.tr`You are not allowed to use filtered words in announcements.`);

			const supportHTML = cmd === 'htmledit';

			this.checkCan('minigame', null, room);
			if (supportHTML) this.checkCan('declare', null, room);
			this.checkChat();

			const source = supportHTML ? this.checkHTML(Chat.collapseLineBreaksHTML(target)) : Chat.formatText(target, true);
			announcement.source = source;
			announcement.save();

			this.roomlog(`${user.name} used ${message}`);
			this.modlog('ANNOUNCEMENT EDIT');
			this.privateModAction(room.tr`The announcement was edited by ${user.name}.`);
			this.parse('/announcement display');
		},
		edithelp: [`/announcement edit [announcement] - Edits the announcement. Requires: % @ # ~`],

		timer(target, room, user) {
			room = this.requireRoom();
			const announcement = this.requireMinorActivity(Announcement);

			if (target) {
				this.checkCan('minigame', null, room);
				if (target === 'clear') {
					if (!announcement.endTimer()) throw new Chat.ErrorMessage(this.tr`There is no timer to clear.`);
					return this.add(this.tr`The announcement timer was turned off.`);
				}
				const timeoutMins = parseFloat(target);
				if (isNaN(timeoutMins) || timeoutMins <= 0 || timeoutMins > 7 * 24 * 60) {
					throw new Chat.ErrorMessage(this.tr`Time should be a number of minutes less than one week.`);
				}
				announcement.setTimer({ timeoutMins });
				room.add(`The announcement timer was turned on: the announcement will end in ${timeoutMins} minute${Chat.plural(timeoutMins)}.`);
				this.modlog('ANNOUNCEMENT TIMER', null, `${timeoutMins} minutes`);
				return this.privateModAction(`The announcement timer was set to ${timeoutMins} minute${Chat.plural(timeoutMins)} by ${user.name}.`);
			} else {
				if (!this.runBroadcast()) return;
				if (announcement.timeout) {
					return this.sendReply(`The announcement timer is on and will end in ${announcement.timeoutMins} minute${Chat.plural(announcement.timeoutMins)}.`);
				} else {
					return this.sendReply(this.tr`The announcement timer is off.`);
				}
			}
		},
		timerhelp: [
			`/announcement timer [minutes] - Sets the announcement to automatically end after [minutes] minutes. Requires: % @ # ~`,
			`/announcement timer clear - Clears the announcement's timer. Requires: % @ # ~`,
		],

		close: 'end',
		stop: 'end',
		end(target, room, user) {
			room = this.requireRoom();
			this.checkCan('minigame', null, room);
			this.checkChat();
			const announcement = this.requireMinorActivity(Announcement);
			announcement.end(room);
			this.modlog('ANNOUNCEMENT END');
			this.privateModAction(room.tr`The announcement was ended by ${user.name}.`);
		},
		endhelp: [`/announcement end - Ends a announcement and displays the results. Requires: % @ # ~`],

		show: '',
		display: '',
		''(target, room, user, connection) {
			room = this.requireRoom();
			const announcement = this.requireMinorActivity(Announcement);
			if (!this.runBroadcast()) return;
			room.update();

			if (this.broadcasting) {
				announcement.display();
			} else {
				announcement.displayTo(user, connection);
			}
		},
		displayhelp: [`/announcement display - Displays the announcement`],
	},
	announcementhelp: [
		`/announcement allows rooms to run their own announcements. These announcements are limited to one announcement at a time per room.`,
		`Accepts the following commands:`,
		`/announcement create [announcement] - Creates a announcement. Requires: % @ # ~`,
		`/announcement htmlcreate [announcement] - Creates a announcement, with HTML allowed. Requires: # ~`,
		`/announcement edit [announcement] - Edits the announcement. Requires: % @ # ~`,
		`/announcement htmledit [announcement] - Edits the announcement, with HTML allowed. Requires: # ~`,
		`/announcement timer [minutes] - Sets the announcement to automatically end after [minutes]. Requires: % @ # ~`,
		`/announcement display - Displays the announcement`,
		`/announcement end - Ends a announcement. Requires: % @ # ~`,
	],
};

// should handle restarts and also hotpatches
export function start() {
	Chat.multiLinePattern.register('/announcement (new|create|htmlcreate|edit|htmledit) ');

	for (const room of Rooms.rooms.values()) {
		if (room.settings.minorActivity?.activityid === 'announcement') {
			room.setMinorActivity(new Announcement(room, room.settings.minorActivity), true);
		}
	}
}
