// # Fixtures
// This module handles populating or updating fixtures.
//
// Currently fixtures only change between data version 002 and 003, therefore the update logic is hard coded
// rather than abstracted into a migration system. The upgrade function checks that its changes are safe before
// making them.

var Promise     = require('bluebird'),
    sequence    = require('../../utils/sequence'),
    _           = require('lodash'),
    errors      = require('../../errors'),
    utils       = require('../../utils'),
    models      = require('../../models'),
    fixtures    = require('./fixtures'),
    permissions = require('./permissions'),

    // Private
    logInfo,
    to003,
    convertAdminToOwner,
    createOwner,
    options = {context: {internal: true}},

    // Public
    populate,
    update;

logInfo = function logInfo(message) {
    errors.logInfo('Migrations', message);
};

/**
 * Convert admin to Owner
 * Changes an admin user to have the owner role
 * @returns {Promise|*}
 */
convertAdminToOwner = function () {
    var adminUser;

    return models.User.findOne({role: 'Administrator'}).then(function (user) {
        adminUser = user;
        return models.Role.findOne({name: 'Owner'});
    }).then(function (ownerRole) {
        if (adminUser) {
            logInfo('Converting admin to owner');
            return adminUser.roles().updatePivot({role_id: ownerRole.id});
        }
    });
};

/**
 * Create Owner
 * Creates the user fixture and gives it the owner role
 * @returns {Promise|*}
 */
createOwner = function () {
    var user = fixtures.users[0];

    return models.Role.findOne({name: 'Owner'}).then(function (ownerRole) {
        user.roles = [ownerRole.id];
        user.password = utils.uid(50);

        logInfo('Creating owner');
        return models.User.add(user, options);
    });
};

populate = function () {
    var ops = [],
        relations = [],
        Post = models.Post,
        Tag = models.Tag,
        Role = models.Role,
        Client = models.Client;

    logInfo('Populating fixtures');

    _.each(fixtures.posts, function (post) {
        ops.push(Post.add(post, options));
    });

    _.each(fixtures.tags, function (tag) {
        ops.push(Tag.add(tag, options));
    });

    _.each(fixtures.roles, function (role) {
        ops.push(Role.add(role, options));
    });

    _.each(fixtures.clients, function (client) {
        ops.push(Client.add(client, options));
    });

    // add the tag to the post
    relations.push(function () {
        return Post.forge({slug: fixtures.posts[0].slug}).fetch().then(function (post) {
            return Tag.forge({slug: fixtures.tags[0].slug}).fetch().then(function (tag) {
                return post.related('tags').attach(tag.id);
            });
        });
    });

    return Promise.all(ops).then(function () {
        return sequence(relations);
    }).then(function () {
        return permissions.populate(options);
    }).then(function () {
        return createOwner();
    }).catch(function (errs) {
        errors.logError(errs);
    });
};

/**
 * ### Update fixtures to 003
 * Need to add client & owner role, then update permissions to 003 as well
 * By doing this in a way that checks before adding, we can ensure that it's possible to force a migration safely.
 *
 * Note: At the moment this is pretty adhoc & untestable, in future it would be better to have a config based system.
 * @returns {Promise|*}
 */
to003 = function () {
    var ops = [],
        upgradeOp,
        Role = models.Role,
        Client = models.Client;

    logInfo('Upgrading fixtures');

    // Add the client fixture if missing
    upgradeOp = Client.findOne({secret: fixtures.clients[0].secret}).then(function (client) {
        if (!client) {
            logInfo('Adding client fixture');
            _.each(fixtures.clients, function (client) {
                return Client.add(client, options);
            });
        }
    });
    ops.push(upgradeOp);

    // Add the owner role if missing
    upgradeOp = Role.findOne({name: fixtures.roles[3].name}).then(function (owner) {
        if (!owner) {
            logInfo('Adding owner role fixture');
            _.each(fixtures.roles.slice(3), function (role) {
                return Role.add(role, options);
            });
        }
    });
    ops.push(upgradeOp);

    return Promise.all(ops).then(function () {
        return permissions.to003(options);
    }).then(function () {
        return convertAdminToOwner();
    });
};

update = function (fromVersion, toVersion) {
    logInfo('Updating fixtures');
    // Are we migrating to, or past 003?
    if ((fromVersion < '003' && toVersion >= '003') ||
        fromVersion === '003' && toVersion === '003' && process.env.FORCE_MIGRATION) {
        return to003();
    }
};

module.exports = {
    populate: populate,
    update: update
};