/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.shardingsphere.governance.core.registry.config.service.impl;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.eventbus.Subscribe;
import org.apache.shardingsphere.authority.api.config.AuthorityRuleConfiguration;
import org.apache.shardingsphere.governance.core.registry.config.service.GlobalRegistryService;
import org.apache.shardingsphere.governance.core.registry.config.node.GlobalNode;
import org.apache.shardingsphere.governance.core.registry.state.node.StatesNode;
import org.apache.shardingsphere.governance.repository.spi.RegistryCenterRepository;
import org.apache.shardingsphere.infra.config.RuleConfiguration;
import org.apache.shardingsphere.infra.metadata.mapper.event.dcl.impl.CreateUserStatementEvent;
import org.apache.shardingsphere.infra.metadata.mapper.event.dcl.impl.GrantStatementEvent;
import org.apache.shardingsphere.infra.metadata.user.ShardingSphereUser;
import org.apache.shardingsphere.infra.metadata.user.ShardingSphereUsers;
import org.apache.shardingsphere.infra.metadata.user.yaml.config.YamlUsersConfigurationConverter;
import org.apache.shardingsphere.infra.yaml.engine.YamlEngine;
import org.apache.shardingsphere.infra.yaml.swapper.YamlRuleConfigurationSwapperEngine;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Optional;

/**
 * Global rule registry service.
 */
public final class GlobalRuleRegistryService implements GlobalRegistryService<Collection<RuleConfiguration>> {
    
    private final RegistryCenterRepository repository;
    
    public GlobalRuleRegistryService(final RegistryCenterRepository repository) {
        this.repository = repository;
    }
    
