package com.atlassian.stash.internal.user;

import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.plugin.event.events.PluginFrameworkShuttingDownEvent;
import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
import com.atlassian.plugin.osgi.container.OsgiContainerManager;
import com.atlassian.bitbucket.permission.EffectivePermissionsChangedEvent;
import com.atlassian.bitbucket.permission.EffectivePermissionsProvider;
import com.google.common.collect.Maps;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.Set;

@Component("effectivePermissionsProviderTracker")
public class DefaultEffectivePermissionsProviderTracker implements
        ServiceTrackerCustomizer<EffectivePermissionsProvider, EffectivePermissionsProvider>,
        EffectivePermissionsProviderTracker {
    private static final Logger log = LoggerFactory.getLogger(DefaultEffectivePermissionsProviderTracker.class);

    private final OsgiContainerManager containerManager;
    private final EventPublisher eventPublisher;
    private final Set<EffectivePermissionsProvider> providers;

    private BundleContext bundleContext;
    private ServiceTracker<EffectivePermissionsProvider, EffectivePermissionsProvider> serviceTracker;

    @Autowired
    public DefaultEffectivePermissionsProviderTracker(OsgiContainerManager containerManager, EventPublisher eventPublisher) {
        this.containerManager = containerManager;
        this.eventPublisher = eventPublisher;
        this.providers = Collections.newSetFromMap(Maps.<EffectivePermissionsProvider, Boolean>newConcurrentMap());
    }

    @Override
    public EffectivePermissionsProvider addingService(ServiceReference<EffectivePermissionsProvider> serviceReference) {
        EffectivePermissionsProvider provider = bundleContext.getService(serviceReference);

        if (provider == null) {
            log.debug("Failed to resolve EffectivePermissionsProvider from bundle {} for reference {}",
                    serviceReference.getBundle(), serviceReference);
            return null;
        }

        providers.add(provider);
        fireEvent();
        return provider;
    }

    @Nonnull
    @Override
    public Set<EffectivePermissionsProvider> getProviders() {
        return Collections.unmodifiableSet(providers);
    }

    @Override
    public void modifiedService(ServiceReference<EffectivePermissionsProvider> reference, EffectivePermissionsProvider service) {
        // ignore
    }

    @EventListener
    public void onPluginFrameworkShuttingDown(PluginFrameworkShuttingDownEvent event) {
        providers.clear();
        try {
            fireEvent();
        } finally {
            if (serviceTracker != null) {
                serviceTracker.close();
            }
        }
    }

    @EventListener
    public void onPluginFrameworkStarted(PluginFrameworkStartedEvent event) {
        bundleContext = containerManager.getBundles()[0].getBundleContext();
        serviceTracker = new ServiceTracker<>(bundleContext, EffectivePermissionsProvider.class, this);
        serviceTracker.open();
    }

    @Override
    public void removedService(ServiceReference<EffectivePermissionsProvider> reference, EffectivePermissionsProvider service) {
        bundleContext.ungetService(reference);
        if (!providers.remove(service)) {
            log.debug("Removed service EffectivePermissionsProvider of type {} for reference {} was not being tracked",
                    service.getClass().getName(), reference);
            return;
        }

        fireEvent();
    }

    private void fireEvent() {
        eventPublisher.publish(new EffectivePermissionsChangedEvent(this));
    }
}
