'use strict';

/* global angular */
/* global moment */
/* global $ */

// create main application module
var app = angular.module('Application', ['pascalprecht.translate', 'ngCookies', 'angular-md5', 'ui-notification']);

app.controller('LogsController', ['$scope', '$translate', 'Client', function ($scope, $translate, Client) {
    var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});

    $scope.initialized = false;
    $scope.client = Client;
    $scope.selected = '';
    $scope.activeEventSource = null;
    $scope.lines = 100;
    $scope.selectedAppInfo = null;
    $scope.selectedTaskInfo = null;

    function ab2str(buf) {
        return String.fromCharCode.apply(null, new Uint16Array(buf));
    }

    $scope.clear = function () {
        var logViewer = $('.logs-container');
        logViewer.empty();
    };

    // https://github.com/janl/mustache.js/blob/master/mustache.js#L60
    var entityMap = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#39;',
        '/': '&#x2F;',
        '`': '&#x60;',
        '=': '&#x3D;'
    };

    function escapeHtml(string) {
        return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
            return entityMap[s];
        });
    }

    function showLogs() {
        if (!$scope.selected) return;

        var func;
        if ($scope.selected.type === 'platform') func = Client.getPlatformLogs;
        else if ($scope.selected.type === 'service') func = Client.getServiceLogs;
        else if ($scope.selected.type === 'task') func = Client.getTaskLogs;
        else if ($scope.selected.type === 'app') func = Client.getAppLogs;

        func($scope.selected.value, true /* follow */, $scope.lines, function handleLogs(error, result) {
            if (error) return console.error(error);

            $scope.activeEventSource = result;
            result.onmessage = function handleMessage(message) {
                var data;

                try {
                    data = JSON.parse(message.data);
                } catch (e) {
                    return console.error(e);
                }

                // check if we want to auto scroll (this is before the appending, as that skews the check)
                var tmp = $('.logs-container');
                var autoScroll = tmp[0].scrollTop > (tmp[0].scrollHeight - tmp.innerHeight() - 24);

                var logLine = $('<div class="log-line">');
                // realtimeTimestamp is 0 if line is blank or some parse error
                var timeString = data.realtimeTimestamp ? moment(data.realtimeTimestamp/1000).format('MMM DD HH:mm:ss') : '';
                logLine.html('<span class="time">' + timeString + ' </span>' + window.ansiToHTML(escapeHtml(typeof data.message === 'string' ? data.message : ab2str(data.message))));
                tmp.append(logLine);

                if (autoScroll) tmp[0].lastChild.scrollIntoView({ behavior: 'instant', block: 'end' });
            };
        });
    }

    function select(ids, callback) {
        if (ids.id && ids.id.indexOf('redis:') === 0) {
            $scope.selected = {
                name: 'Redis',
                type: 'service',
                value: ids.id,
                url: Client.makeURL('/api/v1/services/' + ids.id + '/logs')
            };
            callback();
        } else if (ids.id) {
            var BUILT_IN_LOGS = [
                { name: 'Box', type: 'platform', value: 'box', url: Client.makeURL('/api/v1/cloudron/logs/box') },
                { name: 'Graphite', type: 'service', value: 'graphite', url: Client.makeURL('/api/v1/services/graphite/logs') },
                { name: 'MongoDB', type: 'service', value: 'mongodb', url: Client.makeURL('/api/v1/services/mongodb/logs') },
                { name: 'MySQL', type: 'service', value: 'mysql', url: Client.makeURL('/api/v1/services/mysql/logs') },
                { name: 'PostgreSQL', type: 'service', value: 'postgresql', url: Client.makeURL('/api/v1/services/postgresql/logs') },
                { name: 'Mail', type: 'service', value: 'mail', url: Client.makeURL('/api/v1/services/mail/logs') },
                { name: 'Docker', type: 'service', value: 'docker', url: Client.makeURL('/api/v1/services/docker/logs') },
                { name: 'Nginx', type: 'service', value: 'nginx', url: Client.makeURL('/api/v1/services/nginx/logs') },
                { name: 'Unbound', type: 'service', value: 'unbound', url: Client.makeURL('/api/v1/services/unbound/logs') },
                { name: 'SFTP', type: 'service', value: 'sftp', url: Client.makeURL('/api/v1/services/sftp/logs') },
                { name: 'TURN/STUN', type: 'service', value: 'turn', url: Client.makeURL('/api/v1/services/turn/logs') },
            ];

            $scope.selected = BUILT_IN_LOGS.find(function (e) { return e.value === ids.id; });
            callback();
        } else if (ids.crashId) {
            $scope.selected = {
                type: 'platform',
                value: 'crash-' + ids.crashId,
                name: 'Crash',
                url: Client.makeURL('/api/v1/cloudron/logs/crash-' + ids.crashId)
            };

            callback();
        } else if (ids.appId) {
            Client.getApp(ids.appId, function (error, app) {
                if (error) return callback(error);

                $scope.selectedAppInfo = app;

                $scope.selected = {
                    type: 'app',
                    value: app.id,
                    name: app.fqdn + ' (' + app.manifest.title + ')',
                    url: Client.makeURL('/api/v1/apps/' + app.id + '/logs'),
                    addons: app.manifest.addons
                };

                callback();
            });
        } else if (ids.taskId) {
            Client.getTask(ids.taskId, function (error, task) {
                if (error) return callback(error);

                $scope.selectedTaskInfo = task;

                $scope.selected = {
                    type: 'task',
                    value: task.id,
                    name: task.type,
                    url: Client.makeURL('/api/v1/tasks/' + task.id + '/logs')
                };

                callback();
            });
        }
    }

    function init() {

        Client.getStatus(function (error, status) {
            if (error) return Client.initError(error, init);

            if (!status.activated) {
                console.log('Not activated yet, redirecting', status);
                window.location.href = '/';
                return;
            }

            // check version and force reload if needed
            if (!localStorage.version) {
                localStorage.version = status.version;
            } else if (localStorage.version !== status.version) {
                localStorage.version = status.version;
                window.location.reload(true);
            }

            console.log('Running log version ', localStorage.version);

            // get user profile as the first thing. this populates the "scope" and affects subsequent API calls
            Client.refreshUserInfo(function (error) {
                if (error) return Client.initError(error, init);

                Client.refreshConfig(function (error) {
                    if (error) return Client.initError(error, init);

                    select({ id: search.id, taskId: search.taskId, appId: search.appId, crashId: search.crashId }, function (error) {
                        if (error) return Client.initError(error, init);

                        // now mark the Client to be ready
                        Client.setReady();

                        $scope.initialized = true;

                        showLogs();
                    });
                });
            });
        });
    }

    init();

    $translate([ 'logs.title' ]).then(function (tr) {
        if (tr['logs.title'] !== 'logs.title') window.document.title = tr['logs.title'];
    });
}]);
