/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

(function() {
    var angular = require('angular');
    require('object-describer/dist/object-describer.js');
    require('kubernetes-object-describer/dist/object-describer.js');
    require('angular-dialog.js');

    require('./containers');
    require('./date');
    require('./kube-client');
    require('./listing');
    require('./utils');
    require('./volumes');

    require('../views/details-page.html');
    require('../views/pod-container.html');
    require('../views/details-page.html');
    require('../views/item-delete.html');
    require('../views/route-modify.html');
    require('../views/replicationcontroller-modify.html');
    require('../views/service-modify.html');
    require('../views/deploymentconfig-body.html');
    require('../views/replicationcontroller-pods.html');
    require('../views/replicationcontroller-body.html');
    require('../views/route-body.html');
    require('../views/service-body.html');
    require('../views/service-endpoint.html');

    require('../views/pod-page.html');
    require('../views/image-page.html');
    require('../views/registry-dashboard-page.html');
    require('../views/details-page.html');
    require('../views/project-page.html');
    require('../views/topology-page.html');
    require('../views/node-page.html');
    require('../views/dashboard-page.html');
    require('../views/nodes-page.html');
    require('../views/deploymentconfig-page.html');
    require('../views/pv-page.html');
    require('../views/container-page.html');
    require('../views/service-page.html');
    require('../views/group-page.html');
    require('../views/containers-page.html');
    require('../views/projects-page.html');
    require('../views/user-page.html');
    require('../views/images-page.html');
    require('../views/replicationcontroller-page.html');
    require('../views/route-page.html');
    require('../views/imagestream-page.html');
    require('../views/volumes-page.html');

    function validItem(item, type) {
        var valid = (item && (!type || item.kind === type) &&
                     item.spec && item.metadata);
        var type_name = type || "Object";

        if (!valid)
            console.warn("Invalid " + type_name, item);

        return valid;
    }

    function format_addresses_with_ports(addresses, ports) {
        var text = addresses.join(", ");

        if (ports && ports.length) {
            text = text + ":" + ports.map(function(p) {
                if (p.protocol === "TCP")
                    return p.port;
                else
                    return p.port + "/" + p.protocol;
            }).join(", ");
        }

        return text;
    }

    angular.module('kubernetes.details', [
        'ngRoute',
        'ui.cockpit',
        'kubernetesUI',
        'kubeClient',
        'kubeUtils',
        'kubernetes.listing',
        'kubernetes.date',
        'kubernetes.volumes',
        'kubernetes.containers',
    ])

            .config([
                '$routeProvider',
                function($routeProvider) {
                    $routeProvider
                            .when('/list/:namespace?', {
                                templateUrl: 'views/details-page.html',
                                controller: 'DetailsCtrl'
                            })
                            .when('/l/pods/:pod_namespace/:pod_name/:container_name', {
                                templateUrl: 'views/pod-container.html',
                                controller: 'ContainerCtrl',
                            })
                            .when('/l/:target_type/:target_namespace/:target', {
                                templateUrl: function (params) {
                                    var re = /s$/;
                                    var kind = params.target_type || "";

                                    return 'views/' + kind.replace(re, "") + "-page.html";
                                },
                                controller: 'DetailCtrl',
                                resolve: {
                                    'kindData': [
                                        'kindData',
                                        function (kindData) {
                                            return kindData();
                                        }
                                    ]
                                }
                            })
                            .when('/l/:target_type', {
                                templateUrl: 'views/details-page.html',
                                controller: 'DetailCtrl',
                                resolve: {
                                    'kindData': [
                                        'kindData',
                                        function (kindData) {
                                            return kindData();
                                        }
                                    ]
                                }
                            });
                }
            ])

            .factory("kindData", [
                "$q",
                "$route",
                "$location",
                function($q, $route, $location) {
                    var typesToKinds = {
                        'services': 'Service',
                        'routes': 'Route',
                        'deploymentconfigs': 'DeploymentConfig',
                        'replicationcontrollers': 'ReplicationController',
                        'pods': 'Pod',
                    };

                    return function() {
                        var current = $route.current.params['target_type'];
                        var kind;
                        if (current)
                            kind = typesToKinds[current];

                        if (!kind) {
                            $location.path('/');
                            return $q.reject();
                        }

                        return $q.when({
                            'kind' : kind,
                            'type' : current,
                        });
                    };
                }
            ])

            .factory("detailsWatch", [
                "kubeLoader",
                "KubeDiscoverSettings",
                function (loader, settings) {
                    return function(until) {
                        loader.watch("Pod", until);
                        loader.watch("Service", until);
                        loader.watch("ReplicationController", until);
                        loader.watch("Endpoints", until);
                        loader.watch("PersistentVolumeClaim", until);
                        settings().then(function(settings) {
                            if (settings.flavor === "openshift") {
                                loader.watch("DeploymentConfig", until);
                                loader.watch("Route", until);
                            }
                        });
                    };
                }
            ])

            .factory("detailsData", [
                'kubeSelect',
                'volumeData',
                'KubeContainers',
                "KubeTranslate",
                function (select, volumeData, containers, translate) {
                    const _ = translate.gettext;
                    var names = {
                        'services': {
                            'name' : _("Services")
                        },
                        'routes': {
                            'name' : _("Routes"),
                            'flavor': "openshift"
                        },
                        'deploymentconfigs': {
                            'name': _("Deployment Configs"),
                            'flavor': "openshift"
                        },
                        'replicationcontrollers': {
                            'name' : _("Replication Controllers")
                        },
                        'pods': {
                            'name' : _("Pods")
                        }
                    };

                    function item_identifier(item) {
                        var meta = item.metadata || { };
                        var id = item.kind.toLowerCase() + "s/";
                        if (meta.namespace)
                            id = id + meta.namespace + "/";
                        return id + meta.name;
                    }

                    function service_endpoint(service) {
                        return select().kind("Endpoints")
                                .namespace(service.metadata.namespace)
                                .name(service.metadata.name)
                                .one();
                    }

                    function replicationcontroller_pods(item) {
                        var meta = item.metadata || {};
                        var spec = item.spec || {};
                        return select().kind("Pod")
                                .namespace(meta.namespace || "")
                                .label(spec.selector || {});
                    }

                    function podStatus(item) {
                        var status = item.status || {};
                        var meta = item.metadata || {};

                        if (meta.deletionTimestamp)
                            return "Terminating";
                        else
                            return status.phase;
                    }

                    return {
                        itemIdentifier: item_identifier,
                        serviceEndpoint: service_endpoint,
                        replicationcontrollerPods: replicationcontroller_pods,
                        podStatus: podStatus,
                        volumesForPod: volumeData.volumesForPod,
                        claimFromVolumeSource: volumeData.claimFromVolumeSource,
                        containers: containers,
                        names: names
                    };
                }
            ])

    /*
     * The controller for the details view.
     */
            .controller('DetailsCtrl', [
                '$scope',
                'kubeLoader',
                'kubeSelect',
                'KubeDiscoverSettings',
                'ListingState',
                '$location',
                'itemActions',
                'detailsData',
                'detailsWatch',
                function($scope, loader, select, discoverSettings, ListingState,
                    $location, actions, detailsData, detailsWatch) {
                    loader.listen(function() {
                        $scope.pods = select().kind("Pod");
                        $scope.services = select().kind("Service");
                        $scope.replicationcontrollers = select().kind("ReplicationController");
                        $scope.deploymentconfigs = select().kind("DeploymentConfig");
                        $scope.routes = select().kind("Route");
                    }, $scope);

                    detailsWatch($scope);
                    $scope.listing = new ListingState($scope);
                    $scope.showAll = true;

                    $scope.$on("activate", function(ev, id) {
                        ev.preventDefault();
                        actions.navigate(id);
                    });

                    /* All the data and actions available on the $scope */
                    angular.extend($scope, detailsData);
                    angular.extend($scope, actions);
                }
            ])

            .controller('DetailCtrl', [
                '$scope',
                'kindData',
                'kubeLoader',
                'kubeSelect',
                'ListingState',
                '$routeParams',
                '$location',
                'itemActions',
                'detailsData',
                'detailsWatch',
                function($scope, kindData, loader, select, ListingState, $routeParams,
                    $location, actions, detailsData, detailsWatch) {
                    var target = $routeParams["target"] || "";
                    $scope.target = target;
                    $scope.name = detailsData.names[kindData.type].name;

                    loader.listen(function() {
                        if (kindData.type)
                            $scope[kindData.type] = select().kind(kindData.kind);

                        if (target && $routeParams.target_namespace) {
                            $scope.item = select().kind(kindData.kind)
                                    .namespace($routeParams.target_namespace)
                                    .name(target)
                                    .one();
                        }
                    }, $scope);

                    detailsWatch($scope);
                    $scope.listing = new ListingState($scope);
                    $scope.listing.inline = true;

                    $scope.$on("activate", function(ev, id) {
                        ev.preventDefault();
                        actions.navigate(id);
                    });

                    /* All the data and actions available on the $scope */
                    angular.extend($scope, detailsData);
                    angular.extend($scope, actions);
                    angular.extend($scope, kindData);
                }
            ])

            .directive('kubernetesServiceCluster', function() {
                return {
                    restrict: 'E',
                    link: function($scope, element, attributes) {
                        $scope.$watchGroup(["item.spec.clusterIP",
                            "item.spec.ports"], function(values) {
                            var text = format_addresses_with_ports([values[0]],
                                                                   values[1]);
                            element.text(text);
                        });
                    }
                };
            })

            .factory('itemActions', [
                '$modal',
                '$location',
                function($modal, $location) {
                    function deleteItem(item) {
                        return $modal.open({
                            animation: false,
                            controller: 'ItemDeleteCtrl',
                            templateUrl: 'views/item-delete.html',
                            resolve: {
                                dialogData: function() {
                                    return { item: item };
                                }
                            },
                        }).result;
                    }

                    function modifyRoute(item) {
                        return $modal.open({
                            animation: false,
                            controller: 'RouteModifyCtrl',
                            templateUrl: 'views/route-modify.html',
                            resolve: {
                                dialogData: function() {
                                    return { item: item };
                                }
                            },
                        }).result;
                    }

                    function modifyRC(item) {
                        return $modal.open({
                            animation: false,
                            controller: 'RCModifyCtrl',
                            templateUrl: 'views/replicationcontroller-modify.html',
                            resolve: {
                                dialogData: function() {
                                    return { item: item };
                                }
                            },
                        }).result;
                    }

                    function modifyService(item) {
                        return $modal.open({
                            animation: false,
                            controller: 'ServiceModifyCtrl',
                            templateUrl: 'views/service-modify.html',
                            resolve: {
                                dialogData: function() {
                                    return { item: item };
                                }
                            },
                        }).result;
                    }

                    function navigate(path) {
                        var prefix = '/l';
                        path = path || "";
                        if (!path)
                            prefix = "/list";

                        if (path && path.indexOf('/') !== 0)
                            prefix = prefix + '/';

                        $location.path(prefix + path);
                    }

                    return {
                        modifyRC: modifyRC,
                        modifyRoute: modifyRoute,
                        deleteItem: deleteItem,
                        modifyService: modifyService,
                        navigate: navigate,
                    };
                }
            ])

            .controller("ItemDeleteCtrl", [
                "$scope",
                "$modalInstance",
                "dialogData",
                "kubeMethods",
                function($scope, $instance, dialogData, methods) {
                    angular.extend($scope, dialogData);

                    $scope.performDelete = function performDelete() {
                        return methods.delete($scope.item).catch(function(ex) {
                            /* HACK: While debugging delete issues */
                            console.log(JSON.stringify(ex));
                        });
                    };
                }
            ])

            .controller("RCModifyCtrl", [
                "$q",
                "$scope",
                "$modalInstance",
                "dialogData",
                "kubeMethods",
                "KubeTranslate",
                function($q, $scope, $instance, dialogData, methods, translate) {
                    const _ = translate.gettext;
                    var item = dialogData.item;
                    var fields = {};

                    if (!validItem(item, "ReplicationController")) {
                        $scope.$applyAsync(function () {
                            $scope.$dismiss();
                        });
                        return;
                    }

                    fields.replicas = item.spec ? item.spec.replicas : 1;

                    function validate() {
                        var defer = $q.defer();
                        var replicas = Number(fields.replicas.trim());
                        var ex;

                        if (isNaN(replicas) || replicas < 0)
                            ex = new Error(_("Not a valid number of replicas"));
                        else if (replicas > 128)
                            ex = new Error(_("The maximum number of replicas is 128"));

                        if (ex) {
                            ex.target = "#replicas";
                            defer.reject(ex);
                        } else {
                            defer.resolve({ spec: { replicas: replicas } });
                        }

                        return defer.promise;
                    }

                    $scope.fields = fields;
                    $scope.item = item;

                    $scope.performModify = function performModify() {
                        return validate().then(function(data) {
                            return methods.patch($scope.item, data);
                        });
                    };
                }
            ])

            .controller("RouteModifyCtrl", [
                "$q",
                "$scope",
                "$modalInstance",
                "dialogData",
                "kubeMethods",
                "KubeTranslate",
                function($q, $scope, $instance, dialogData, methods, translate) {
                    const _ = translate.gettext;
                    var fields = {};

                    if (!validItem(dialogData.item, "Route")) {
                        $scope.$applyAsync(function () {
                            $scope.$dismiss();
                        });
                        return;
                    }

                    fields.host = dialogData.item.spec.host;

                    function validate() {
                        var defer = $q.defer();
                        var host = fields.host.trim();
                        var ex;

                        if (!host) {
                            ex = new Error(_("Not a valid value for Host"));
                            ex.target = "#host-value";
                            defer.reject(ex);
                        } else {
                            defer.resolve({ spec: { host: fields.host.trim() } });
                        }

                        return defer.promise;
                    }

                    $scope.fields = fields;
                    angular.extend($scope, dialogData);

                    $scope.performModify = function performModify() {
                        return validate().then(function(data) {
                            return methods.patch($scope.item, data);
                        });
                    };
                }
            ])

            .controller("ServiceModifyCtrl", [
                "$q",
                "$scope",
                "$modalInstance",
                "dialogData",
                'kubeLoader',
                'kubeSelect',
                "KubeRequest",
                "KubeTranslate",
                "KubeFormat",
                function($q, $scope, $instance, dialogData, loader, select, KubeRequest, translate, format) {
                    const _ = translate.gettext;
                    var fields = {};
                    var key;

                    if (!validItem(dialogData.item, "Service")) {
                        $scope.$applyAsync(function () {
                            $scope.$dismiss();
                        });
                        return;
                    }

                    $scope.rcs = select().kind("ReplicationController")
                            .namespace(dialogData.item.metadata.namespace || "")
                            .label(dialogData.item.spec.selector || {});

                    for (key in $scope.rcs) {
                        var item = $scope.rcs[key];
                        fields[key] = {
                            name: item.metadata.name,
                            replicas: item.spec.replicas,
                        };
                    }

                    $scope.service = dialogData.item;
                    $scope.fields = fields;
                    angular.extend($scope, dialogData);

                    function validate() {
                        var defer = $q.defer();
                        var link;
                        var objects = [];
                        var failures = [];

                        for (link in fields) {
                            var ex;
                            var replicas = Number(fields[link].replicas);
                            var name = fields[link].name;

                            if (isNaN(replicas) || replicas < 0)
                                ex = new Error(_("Not a valid number of replicas"));
                            else if (replicas > 128)
                                ex = new Error(_("The maximum number of replicas is 128"));

                            if (ex) {
                                ex.target = "#" + name;
                                failures.push(ex);
                            } else {
                                objects.push({
                                    link: link,
                                    name: name,
                                    data: { spec: { replicas: replicas } }
                                });
                            }
                        }

                        if (failures.length > 0) {
                            defer.reject(failures);
                        } else
                            defer.resolve(objects);

                        return defer.promise;
                    }

                    $scope.performModify = function performModify() {
                        var defer = $q.defer();
                        var req;

                        validate().then(function (objects) {
                            function step() {
                                var obj = objects.shift();
                                if (!obj) {
                                    defer.resolve();
                                    return;
                                }

                                defer.notify(format.format(_("Updating $0..."), obj.name));

                                var config = { headers: { "Content-Type": "application/strategic-merge-patch+json" } };
                                new KubeRequest("PATCH", obj.link, JSON.stringify(obj.data), config)
                                        .then(function(response) {
                                            step();
                                        }, function(response) {
                                            var resp = response.data;
                                            return defer.reject(resp || response);
                                        });
                            }

                            step();
                        })
                                .catch(function(exs) {
                                    defer.reject(exs);
                                });

                        var promise = defer.promise;
                        promise.cancel = function cancel() {
                            if (req && req.cancel)
                                req.cancel();
                        };

                        return promise;
                    };
                }
            ])

            .directive('deploymentconfigBody',
                       function() {
                           return {
                               restrict: 'A',
                               templateUrl: 'views/deploymentconfig-body.html'
                           };
                       }
            )

            .directive('replicationcontrollerPods',
                       function() {
                           return {
                               restrict: 'A',
                               templateUrl: 'views/replicationcontroller-pods.html'
                           };
                       }
            )

            .directive('replicationcontrollerBody',
                       function() {
                           return {
                               restrict: 'A',
                               templateUrl: 'views/replicationcontroller-body.html'
                           };
                       }
            )

            .directive('routeBody',
                       function() {
                           return {
                               restrict: 'A',
                               templateUrl: 'views/route-body.html'
                           };
                       }
            )

            .directive('serviceBody',
                       function() {
                           return {
                               restrict: 'A',
                               templateUrl: 'views/service-body.html'
                           };
                       }
            )

            .directive('serviceEndpoint',
                       function() {
                           return {
                               restrict: 'A',
                               templateUrl: 'views/service-endpoint.html'
                           };
                       }
            );
}());
