/**
 * Course import-related js.
 */
define(
    ['jquery', 'underscore', 'gettext', 'moment', 'jquery.cookie'],
    function($, _, gettext, moment) {
        'use strict';

        /** ******** Private properties ****************************************/

        var COOKIE_NAME = 'lastimportupload';

        var STAGE = {
            'UPLOADING': 0,
            'UNPACKING': 1,
            'VERIFYING': 2,
            'UPDATING': 3,
            'SUCCESS': 4
        };

        var STATE = {
            'READY': 1,
            'IN_PROGRESS': 2,
            'SUCCESS': 3,
            'ERROR': 4
        };

        var current = {stage: 0, state: STATE.READY};
        var deferred = null;
        var file = {name: null, url: null};
        var timeout = {id: null, delay: 1000};
        var $dom = {
            stages: $('ol.status-progress').children(),
            successStage: $('.item-progresspoint-success'),
            wrapper: $('div.wrapper-status')
        };

        /** ******** Private functions *****************************************/

        /**
         * Destroys any event listener Import might have needed
         * during the process the import
         *
         */
        var destroyEventListeners = function() {
            $(window).off('beforeunload.import');
        };

        /**
         * Makes Import feedback status list visible
         *
         */
        var displayFeedbackList = function() {
            $dom.wrapper.removeClass('is-hidden');
        };

        /**
         * Sets the Import in the "error" status.
         *
         * Immediately stops any further polling from the server.
         * Displays the error message at the list element that corresponds
         * to the stage where the error occurred.
         *
         * @param {string} msg Error message to display.
         * @param {int} [stage=current.stage] Stage of import process at which error occurred.
         */
        var error = function(msg, stage) {
            current.stage = Math.abs(stage || current.stage); // Could be negative
            current.state = STATE.ERROR;

            destroyEventListeners();
            clearTimeout(timeout.id);
            updateFeedbackList(msg);

            deferred.resolve();
        };

        /**
         * Initializes the event listeners
         *
         */
        var initEventListeners = function() {
            $(window).on('beforeunload.import', function() {
                if (current.stage <= STAGE.UNPACKING) {
                    return gettext('Your import is in progress; navigating away will abort it.');
                }
            });
        };

        /**
         * Stores in a cookie the current import data
         *
         * @param {boolean} [completed=false] If the import has been completed or not
         */
        var storeImport = function(completed) {
            $.cookie(COOKIE_NAME, JSON.stringify({
                file: file,
                date: moment().valueOf(),
                completed: completed || false
            }), {path: window.location.pathname});
        };

        /**
         * Sets the Import on the "success" status
         *
         * If it wasn't already, marks the stored import as "completed",
         * and updates its date timestamp
         */
        var success = function() {
            current.state = STATE.SUCCESS;

            if (CourseImport.storedImport().completed !== true) {
                storeImport(true);
            }

            destroyEventListeners();
            updateFeedbackList();

            deferred.resolve();
        };

        /**
         * Updates the Import feedback status list
         *
         * @param {string} [currStageMsg=''] The message to show on the
         *   current stage (for now only in case of error)
         */
        var updateFeedbackList = function(currStageMsg) {
            var $checkmark, $curr, $prev, $next;
            var date, successUnix, time;

            $checkmark = $dom.successStage.find('.icon');
            currStageMsg = currStageMsg || '';

            function completeStage(stage) {
                $(stage)
                    .removeClass('is-not-started is-started')
                    .addClass('is-complete');
            }

            function errorStage(stage) {
                if (!$(stage).hasClass('has-error')) {
                    $(stage)
                        .removeClass('is-started')
                        .addClass('has-error')
                        .find('p.copy')
                        .hide()
                        .after("<p class='copy error'>" + currStageMsg + '</p>');
                }
            }

            function resetStage(stage) {
                $(stage)
                    .removeClass('is-complete is-started has-error')
                    .addClass('is-not-started')
                    .find('p.error').remove().end()
                    .find('p.copy').show();
            }

            switch (current.state) {
            case STATE.READY:
                _.map($dom.stages, resetStage);

                break;

            case STATE.IN_PROGRESS:
                $prev = $dom.stages.slice(0, current.stage);
                $curr = $dom.stages.eq(current.stage);

                _.map($prev, completeStage);
                $curr.removeClass('is-not-started').addClass('is-started');

                break;

            case STATE.SUCCESS:
                successUnix = CourseImport.storedImport().date;
                date = moment(successUnix).utc().format('MM/DD/YYYY');
                time = moment(successUnix).utc().format('HH:mm');

                _.map($dom.stages, completeStage);

                $dom.successStage
                        .find('.item-progresspoint-success-date')
                        .html('(' + date + ' at ' + time + ' UTC)');

                break;

            case STATE.ERROR:
                    // Make all stages up to, and including, the error stage 'complete'.
                $prev = $dom.stages.slice(0, current.stage + 1);
                $curr = $dom.stages.eq(current.stage);
                $next = $dom.stages.slice(current.stage + 1);

                _.map($prev, completeStage);
                _.map($next, resetStage);
                errorStage($curr);

                break;
            }

            if (current.state === STATE.SUCCESS) {
                $checkmark.removeClass('fa-square-o').addClass('fa-check-square-o');
            } else {
                $checkmark.removeClass('fa-check-square-o').addClass('fa-square-o');
            }
        };

        /** ******** Public functions ******************************************/

        var CourseImport = {

            /**
             * Cancels the import and sets the Object to the error state
             *
             * @param {string} msg Error message to display.
             * @param {int} stage Stage of import process at which error occurred.
             */
            cancel: function(msg, stage) {
                error(msg, stage);
            },

            /**
             * Entry point for server feedback
             *
             * Checks for import status updates every `timeout` milliseconds,
             * and updates the page accordingly.
             *
             * @param {int} [stage=0] Starting stage.
             */
            pollStatus: function(stage) {
                if (current.state !== STATE.IN_PROGRESS) {
                    return;
                }

                current.stage = stage || STAGE.UPLOADING;

                if (current.stage === STAGE.SUCCESS) {
                    success();
                } else if (current.stage < STAGE.UPLOADING) { // Failed
                    error(gettext('Error importing course'));
                } else { // In progress
                    updateFeedbackList();

                    $.getJSON(file.url, function(data) {
                        timeout.id = setTimeout(function() {
                            this.pollStatus(data.ImportStatus);
                        }.bind(this), timeout.delay);
                    }.bind(this));
                }
            },

            /**
             * Resets the Import internally and visually
             *
             */
            reset: function() {
                current.stage = STAGE.UPLOADING;
                current.state = STATE.READY;

                clearTimeout(timeout.id);
                updateFeedbackList();
            },

            /**
             * Show last import status from server and start sending requests
             * to the server for status updates
             *
             * @return {jQuery promise}
             */
            resume: function() {
                deferred = $.Deferred();
                file = this.storedImport().file;

                $.getJSON(file.url, function(data) {
                    current.stage = data.ImportStatus;

                    displayFeedbackList();

                    if (current.stage !== STAGE.UPLOADING) {
                        current.state = STATE.IN_PROGRESS;

                        this.pollStatus(current.stage);
                    } else {
                        // An import in the upload stage cannot be resumed
                        error(gettext('There was an error with the upload'));
                    }
                }.bind(this));

                return deferred.promise();
            },

            /**
             * Starts the importing process.
             * Makes status list visible and starts showing upload progress.
             *
             * @param {string} fileName The name of the file to import
             * @param {string} fileUrl The full URL to use to query the server
             *     about the import status
             * @return {jQuery promise}
             */
            start: function(fileName, fileUrl) {
                current.state = STATE.IN_PROGRESS;
                deferred = $.Deferred();

                file.name = fileName;
                file.url = fileUrl;

                initEventListeners();
                storeImport();
                displayFeedbackList();
                updateFeedbackList();

                return deferred.promise();
            },

            /**
             * Fetches the previous stored import
             *
             * @return {JSON} the data of the previous import
             */
            storedImport: function() {
                return JSON.parse($.cookie(COOKIE_NAME));
            }
        };

        return CourseImport;
    });
