package io.cattle.platform.api.pubsub.manager;

import io.cattle.platform.api.auth.Policy;
import io.cattle.platform.api.pubsub.model.Subscribe;
import io.cattle.platform.api.pubsub.subscribe.SubscriptionHandler;
import io.cattle.platform.api.pubsub.util.SubscriptionUtils;
import io.cattle.platform.api.pubsub.util.SubscriptionUtils.SubscriptionStyle;
import io.cattle.platform.api.utils.ApiUtils;
import io.cattle.platform.framework.event.FrameworkEvents;
import io.cattle.platform.iaas.event.IaasEvents;
import io.github.ibuildthecloud.gdapi.condition.Condition;
import io.github.ibuildthecloud.gdapi.context.ApiContext;
import io.github.ibuildthecloud.gdapi.factory.SchemaFactory;
import io.github.ibuildthecloud.gdapi.model.ListOptions;
import io.github.ibuildthecloud.gdapi.request.ApiRequest;
import io.github.ibuildthecloud.gdapi.request.resource.impl.AbstractNoOpResourceManager;
import io.github.ibuildthecloud.gdapi.util.ProxyUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;

public class SubscribeManager extends AbstractNoOpResourceManager {

    public static final String EVENT_DISCONNECT = "disconnect";
    List<SubscriptionHandler> handlers = new ArrayList<SubscriptionHandler>();

    @Override
    public Class<?>[] getTypeClasses() {
        return new Class<?>[] { Subscribe.class };
    }

    @Override
    protected Object createInternal(String type, ApiRequest request) {
        List<String> eventNames = getEventNames(request);
        Set<String> filteredEventNames = new TreeSet<String>();

        Policy policy = ApiUtils.getPolicy();

        SubscriptionStyle style = SubscriptionUtils.getSubscriptionStyle(policy);
        for (String eventName : eventNames) {
            switch (style) {
            case QUALIFIED:
                if (eventName.contains(FrameworkEvents.EVENT_SEP)) {
                    eventName = StringUtils.substringBefore(eventName, FrameworkEvents.EVENT_SEP);
                }

                String key = SubscriptionUtils.getSubscriptionQualifier(policy);
                String value = SubscriptionUtils.getSubscriptionQualifierValue(policy);
                eventName = String.format("%s%s%s=%s", eventName, FrameworkEvents.EVENT_SEP, key, value);
                break;
            case RAW:
                break;
            }

            filteredEventNames.add(eventName);
        }

        if (SubscriptionStyle.QUALIFIED.equals(style)) {
                String value = "" + policy.getAuthenticatedAsAccountId();
                String eventName = String.format("%s%s%s=%s", EVENT_DISCONNECT, FrameworkEvents.EVENT_SEP, IaasEvents.ACCOUNT_QUALIFIER, value);
                filteredEventNames.add(eventName);
        }
        request.setResponseContentType("text/plain");

        try {
            for (SubscriptionHandler handler : handlers) {
                if (handler.subscribe(filteredEventNames, request, style != SubscriptionStyle.RAW)) {
                    return new Object();
                }
            }
        } catch (IOException e) {
            throw new IllegalStateException("Failed to subscribe to [" + filteredEventNames + "]", e);
        }

        return super.createInternal(type, request);
    }

    protected List<String> getEventNames(ApiRequest request) {
        Subscribe subscribe = request.proxyRequestObject(Subscribe.class);

        List<String> eventNames = subscribe.getEventNames();
        if (eventNames != null) {
            return eventNames;
        }

        eventNames = new ArrayList<String>();
        Map<String, List<Condition>> conditions = request.getConditions();
        if (conditions == null) {
            return eventNames;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        List<?> list = ProxyUtils.proxy((Map) conditions, Subscribe.class).getEventNames();
        if (list != null) {
            for (Object condition : list) {
                if (condition instanceof Condition) {
                    Object value = ((Condition) condition).getValue();
                    if (value != null) {
                        eventNames.add(value.toString());
                    }
                }
            }
        }

        return eventNames;
    }

    @Override
    protected Object listInternal(SchemaFactory schemaFactory, String type, Map<Object, Object> criteria, ListOptions options) {
        return createInternal(type, ApiContext.getContext().getApiRequest());
    }

    public List<SubscriptionHandler> getHandlers() {
        return handlers;
    }

    @Inject
    public void setHandlers(List<SubscriptionHandler> handlers) {
        this.handlers = handlers;
    }

}
