import { Main } from './Main';
import { StartOrigin } from './Unity/UnityRunner';
import { delay } from './Utils';

export class Status {
	private _Main: Main;

	states: Map<StatusType, StatusEntry> = new Map();

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

		this.ensure();
		this.startClock();
	}

	update(
		type: StatusType,
		state: State,
		message: string,
		buttons?: { reboot?: boolean; start?: boolean; stop?: boolean },
	) {
		this.states.set(type, { state, message, buttons });

		this._Main.WebServer.socket.emit('status', this.getState());
	}

	reboot(type: StatusType) {
		switch (type) {
			case StatusType.CameraPC: {
				this._Main.CameraRunner.sendCommand(
					'reboot',
					(response: { succeed: boolean; message?: string }) => {
						console.log('CameraRunner reboot response:', response);
					},
				);
			}
			case StatusType.CameraProcess: {
				this._Main.CameraRunner.sendCommand(
					'restart',
					(response: { succeed: boolean; message?: string }) => {
						console.log('CameraRunner restart response:', response);
					},
				);
			}
			case StatusType.CameraUnityStream: {
				this._Main.CameraRunner.sendCommand(
					'restart',
					(response: { succeed: boolean; message?: string }) => {
						console.log('CameraRunner restart response:', response);
					},
				);
				this._Main.UnityRunner.restart(StartOrigin.Manual);
			}
			case StatusType.UnityBuild: {
				this._Main.UnityRunner.restart(StartOrigin.Manual);
			}
			case StatusType.ReplayFunction: {
				console.log(
					'ReplayFunction reboot requested. Not implemented.',
				);
			}
		}
	}

	start(type: StatusType) {
		switch (type) {
			case StatusType.UnityBuild: {
				this._Main.UnityRunner.requestStart(StartOrigin.Manual);
			}
		}
	}

	stop(type: StatusType) {
		switch (type) {
			case StatusType.UnityBuild: {
				this._Main.UnityRunner.requestStop();
			}
		}
	}

	getState() {
		const state = {};
		this.states.forEach((value, key) => {
			state[key] = value;
		});
		return state;
	}

	private startClock() {
		let hadInvalidLastRound: boolean = false;
		setInterval(() => {
			let hasInvalid = false;
			this.states.forEach((state, key) => {
				if (state.state === State.Red) hasInvalid = true;
			});

			if (hasInvalid && !hadInvalidLastRound) {
				hadInvalidLastRound = true;
				console.warn(
					'One ore more status entries are invalid, waiting to see if they recover...',
				);
			} else if (hasInvalid && hadInvalidLastRound) {
				console.error(
					'One ore more status entries are still invalid after 5 seconds, restarting system.',
				);
				this._Main.Twilio.sendError(
					'Status',
					'One ore more status entries are still invalid after 5 seconds, restarting system.',
				);
				this._Main.restart();
			} else {
				this._Main.Twilio.sendError('Status', null);
				hadInvalidLastRound = false;
			}
		}, 5000);
	}

	private ensure() {
		for (const type in StatusType) {
			if (!this.states.has(StatusType[type]))
				this.states.set(StatusType[type], {
					state: State.Gray,
					message: 'Loading...',
				});
		}
	}

	private doLog() {
		console.clear();
		this.states.forEach((state, key) => {
			console.log(`[${key}] ${state.state} - ${state.message}`);
		});
	}
}

export interface StatusEntry {
	state: State;
	message: string;
	buttons?: { reboot?: boolean; start?: boolean; stop?: boolean };
}

export enum State {
	Green = 'GREEN',
	Yellow = 'YELLOW',
	Red = 'RED',
	Gray = 'GRAY',
}

export enum StatusType {
	CameraPC = 'CAMERAPC',
	CameraProcess = 'CAMERAPROCESS',
	CameraUnityStream = 'CAMERAUNITYSTREAM',
	UnityBuild = 'UNITYBUILD',
	ReplayFunction = 'REPLAYFUNCTION',
}
