/**
 *  Copyright 2005-2015 Red Hat, Inc.
 *
 *  Red Hat 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 io.fabric8.git.internal;

import java.io.IOException;
import java.net.URL;

import io.fabric8.api.jcip.ThreadSafe;
import io.fabric8.api.scr.AbstractComponent;
import io.fabric8.api.scr.ValidatingReference;
import io.fabric8.common.util.Closeables;
import io.fabric8.common.util.Strings;
import io.fabric8.git.GitNode;
import io.fabric8.git.GitService;
import io.fabric8.groups.Group;
import io.fabric8.groups.GroupListener;
import io.fabric8.groups.internal.ZooKeeperGroup;
import io.fabric8.zookeeper.ZkPath;
import org.apache.curator.framework.CuratorFramework;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.eclipse.jgit.transport.URIish;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.fabric8.zookeeper.utils.ZooKeeperUtils.getSubstitutedData;

@ThreadSafe
@Component(name = "io.fabric8.git.master.listener", label = "Fabric8 Git Master Listener", immediate = true, metatype = false)
public final class GitMasterListener extends AbstractComponent implements GroupListener<GitNode> {

    private static final Logger LOGGER = LoggerFactory.getLogger(GitMasterListener.class);

    @Reference(referenceInterface = GitService.class)
    private final ValidatingReference<GitService> gitService = new ValidatingReference<GitService>();
    @Reference(referenceInterface = CuratorFramework.class)
    private final ValidatingReference<CuratorFramework> curator = new ValidatingReference<CuratorFramework>();

    private Group<GitNode> group;

    @Activate
    void activate() throws IOException {
        activateComponent();
        group = new ZooKeeperGroup<GitNode>(curator.get(), ZkPath.GIT.getPath(), GitNode.class);
        group.add(this);
        group.start();
    }

    @Deactivate
    void deactivate() {
        deactivateComponent();
        group.remove(this);
        Closeables.closeQuietly(group);
    }

    @Override
    public void groupEvent(Group<GitNode> group, GroupListener.GroupEvent event) {
        if (isValid()) {
            switch (event) {
            case CONNECTED:
            case CHANGED:
                updateMasterUrl(group);
                break;
            }
        }
    }

    /**
     * Updates the git master url, if needed.
     */
    private void updateMasterUrl(Group<GitNode> group) {
        GitNode master = group.master();
        String masterUrl = master != null ? master.getUrl() : null;
        try {
            if (masterUrl != null) {
                GitService gitservice = gitService.get();
                String substitutedUrl = getSubstitutedData(curator.get(), masterUrl);
                if (!Strings.isNotBlank(substitutedUrl)) {
                    LOGGER.warn("Could not render git master URL {}.", masterUrl);
                }
                //Catch any possible issue indicating that the URL is invalid.
                URIish uri = new URIish(substitutedUrl);
                gitservice.notifyRemoteChanged(substitutedUrl);
            }
        } catch (Exception e) {
            LOGGER.error("Failed to point origin to the new master.", e);
        }
    }

    void bindCurator(CuratorFramework curator) {
        this.curator.bind(curator);
    }

    void unbindCurator(CuratorFramework curator) {
        this.curator.unbind(curator);
    }

    void bindGitService(GitService service) {
        this.gitService.bind(service);
    }

    void unbindGitService(GitService service) {
        this.gitService.unbind(service);
    }

}
