/*global define*/

define(
    ['./PersistenceFailureDialog', './PersistenceFailureConstants'],
    function (PersistenceFailureDialog, PersistenceFailureConstants) {
        "use strict";

        function PersistenceFailureHandler($q, dialogService) {
            // Refresh revision information for the domain object associated
            // with this persistence failure
            function refresh(failure) {
                // Refresh the domain object to the latest from persistence
                return failure.persistence.refresh();
            }

            // Issue a new persist call for the domain object associated with
            // this failure.
            function persist(failure) {
                // Note that we reissue the persist request here, but don't
                // return it, to avoid a circular wait. We trust that the
                // PersistenceQueue will behave correctly on the next round
                // of flushing.
                failure.requeue();
            }

            // Retry persistence (overwrite) for this set of failed attempts
            function retry(failures) {
                var models = {};

                // Cache a copy of the model
                function cacheModel(failure) {
                    // Clone...
                    models[failure.id] = JSON.parse(JSON.stringify(
                        failure.domainObject.getModel()
                    ));
                }

                // Mutate a domain object to restore its model
                function remutate(failure) {
                    var model = models[failure.id];
                    return failure.domainObject.useCapability(
                        "mutation",
                        function () { return model; },
                        model.modified
                    );
                }

                // Cache the object models we might want to save
                failures.forEach(cacheModel);

                // Strategy here:
                // * Cache all of the models we might want to save (above)
                // * Refresh all domain objects (so they are latest versions)
                // * Re-insert the cached domain object models
                // * Invoke persistence again
                return $q.all(failures.map(refresh)).then(function () {
                    return $q.all(failures.map(remutate));
                }).then(function () {
                    return $q.all(failures.map(persist));
                });
            }

            // Discard changes for a failed refresh
            function discard(failure) {
                var persistence =
                    failure.domainObject.getCapability('persistence');
                return persistence.refresh();
            }

            // Discard changes associated with a failed save
            function discardAll(failures) {
                return $q.all(failures.map(discard));
            }

            // Handle failures in persistence
            function handleFailures(failures) {
                // Prepare dialog for display
                var dialogModel = new PersistenceFailureDialog(failures),
                    revisionErrors = dialogModel.model.revised;

                // Handle user input (did they choose to overwrite?)
                function handleChoice(key) {
                    // If so, try again
                    if (key === PersistenceFailureConstants.OVERWRITE_KEY) {
                        return retry(revisionErrors);
                    } else {
                        return discardAll(revisionErrors);
                    }
                }

                // Prompt for user input, the overwrite if they said so.
                return dialogService.getUserChoice(dialogModel)
                    .then(handleChoice, handleChoice);
            }

            return {
                /**
                 * Handle persistence failures by providing the user with a
                 * dialog summarizing these failures, and giving the option
                 * to overwrite/cancel as appropriate.
                 * @param {Array} failures persistence failures, as prepared
                 *        by PersistenceQueueHandler
                 */
                handle: handleFailures
            };
        }

        return PersistenceFailureHandler;
    }
);