/*
 *  Copyright 2018, Yahoo Inc.
 *  Licensed under the terms of the Apache License, Version 2.0.
 *  See the LICENSE file associated with the project for terms.
 */
package com.yahoo.bullet.storm;

import com.yahoo.bullet.pubsub.PubSubException;
import com.yahoo.bullet.pubsub.PubSubMessage;
import com.yahoo.bullet.pubsub.Publisher;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.IRichBolt;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.tuple.Tuple;

import java.util.Map;

@Slf4j
public abstract class PublisherBolt extends ConfigComponent implements IRichBolt {
    private static final long serialVersionUID = -3198734196446239476L;

    private transient OutputCollector collector;

    // Exposed for testing only
    @Getter(AccessLevel.PACKAGE)
    protected transient Publisher publisher;

    /**
     * Creates an instance of this class with the given non-null config.
     *
     * @param config The non-null {@link BulletStormConfig} which is the config for this component.
     */
    public PublisherBolt(BulletStormConfig config) {
        super(config);
    }

    @Override
    public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
        // Add the Storm Config and the context as is, in case any PubSubs need it.
        config.set(BulletStormConfig.STORM_CONFIG, conf);
        config.set(BulletStormConfig.STORM_CONTEXT, context);

        this.collector = collector;
        try {
            this.publisher = createPublisher();
        } catch (PubSubException e) {
            throw new RuntimeException("Cannot create PubSub instance or a Publisher for it.", e);
        }
    }

    /**
     * Creates an instance of {@link Publisher} for use.
     *
     * @return A created publisher instance.
     * @throws PubSubException if there were issues creating one.
     */
    protected abstract Publisher createPublisher() throws PubSubException;

    /**
     * Publishes a message through the {@link Publisher} instance and acks the tuple even if the publishing failed.
     *
     * @param message The {@link PubSubMessage} to publish.
     * @param tuple The {@link Tuple} to ack.
     */
    protected void publish(PubSubMessage message, Tuple tuple) {
        try {
            publisher.send(message);
        } catch (PubSubException e) {
            log.error(e.getMessage());
        }
        collector.ack(tuple);
    }

    @Override
    public void cleanup() {
        try {
            publisher.close();
        } catch (Exception e) {
            log.error("Could not close Publisher.", e);
        }
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
    }
}