/**
 * Application dialogs provider
 *
 * @author Pierre Hubert
 */

import {
	Dialog,
	DialogTitle,
	DialogContent,
	DialogContentText,
	DialogActions,
	Button,
	TextField,
	Snackbar,
	IconButton,
} from "@mui/material";
import { Close } from "@mui/icons-material";
import React, { FormEvent } from "react";

let cache: ApplicationDialogsProvider;

export interface TextInputOptions {
	title?: string;
	message?: string;
	initialValue?: string;
	minLength?: number;
	maxLength?: number;
	label: string;
	type?: string;
	validateInput?: (value: string) => boolean;
}

interface AppDiagProvState {
	// Alert dialog
	alertMessage: string;
	showAlert: boolean;

	// Simple snackbar
	snackBarMessage: string;
	showSnackBar: boolean;

	// Confirm dialog
	confirmMessage: string;
	showConfirm: boolean;
	resolveConfirm?: (confirmed: boolean) => void;

	// Text input dialog
	showInputDialog: boolean;
	inputValue: string;
	inputOptions: TextInputOptions;
	resolveInput?: (text: string) => void;
}

export class ApplicationDialogsProvider extends React.Component<
	{},
	AppDiagProvState
> {
	private acceptConfirm: () => void;
	private rejectConfirm: () => void;
	private cancelInput: () => void;
	private confirmInput: () => void;

	constructor(props: any) {
		super(props);

		this.state = {
			// Alert dialog
			alertMessage: "",
			showAlert: false,

			// Simple snackbar
			snackBarMessage: "",
			showSnackBar: false,

			// Confirm dialog
			showConfirm: false,
			confirmMessage: "",
			resolveConfirm: undefined,

			// Text input dialog
			showInputDialog: false,
			inputValue: "",
			inputOptions: {
				label: "",
			},
			resolveInput: undefined,
		};

		this.handleCloseAlert = this.handleCloseAlert.bind(this);

		this.handleCloseSnackbar = this.handleCloseSnackbar.bind(this);

		this.acceptConfirm = this.handleCloseConfirm.bind(this, true);
		this.rejectConfirm = this.handleCloseConfirm.bind(this, false);

		this.submitInput = this.submitInput.bind(this);
		this.handleInputValueChanged = this.handleInputValueChanged.bind(this);
		this.cancelInput = this.handleCloseInput.bind(this, true);
		this.confirmInput = this.handleCloseInput.bind(this, false);
	}

	showAlert(message: string) {
		this.setState({
			showAlert: true,
			alertMessage: message,
		});
	}

	handleCloseAlert() {
		this.setState({ showAlert: false });
	}

	showSnackbar(message: string) {
		this.setState({ showSnackBar: true, snackBarMessage: message });
	}

	handleCloseSnackbar(
		_event: React.SyntheticEvent | React.MouseEvent | Event,
		reason?: string
	) {
		if (reason === "clickaway") {
			return;
		}

		this.setState({ showSnackBar: false });
	}

	showConfirm(message: string): Promise<boolean> {
		return new Promise((res, _rej) => {
			this.setState({
				showConfirm: true,
				confirmMessage: message,
				resolveConfirm: res,
			});
		});
	}

	handleCloseConfirm(accept: boolean) {
		this.setState({
			showConfirm: false,
		});

		this.state.resolveConfirm && this.state.resolveConfirm(accept);
	}

	showInput(options: TextInputOptions): Promise<string> {
		return new Promise((res, _rej) => {
			this.setState({
				showInputDialog: true,
				inputOptions: options,
				resolveInput: res,
				inputValue: options.initialValue || "",
			});
		});
	}

	handleInputValueChanged(e: React.ChangeEvent<HTMLInputElement>) {
		this.setState({ inputValue: e.target.value });
	}

	submitInput(e: FormEvent<HTMLFormElement>) {
		e.preventDefault();
		this.handleCloseInput(false);
	}

	handleCloseInput(cancel: boolean) {
		if (!cancel && !this.isInputValid) return;

		this.setState({
			showInputDialog: false,
		});

		if (!cancel)
			this.state.resolveInput &&
				this.state.resolveInput(this.state.inputValue);
	}

	get isInputValid(): boolean {
		const options = this.state.inputOptions;
		const value = this.state.inputValue;

		if (options.minLength && value.length < options.minLength) return false;

		if (options.maxLength && value.length > options.maxLength) return false;

		if (options.validateInput && !options.validateInput(value))
			return false;

		return true;
	}

	render() {
		cache = this;

		if (this.state == null) return <div></div>;

		return (
			<div>
				{/* Simple alert dialog */}
				<Dialog
					open={this.state.showAlert}
					onClose={this.handleCloseAlert}
					aria-labelledby="alert-dialog-title"
					aria-describedby="alert-dialog-description"
				>
					<DialogTitle
						id="alert-dialog-title"
						style={{ minWidth: "300px" }}
					>
						Message
					</DialogTitle>
					<DialogContent>
						<DialogContentText id="alert-dialog-description">
							{this.state.alertMessage}
						</DialogContentText>
					</DialogContent>
					<DialogActions>
						<Button onClick={this.handleCloseAlert} color="primary">
							OK
						</Button>
					</DialogActions>
				</Dialog>

				{/* Simple snackbar */}
				<Snackbar
					anchorOrigin={{
						vertical: "bottom",
						horizontal: "left",
					}}
					open={this.state.showSnackBar}
					autoHideDuration={6000}
					onClose={this.handleCloseSnackbar}
					message={this.state.snackBarMessage}
					action={
						<React.Fragment>
							<IconButton
								size="small"
								aria-label="close"
								color="inherit"
								onClick={this.handleCloseSnackbar}
							>
								<Close fontSize="small" />
							</IconButton>
						</React.Fragment>
					}
				/>

				{/* Confirm dialog */}
				<Dialog
					open={this.state.showConfirm}
					onClose={this.rejectConfirm}
					aria-labelledby="alert-dialog-title"
					aria-describedby="alert-dialog-description"
				>
					<DialogTitle
						id="alert-dialog-title"
						style={{ minWidth: "300px" }}
					>
						Confirm action
					</DialogTitle>
					<DialogContent>
						<DialogContentText id="alert-dialog-description">
							{this.state.confirmMessage}
						</DialogContentText>
					</DialogContent>
					<DialogActions>
						<Button onClick={this.rejectConfirm} color="secondary">
							Cancel
						</Button>
						<Button onClick={this.acceptConfirm} color="secondary">
							Confirm
						</Button>
					</DialogActions>
				</Dialog>

				{/* Text input dialog */}
				<Dialog
					open={this.state.showInputDialog}
					onClose={this.rejectConfirm}
					aria-labelledby="alert-dialog-title"
					aria-describedby="alert-dialog-description"
				>
					<DialogTitle id="alert-dialog-title">
						{this.state.inputOptions.title ||
							this.state.inputOptions.label}
					</DialogTitle>
					<DialogContent>
						{this.state.inputOptions.message ? (
							<DialogContentText id="alert-dialog-description">
								{this.state.inputOptions.message} <br />
								<br />
							</DialogContentText>
						) : (
							<span></span>
						)}
						<form onSubmit={this.submitInput}>
							<TextField
								label={this.state.inputOptions.label}
								variant="outlined"
								value={this.state.inputValue}
								onChange={this.handleInputValueChanged}
								type={this.state.inputOptions.type || "text"}
							/>
						</form>
					</DialogContent>
					<DialogActions>
						<Button onClick={this.cancelInput} color="secondary">
							Cancel
						</Button>
						<Button
							onClick={this.confirmInput}
							color="secondary"
							disabled={!this.isInputValid}
						>
							OK
						</Button>
					</DialogActions>
				</Dialog>
			</div>
		);
	}
}

export function matAlert(msg: string) {
	cache.showAlert(msg);
}

export function snackbar(msg: string) {
	cache.showSnackbar(msg);
}

export function matConfirm(msg: string): Promise<boolean> {
	return cache.showConfirm(msg);
}

export function input(options: TextInputOptions): Promise<string> {
	return cache.showInput(options);
}
