"use strict";

const Promise = require("bluebird");
const React = require("react");
const propTypes = require("prop-types");
const defaultValue = require("default-value");
const useForceUpdate = require("use-force-update").default;

const context = require("./context");

function NoView({isLoading, loadingSince, spinnerDelay}) {
	if (isLoading) {
		if (Date.now() - loadingSince > spinnerDelay) {
			/* FIXME: Show a better loading indicator. */
			return "Loading...";
		} else {
			return null;
		}
	} else {
		return "ERROR: View missing.";
	}
}

function ViewManager({router, initialPath, onAction, spinnerDelay}) {
	let [isLoading, setIsLoading] = React.useState(false);
	let [loadingSince, setLoadingSince] = React.useState(null);
	let [View, setView] = React.useState(null);
	let [locals, setLocals] = React.useState({});
	/* FIXME: Shouldn't the viewOptions be used? */
	let [_viewOptions, setViewOptions] = React.useState({});

	let forceUpdate = useForceUpdate();

	let spinnerDelay_ = defaultValue(spinnerDelay, 100);

	function handle(method, path, data) {
		return Promise.try(() => {
			setIsLoading(true);
			setLoadingSince(Date.now());

			/* FIXME: This is a bit hacky. Isn't there a better way to handle matters of timing in React? */
			setTimeout(() => {
				forceUpdate();
			}, spinnerDelay_);

			return router.handle(method, path, data);
		}).then((result) => {
			result.actions.forEach((action) => {
				// console.log("ACTION", action);

				if (action.type === "render") {
					setView({Component: action.view});
					setLocals(action.locals);
					setViewOptions(action.options);
				} else if (action.type === "redirect") {
					return handle(defaultValue(action.method, method), action.path);
				} else {
					if (onAction != null) {
						onAction(action);
					} else {
						console.error(`Action of type '${action.type}' got lost due to missing onAction handle`);
					}
				}
			});
		});
	}

	React.useEffect(() => {
		if (initialPath != null) {
			/* FIXME: Display loading indicator? */
			handle("get", initialPath);
		}
	}, []);

	/* MARKER/FIXME: Initial URL, and how to produce the children of a ViewManager based on the most recent router navigation; responses need to persist even on re-renders, without re-creating backend requests... Also, how to deal with 'forcible' navigation from upstream in the DOM tree, when the initialUrl is modified *after* the ViewManager was instantiated? */

	return (
		<context.Provider value={{handle}}>
			{(View != null)
				? <View.Component {...locals} />
				: <NoView isLoading={isLoading} loadingSince={loadingSince} spinnerDelay={spinnerDelay_} />}
		</context.Provider>
	);
}

ViewManager.propTypes = {
	router: propTypes.any.isRequired,
	initialPath: propTypes.string.isRequired,
	onAction: propTypes.func
};

module.exports = ViewManager;
