directive_module.directive('apollonspanel', directive);

function directive($window, toastr, AppUtil, EventManager, PermissionService, NamespaceLockService,
                   UserService, CommitService, ReleaseService, InstanceService, NamespaceBranchService, ConfigService) {
    return {
        restrict: 'E',
        templateUrl: '../../views/component/namespace-panel.html',
        transclude: true,
        replace: true,
        scope: {
            namespace: '=',
            appId: '=',
            env: '=',
            cluster: '=',
            user: '=',
            lockCheck: '=',
            createItem: '=',
            editItem: '=',
            preDeleteItem: '=',
            showText: '=',
            showNoModifyPermissionDialog: '=',
            preCreateBranch: '=',
            preDeleteBranch: '=',
            showMergeAndPublishGrayTips: '='
        },
        link: function (scope) {

            //constants
            var namespace_view_type = {
                TEXT: 'text',
                TABLE: 'table',
                HISTORY: 'history',
                INSTANCE: 'instance',
                RULE: 'rule'
            };

            var namespace_instance_view_type = {
                LATEST_RELEASE: 'latest_release',
                NOT_LATEST_RELEASE: 'not_latest_release',
                ALL: 'all'
            };

            var MIN_ROW_SIZE = 10;

            var operate_branch_storage_key = 'OperateBranch';

            scope.switchView = switchView;
            scope.toggleItemSearchInput = toggleItemSearchInput;
            scope.searchItems = searchItems;
            scope.loadCommitHistory = loadCommitHistory;
            scope.toggleTextEditStatus = toggleTextEditStatus;
            scope.goToSyncPage = goToSyncPage;
            scope.modifyByText = modifyByText;
            scope.goToParentAppConfigPage = goToParentAppConfigPage;
            scope.switchInstanceViewType = switchInstanceViewType;
            scope.switchBranch = switchBranch;
            scope.loadInstanceInfo = loadInstanceInfo;
            scope.refreshInstancesInfo = refreshInstancesInfo;
            scope.deleteRuleItem = deleteRuleItem;
            scope.rollback = rollback;

            scope.publish = publish;
            scope.mergeAndPublish = mergeAndPublish;
            scope.addRuleItem = addRuleItem;
            scope.editRuleItem = editRuleItem;

            var subscriberId = EventManager.subscribe(EventManager.EventType.UPDATE_GRAY_RELEASE_RULES,
                                                      function (context) {
                                                          useRules(context.branch);
                                                      }, scope.namespace.baseInfo.namespaceName);

            scope.$on('$destroy', function () {
                EventManager.unsubscribe(EventManager.EventType.UPDATE_GRAY_RELEASE_RULES,
                                         subscriberId, scope.namespace.baseInfo.namespaceName);
            });

            init();

            function init() {
                initNamespace(scope.namespace);
                initOther();
            }

            function initNamespace(namespace, viewType) {
                namespace.hasBranch = false;
                namespace.currentOperateBranch = 'master';
                namespace.isBranch = false;
                namespace.isLinkedNamespace =
                    namespace.isPublic ? namespace.parentAppId != namespace.baseInfo.appId : false;
                namespace.showSearchInput = false;
                namespace.viewItems = namespace.items;
                namespace.isPropertiesFormat = namespace.format == 'properties';
                namespace.isTextEditing = false;
                namespace.instanceViewType = namespace_instance_view_type.LATEST_RELEASE;
                namespace.latestReleaseInstancesPage = 0;
                namespace.allInstances = [];
                namespace.allInstancesPage = 0;
                namespace.commitChangeBtnDisabled = false;

                initNamespaceBranch(namespace);
                initNamespaceViewName(namespace);
                initNamespaceLock(namespace);
                initNamespaceInstancesCount(namespace);
                initPermission(namespace);

                function initNamespaceBranch(namespace) {
                    NamespaceBranchService.findNamespaceBranch(scope.appId, scope.env,
                                                               namespace.baseInfo.clusterName,
                                                               namespace.baseInfo.namespaceName)
                        .then(function (result) {

                            if (!result.baseInfo) {
                                return;
                            }

                            //namespace has branch
                            namespace.hasBranch = true;
                            namespace.branchName = result.baseInfo.clusterName;
                            //init branch
                            namespace.branch = result;
                            namespace.branch.isBranch = true;
                            namespace.branch.parentNamespace = namespace;
                            namespace.branch.viewType = namespace_view_type.TABLE;
                            namespace.branch.isPropertiesFormat = namespace.format == 'properties';
                            namespace.branch.allInstances = [];//master namespace all instances
                            namespace.branch.latestReleaseInstances = [];
                            namespace.branch.latestReleaseInstancesPage = 0;
                            namespace.branch.instanceViewType = namespace_instance_view_type.LATEST_RELEASE;
                            namespace.branch.hasLoadInstances = false;

                            initBranchItems(namespace.branch);
                            initRules(namespace.branch);
                            loadInstanceInfo(namespace.branch);
                            initNamespaceLock(namespace.branch);
                            initPermission(namespace);
                            initUserOperateBranchScene(namespace);
                        });

                    function initBranchItems(branch) {
                        branch.masterItems = [];
                        branch.branchItems = [];

                        var masterItemsMap = {};
                        branch.parentNamespace.items.forEach(function (item) {
                            if (item.item.key) {
                                masterItemsMap[item.item.key] = item;
                            }
                        });

                        var branchItemsMap = {};

                        var itemModifiedCnt = 0;
                        branch.items.forEach(function (item) {
                            var key = item.item.key;
                            var masterItem = masterItemsMap[key];

                            //modify master item and set item's masterReleaseValue
                            if (masterItem) {
                                if (masterItem.isModified && masterItem.oldValue) {
                                    item.masterReleaseValue = masterItem.oldValue;
                                } else if (masterItem.item.value) {
                                    item.masterReleaseValue = masterItem.item.value;
                                }

                            } else {//delete branch item
                                item.masterReleaseValue = '';
                            }

                            //delete master item. ignore
                            if (item.isDeleted && masterItem) {
                                if (item.masterReleaseValue != item.oldValue) {
                                    itemModifiedCnt++;
                                    branch.branchItems.push(item);
                                }
                            } else {//branch's item
                                branchItemsMap[key] = item;

                                if (item.isModified) {
                                    itemModifiedCnt++;
                                }
                                branch.branchItems.push(item);
                            }

                        });
                        branch.itemModifiedCnt = itemModifiedCnt;

                        branch.parentNamespace.items.forEach(function (item) {
                            if (item.item.key) {
                                if (!branchItemsMap[item.item.key]) {
                                    branch.masterItems.push(item);
                                } else {
                                    item.hasBranchValue = true;
                                }
                            }
                        })

                    }
                }

                function initPermission(namespace) {

                    PermissionService.has_modify_namespace_permission(
                        scope.appId,
                        namespace.baseInfo.namespaceName)
                        .then(function (result) {
                            //branch has same permission
                            namespace.hasModifyPermission = result.hasPermission;
                            if (namespace.branch) {
                                namespace.branch.hasModifyPermission = result.hasPermission;
                            }
                        });

                    PermissionService.has_release_namespace_permission(
                        scope.appId,
                        namespace.baseInfo.namespaceName)
                        .then(function (result) {
                            //branch has same permission
                            namespace.hasReleasePermission = result.hasPermission;
                            if (namespace.branch) {
                                namespace.branch.hasReleasePermission = result.hasPermission;
                            }
                        });
                }

                function initNamespaceViewName(namespace) {
                    //namespace view name hide suffix
                    namespace.viewName =
                        namespace.baseInfo.namespaceName.replace(".xml", "").replace(
                            ".properties", "").replace(".json", "").replace(".yml", "")
                            .replace(".yaml", "");

                    if (!viewType) {
                        if (namespace.isPropertiesFormat) {
                            switchView(namespace, namespace_view_type.TABLE);
                        } else {
                            switchView(namespace, namespace_view_type.TEXT);
                        }
                    } else if (viewType == namespace_view_type.TABLE) {
                        namespace.viewType = namespace_view_type.TABLE;
                    }
                }

                function initNamespaceLock(namespace) {
                    NamespaceLockService.get_namespace_lock(
                        scope.appId, scope.env,
                        namespace.baseInfo.clusterName,
                        namespace.baseInfo.namespaceName)
                        .then(function (result) {
                            if (result.dataChangeCreatedBy) {
                                namespace.lockOwner = result.dataChangeCreatedBy;
                            } else {
                                namespace.lockOwner = "";
                            }
                        });

                }

                function initUserOperateBranchScene(namespace) {
                    var operateBranchStorage = JSON.parse(localStorage.getItem(operate_branch_storage_key));
                    var namespaceId = [scope.appId, scope.env, scope.cluster, namespace.baseInfo.namespaceName].join(
                        "+");
                    if (!operateBranchStorage) {
                        operateBranchStorage = {};
                    }
                    if (!operateBranchStorage[namespaceId]) {
                        operateBranchStorage[namespaceId] = namespace.branchName;
                    }

                    localStorage.setItem(operate_branch_storage_key, JSON.stringify(operateBranchStorage));

                    switchBranch(operateBranchStorage[namespaceId]);

                }
                
            }

            function initNamespaceInstancesCount(namespace) {
                InstanceService.getInstanceCountByNamespace(scope.appId,
                                                            scope.env,
                                                            scope.cluster,
                                                            namespace.baseInfo.namespaceName)
                    .then(function (result) {
                        namespace.instancesCount = result.num;
                    })
            }

            function initOther() {

                UserService.load_user().then(function (result) {
                    scope.currentUser = result.userId;
                });

                PermissionService.has_assign_user_permission(scope.appId)
                    .then(function (result) {
                        scope.hasAssignUserPermission = result.hasPermission;
                    }, function (result) {

                    });
            }

            function switchBranch(branchName) {
                if (branchName != 'master') {
                    initRules(scope.namespace.branch);
                }
                scope.namespace.currentOperateBranch = branchName;

                //save to local storage
                var operateBranchStorage = JSON.parse(localStorage.getItem(operate_branch_storage_key));
                if (!operateBranchStorage) {
                    return;
                }
                var namespaceId = [scope.appId, scope.env, scope.cluster, scope.namespace.baseInfo.namespaceName].join(
                    "+");
                operateBranchStorage[namespaceId] = branchName;
                localStorage.setItem(operate_branch_storage_key, JSON.stringify(operateBranchStorage));

            }

            function switchView(namespace, viewType) {
                namespace.viewType = viewType;
                if (namespace_view_type.TEXT == viewType) {
                    namespace.text = parseModel2Text(namespace);
                } else if (namespace_view_type.TABLE == viewType) {

                } else if (namespace_view_type.HISTORY == viewType) {
                    loadCommitHistory(namespace);
                } else if (namespace_view_type.INSTANCE == viewType) {
                    refreshInstancesInfo(namespace);
                }
            }

            function switchInstanceViewType(namespace, type) {
                namespace.instanceViewType = type;
                loadInstanceInfo(namespace);
            }

            function loadCommitHistory(namespace) {
                if (!namespace.commits) {
                    namespace.commits = [];
                    namespace.commitPage = 0;
                }

                var size = 10;
                CommitService.find_commits(scope.appId,
                                           scope.env,
                                           namespace.baseInfo.clusterName,
                                           namespace.baseInfo.namespaceName,
                                           namespace.commitPage,
                                           size)
                    .then(function (result) {
                        if (result.length < size) {
                            namespace.hasLoadAllCommit = true;
                        }

                        for (var i = 0; i < result.length; i++) {
                            //to json
                            result[i].changeSets = JSON.parse(result[i].changeSets);
                            namespace.commits.push(result[i]);
                        }
                        namespace.commitPage += 1;
                    }, function (result) {
                        toastr.error(AppUtil.errorMsg(result), "加载修改历史记录出错");
                    });
            }

            function loadInstanceInfo(namespace) {
                var size = 20;
                if (namespace.isBranch) {
                    size = 2000;
                }

                var type = namespace.instanceViewType;

                if (namespace_instance_view_type.LATEST_RELEASE == type) {
                    if (!namespace.latestRelease) {
                        ReleaseService.findActiveReleases(scope.appId,
                                                          scope.env,
                                                          namespace.baseInfo.clusterName,
                                                          namespace.baseInfo.namespaceName,
                                                          0, 1).then(function (result) {

                            var latestRelease = result[0];
                            if (!latestRelease) {
                                namespace.latestReleaseInstances = {};
                                namespace.latestReleaseInstances.total = 0;
                                return;
                            }
                            namespace.latestRelease = latestRelease;
                            InstanceService.findInstancesByRelease(scope.env,
                                                                   latestRelease.id,
                                                                   namespace.latestReleaseInstancesPage,
                                                                   size)
                                .then(function (result) {
                                    namespace.latestReleaseInstances = result;
                                    namespace.latestReleaseInstancesPage++;
                                })
                        });
                    } else {
                        InstanceService.findInstancesByRelease(scope.env,
                                                               namespace.latestRelease.id,
                                                               namespace.latestReleaseInstancesPage,
                                                               size)
                            .then(function (result) {
                                if (result && result.content.length) {
                                    namespace.latestReleaseInstancesPage++;
                                    result.content.forEach(function (instance) {
                                        namespace.latestReleaseInstances.content.push(
                                            instance);
                                    })
                                }

                            })
                    }

                } else if (namespace_instance_view_type.NOT_LATEST_RELEASE == type) {
                    if (!namespace.latestRelease) {
                        return;
                    }
                    InstanceService.findByReleasesNotIn(scope.appId,
                                                        scope.env,
                                                        scope.cluster,
                                                        namespace.baseInfo.namespaceName,
                                                        namespace.latestRelease.id)
                        .then(function (result) {
                            if (!result || result.length == 0) {
                                return
                            }

                            var groupedInstances = {},
                                notLatestReleases = [];

                            result.forEach(function (instance) {
                                var configs = instance.configs;
                                if (configs && configs.length > 0) {
                                    configs.forEach(function (instanceConfig) {
                                        var release = instanceConfig.release;
                                        //filter dirty data
                                        if (!release) {
                                            return;
                                        }
                                        if (!groupedInstances[release.id]) {
                                            groupedInstances[release.id] = [];
                                            notLatestReleases.push(release);
                                        }
                                        groupedInstances[release.id].push(instance);
                                    })
                                }
                            });

                            namespace.notLatestReleases = notLatestReleases;
                            namespace.notLatestReleaseInstances = groupedInstances;
                        })

                } else {
                    InstanceService.findInstancesByNamespace(scope.appId,
                                                             scope.env,
                                                             scope.cluster,
                                                             namespace.baseInfo.namespaceName,
                                                             '',
                                                             namespace.allInstancesPage)
                        .then(function (result) {
                            if (result && result.content.length) {
                                namespace.allInstancesPage++;
                                result.content.forEach(function (instance) {
                                    namespace.allInstances.push(instance);
                                })
                            }
                        });
                }

            }

            function refreshInstancesInfo(namespace) {

                namespace.instanceViewType = namespace_instance_view_type.LATEST_RELEASE;

                namespace.latestReleaseInstancesPage = 0;
                namespace.latestReleaseInstances = [];
                namespace.latestRelease = undefined;

                if (!namespace.isBranch) {
                    namespace.notLatestReleaseNames = [];
                    namespace.notLatestReleaseInstances = {};

                    namespace.allInstancesPage = 0;
                    namespace.allInstances = [];
                }

                initNamespaceInstancesCount(namespace);
                loadInstanceInfo(namespace);
            }

            function initRules(branch) {

                NamespaceBranchService.findBranchGrayRules(scope.appId,
                                                           scope.env,
                                                           scope.cluster,
                                                           scope.namespace.baseInfo.namespaceName,
                                                           branch.baseInfo.clusterName)
                    .then(function (result) {
                        
                        if (result.appId) {
                            branch.rules = result;
                        }
                        
                    }, function (result) {
                        toastr.error(AppUtil.errorMsg(result), "加载灰度规则出错");
                    });

            }

            function addRuleItem(branch) {
                var newRuleItem = {
                    clientAppId: !branch.parentNamespace.isPublic ? branch.baseInfo.appId : '',
                    clientIpList: [],
                    draftIpList: [],
                    isNew: true
                };

                branch.editingRuleItem = newRuleItem;

                EventManager.emit(EventManager.EventType.EDIT_GRAY_RELEASE_RULES, {
                    branch: branch
                });
            }

            function editRuleItem(branch, ruleItem) {
                ruleItem.isNew = false;
                ruleItem.draftIpList = _.clone(ruleItem.clientIpList);
                branch.editingRuleItem = ruleItem;

                EventManager.emit(EventManager.EventType.EDIT_GRAY_RELEASE_RULES, {
                    branch: branch
                });
            }

            function deleteRuleItem(branch, ruleItem) {
                branch.rules.ruleItems.forEach(function (item, index) {
                    if (item.clientAppId == ruleItem.clientAppId) {
                        branch.rules.ruleItems.splice(index, 1);
                        toastr.success("删除成功");
                    }
                });

                useRules(branch);
            }

            function useRules(branch) {
                NamespaceBranchService.updateBranchGrayRules(scope.appId,
                                                             scope.env,
                                                             scope.cluster,
                                                             scope.namespace.baseInfo.namespaceName,
                                                             branch.baseInfo.clusterName,
                                                             branch.rules
                    )
                    .then(function (result) {
                        toastr.success('灰度规则更新成功');

                        //show tips if branch has not release configs
                        if (branch.itemModifiedCnt) {
                            AppUtil.showModal("#updateRuleTips");
                        }

                        setTimeout(function () {
                            refreshInstancesInfo(branch);
                        }, 1500);

                    }, function (result) {
                        AppUtil.showErrorMsg(result, "灰度规则更新失败");
                    })
            }

            function toggleTextEditStatus(namespace) {
                if (!scope.lockCheck(namespace)) {
                    return;
                }
                namespace.isTextEditing = !namespace.isTextEditing;
                if (namespace.isTextEditing) {//切换为编辑状态
                    namespace.commited = false;
                    namespace.backupText = namespace.text;
                    namespace.editText = parseModel2Text(namespace);

                } else {
                    if (!namespace.commited) {//取消编辑,则复原
                        namespace.text = namespace.backupText;
                    }
                }
            }

            function goToSyncPage(namespace) {
                if (!scope.lockCheck(namespace)) {
                    return false;
                }
                $window.location.href =
                    "config/sync.html?#/appid=" + scope.appId + "&env="
                    + scope.env + "&clusterName="
                    + scope.cluster
                    + "&namespaceName=" + namespace.baseInfo.namespaceName;
            }

            function modifyByText(namespace) {
                var model = {
                    configText: namespace.editText,
                    namespaceId: namespace.baseInfo.id,
                    format: namespace.format
                };

                //prevent repeat submit
                if (namespace.commitChangeBtnDisabled) {
                    return;
                }
                namespace.commitChangeBtnDisabled = true;
                ConfigService.modify_items(scope.appId,
                                           scope.env,
                                           scope.cluster,
                                           namespace.baseInfo.namespaceName,
                                           model).then(
                    function (result) {
                        toastr.success("更新成功, 如需生效请发布");
                        //refresh all namespace items
                        EventManager.emit(EventManager.EventType.REFRESH_NAMESPACE,
                                          {
                                              namespace: namespace
                                          });
                        return true;

                    }, function (result) {
                        toastr.error(AppUtil.errorMsg(result), "更新失败");
                        namespace.commitChangeBtnDisabled = false;
                        return false;
                    }
                );
                namespace.commited = true;
                toggleTextEditStatus(namespace);
            }

            function goToParentAppConfigPage(namespace) {
                $window.location.href = "/config.html?#/appid=" + namespace.parentAppId;
                $window.location.reload();
            }

            function parseModel2Text(namespace) {

                if (namespace.items.length == 0) {
                    namespace.itemCnt = MIN_ROW_SIZE;
                    return "";
                }

                //文件模式
                if (!namespace.isPropertiesFormat) {
                    return parseNotPropertiesText(namespace);
                } else {
                    return parsePropertiesText(namespace);
                }

            }

            function parseNotPropertiesText(namespace) {
                var text = namespace.items[0].item.value;
                var lineNum = text.split("\n").length;
                namespace.itemCnt = lineNum < MIN_ROW_SIZE ? MIN_ROW_SIZE : lineNum;
                return text;
            }

            function parsePropertiesText(namespace) {
                var result = "";
                var itemCnt = 0;
                namespace.items.forEach(function (item) {
                    //deleted key
                    if (!item.item.dataChangeLastModifiedBy) {
                        return;
                    }
                    if (item.item.key) {
                        //use string \n to display as new line
                        var itemValue = item.item.value.replace(/\n/g, "\\n");

                        result +=
                            item.item.key + " = " + itemValue + "\n";
                    } else {
                        result += item.item.comment + "\n";
                    }
                    itemCnt++;
                });

                namespace.itemCnt = itemCnt < MIN_ROW_SIZE ? MIN_ROW_SIZE : itemCnt;
                return result;
            }

            function toggleItemSearchInput(namespace) {
                namespace.showSearchInput = !namespace.showSearchInput;
            }

            function searchItems(namespace) {
                var searchKey = namespace.searchKey.toLowerCase();
                var items = [];
                namespace.items.forEach(function (item) {
                    var key = item.item.key;
                    if (key && key.toLowerCase().indexOf(searchKey) >= 0) {
                        items.push(item);
                    }
                });
                namespace.viewItems = items;
            }

            //normal release and gray release
            function publish(namespace) {

                if (!namespace.hasReleasePermission) {
                    AppUtil.showModal('#releaseNoPermissionDialog');
                    return;
                } else if (namespace.lockOwner && scope.user == namespace.lockOwner) {
                    //can not publish if config modified by himself
                    AppUtil.showModal('#releaseDenyDialog');
                    return;
                }

                if (namespace.isBranch) {
                    namespace.mergeAndPublish = false;
                }

                EventManager.emit(EventManager.EventType.PUBLISH_NAMESPACE,
                                  {
                                      namespace: namespace
                                  });
            }

            function mergeAndPublish(branch) {
                var parentNamespace = branch.parentNamespace;
                if (!parentNamespace.hasReleasePermission) {
                    AppUtil.showModal('#releaseNoPermissionDialog');
                }else if (branch.lockOwner && scope.user == branch.lockOwner) {
                    AppUtil.showModal('#releaseDenyDialog');
                } else if (parentNamespace.itemModifiedCnt > 0) {
                    AppUtil.showModal('#mergeAndReleaseDenyDialog');
                }
                else {
                    EventManager.emit(EventManager.EventType.MERGE_AND_PUBLISH_NAMESPACE, {branch: branch});
                }
            }

            function rollback(namespace) {
                EventManager.emit(EventManager.EventType.PRE_ROLLBACK_NAMESPACE, {namespace: namespace});
            }

        }
    }
}