    @Override
    public void persist(final Collection<RuleConfiguration> globalRuleConfigs, final boolean isOverwrite) {
        if (!globalRuleConfigs.isEmpty() && (isOverwrite || !isExisted())) {
            repository.persist(GlobalNode.getGlobalRuleNode(), YamlEngine.marshal(new YamlRuleConfigurationSwapperEngine().swapToYamlRuleConfigurations(globalRuleConfigs)));
        }
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public Collection<RuleConfiguration> load() {
        return isExisted()
                ? new YamlRuleConfigurationSwapperEngine().swapToRuleConfigurations(YamlEngine.unmarshal(repository.get(GlobalNode.getGlobalRuleNode()), Collection.class))
                : Collections.emptyList();
    }
    
    private boolean isExisted() {
        return !Strings.isNullOrEmpty(repository.get(GlobalNode.getGlobalRuleNode()));
    }
    
    
    /**
     * Update when user created.
     *
     * @param event create user statement event
     */
    @Subscribe
    public void update(final CreateUserStatementEvent event) {
        Collection<RuleConfiguration> globalRuleConfigs = load();
        Optional<AuthorityRuleConfiguration> authorityRuleConfig = globalRuleConfigs.stream().filter(each -> each instanceof AuthorityRuleConfiguration)
                .findAny().map(each -> (AuthorityRuleConfiguration) each);
        Preconditions.checkState(authorityRuleConfig.isPresent());
        refreshAuthorityRuleConfiguration(authorityRuleConfig.get(), event.getUsers());
        persist(globalRuleConfigs, true);
    }
    
    /**
     * Update when granted.
     *
     * @param event grant statement event
     */
    @Subscribe
    public void update(final GrantStatementEvent event) {
        if (!event.getUsers().isEmpty()) {
            repository.persist(StatesNode.getPrivilegeNodePath(), YamlEngine.marshal(YamlUsersConfigurationConverter.convertYamlUserConfigurations(event.getUsers())));
        }
    }
    
    private void refreshAuthorityRuleConfiguration(final AuthorityRuleConfiguration authorityRuleConfig, final Collection<ShardingSphereUser> createdUsers) {
        Collection<ShardingSphereUser> oldUsers = authorityRuleConfig.getUsers();
        Collection<ShardingSphereUser> newUsers = oldUsers.isEmpty() ? createdUsers : getChangedUsers(oldUsers, createdUsers);
        authorityRuleConfig.getUsers().removeAll(oldUsers);
        authorityRuleConfig.getUsers().addAll(newUsers);
    }
    
    private Collection<ShardingSphereUser> getChangedUsers(final Collection<ShardingSphereUser> oldUsers, final Collection<ShardingSphereUser> newUsers) {
        Collection<ShardingSphereUser> result = new LinkedList<>(oldUsers);
        ShardingSphereUsers shardingSphereUsers = new ShardingSphereUsers(oldUsers);
        for (ShardingSphereUser each : newUsers) {
            shardingSphereUsers.findUser(each.getGrantee()).ifPresent(result::remove);
            result.add(each);
        }
        return result;
    }
}

@@ -1157,22 +1157,19 @@
 fig.
-servic
+nod
 e.Global
 Regi
@@ -1164,30 +1164,19 @@
 e.Global
-RegistryServic
+Nod
 e;%0Aimpor
@@ -1231,35 +1231,38 @@
 stry.config.
-nod
+servic
 e.Global
 Node;%0Aimport
@@ -1241,35 +1241,46 @@
 g.service.Global
-Nod
+RegistryServic
 e;%0Aimport org.ap
@@ -1489,32 +1489,104 @@
 eConfiguration;%0A
+import org.apache.shardingsphere.infra.eventbus.ShardingSphereEventBus;%0A
 import org.apach
@@ -1760,155 +1760,8 @@
 nt;%0A
-import org.apache.shardingsphere.infra.metadata.user.ShardingSphereUser;%0Aimport org.apache.shardingsphere.infra.metadata.user.ShardingSphereUsers;%0A
 impo
@@ -2069,37 +2069,8 @@
 ns;%0A
-import java.util.LinkedList;%0A
 impo
@@ -2426,24 +2426,85 @@
 repository;%0A
+        ShardingSphereEventBus.getInstance().register(this);%0A
     %7D%0A    %0A 
@@ -3380,21 +3380,16 @@
  %7D%0A    %0A
-    %0A
     /**%0A
@@ -3924,35 +3924,33 @@
 nt()
-);%0A        refreshA
+, %22No available a
 uthority
 Rule
@@ -3945,34 +3945,50 @@
 uthority
-RuleConfiguration(
+ rules for governance.%22);%0A        
 authorit
@@ -4004,18 +4004,35 @@
 ig.get()
-, 
+.getUsers().addAll(
 event.ge
@@ -4468,24 +4468,24 @@
 Users())));%0A
+
         %7D%0A  
@@ -4492,1029 +4492,6 @@
   %7D%0A
-    %0A    private void refreshAuthorityRuleConfiguration(final AuthorityRuleConfiguration authorityRuleConfig, final Collection%3CShardingSphereUser%3E createdUsers) %7B%0A        Collection%3CShardingSphereUser%3E oldUsers = authorityRuleConfig.getUsers();%0A        Collection%3CShardingSphereUser%3E newUsers = oldUsers.isEmpty() ? createdUsers : getChangedUsers(oldUsers, createdUsers);%0A        authorityRuleConfig.getUsers().removeAll(oldUsers);%0A        authorityRuleConfig.getUsers().addAll(newUsers);%0A    %7D%0A    %0A    private Collection%3CShardingSphereUser%3E getChangedUsers(final Collection%3CShardingSphereUser%3E oldUsers, final Collection%3CShardingSphereUser%3E newUsers) %7B%0A        Collection%3CShardingSphereUser%3E result = new LinkedList%3C%3E(oldUsers);%0A        ShardingSphereUsers shardingSphereUsers = new ShardingSphereUsers(oldUsers);%0A        for (ShardingSphereUser each : newUsers) %7B%0A            shardingSphereUsers.findUser(each.getGrantee()).ifPresent(result::remove);%0A            result.add(each);%0A        %7D%0A        return result;%0A    %7D%0A
 %7D%0A

Add event bus register in GlobalRuleRegistryService (#10657)