var path = require('path'),
    _ = require('lodash'),
    Promise = require('bluebird'),
    config = require('../../config'),
    common = require('../../lib/common'),

    AppProxy = require('./proxy'),
    AppSandbox = require('./sandbox'),
    AppDependencies = require('./dependencies'),
    AppPermissions = require('./permissions'),

    loader;

function isInternalApp(name) {
    return _.includes(config.get('apps:internal'), name);
}

// Get the full path to an app by name
function getAppAbsolutePath(name) {
    if (isInternalApp(name)) {
        return path.join(config.get('paths').internalAppPath, name);
    }

    return path.join(config.getContentPath('apps'), name);
}

// Get a relative path to the given apps root, defaults
// to be relative to __dirname
function getAppRelativePath(name, relativeTo) {
    relativeTo = relativeTo || __dirname;

    var relativePath = path.relative(relativeTo, getAppAbsolutePath(name));

    if (relativePath.charAt(0) !== '.') {
        relativePath = './' + relativePath;
    }

    return relativePath;
}

// Load apps through a pseudo sandbox
function loadApp(appPath, isInternal) {
    var sandbox = new AppSandbox({internal: isInternal});

    return sandbox.loadApp(appPath);
}

function getAppByName(name, permissions) {
    // Grab the app class to instantiate
    var AppClass = loadApp(getAppRelativePath(name), isInternalApp(name)),
        appProxy = new AppProxy({
            name: name,
            permissions: permissions,
            internal: isInternalApp(name)
        }),
        app;

    // Check for an actual class, otherwise just use whatever was returned
    if (_.isFunction(AppClass)) {
        app = new AppClass(appProxy);
    } else {
        app = AppClass;
    }

    return {
        app: app,
        proxy: appProxy
    };
}

// The loader is responsible for loading apps
loader = {
    // Load a app and return the instantiated app
    installAppByName: function (name) {
        // Install the apps dependencies first
        var appPath = getAppAbsolutePath(name),
            deps = new AppDependencies(appPath);

        return deps.install()
            .then(function () {
                // Load app permissions
                var perms = new AppPermissions(appPath);

                return perms.read().catch(function (err) {
                    // Provide a helpful error about which app
                    return Promise.reject(new Error(common.i18n.t('errors.apps.permissionsErrorLoadingApp.error', {
                        name: name,
                        message: err.message
                    })));
                });
            })
            .then(function (appPerms) {
                var appInfo = getAppByName(name, appPerms),
                    app = appInfo.app,
                    appProxy = appInfo.proxy;

                // Check for an install() method on the app.
                if (!_.isFunction(app.install)) {
                    return Promise.reject(new Error(common.i18n.t('errors.apps.noInstallMethodLoadingApp.error', {name: name})));
                }

                // Run the app.install() method
                // Wrapping the install() with a when because it's possible
                // to not return a promise from it.
                return Promise.resolve(app.install(appProxy)).return(app);
            });
    },

    // Activate a app and return it
    activateAppByName: function (name) {
        var perms = new AppPermissions(getAppAbsolutePath(name));

        return perms.read().then(function (appPerms) {
            var appInfo = getAppByName(name, appPerms),
                app = appInfo.app,
                appProxy = appInfo.proxy;

            // Check for an activate() method on the app.
            if (!_.isFunction(app.activate)) {
                return Promise.reject(new Error(common.i18n.t('errors.apps.noActivateMethodLoadingApp.error', {name: name})));
            }

            // Wrapping the activate() with a when because it's possible
            // to not return a promise from it.
            return Promise.resolve(app.activate(appProxy)).return(app);
        });
    }
};

module.exports = loader;
