package com.ctrip.hermes.producer;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unidal.lookup.annotation.Inject;
import org.unidal.lookup.annotation.Named;

import com.ctrip.hermes.core.constants.CatConstants;
import com.ctrip.hermes.core.exception.MessageSendException;
import com.ctrip.hermes.core.message.ProducerMessage;
import com.ctrip.hermes.core.meta.MetaService;
import com.ctrip.hermes.core.pipeline.Pipeline;
import com.ctrip.hermes.core.result.CompletionCallback;
import com.ctrip.hermes.core.result.SendResult;
import com.ctrip.hermes.core.service.SystemClockService;
import com.ctrip.hermes.core.utils.StringUtils;
import com.ctrip.hermes.meta.entity.Storage;
import com.ctrip.hermes.meta.entity.Topic;
import com.ctrip.hermes.producer.api.Producer;
import com.ctrip.hermes.producer.build.BuildConstants;
import com.ctrip.hermes.producer.config.ProducerConfig;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;

@Named(type = Producer.class)
public class DefaultProducer extends Producer {

	private static final Logger log = LoggerFactory.getLogger(DefaultProducer.class);

	@Inject(BuildConstants.PRODUCER)
	private Pipeline<Future<SendResult>> m_pipeline;

	@Inject
	private SystemClockService m_systemClockService;

	@Inject
	private MetaService m_metaService;

	@Inject
	private ProducerConfig m_config;

	@Override
	public DefaultMessageHolder message(String topic, String partitionKey, Object body) {
		if (StringUtils.isBlank(topic)) {
			throw new IllegalArgumentException("Topic can not be null or empty.");
		}
		return new DefaultMessageHolder(topic, partitionKey, body);
	}

	class DefaultMessageHolder implements MessageHolder {
		private ProducerMessage<Object> m_msg;

		public DefaultMessageHolder(String topic, String partitionKey, Object body) {
			m_msg = new ProducerMessage<Object>(topic, body);
			m_msg.setPartitionKey(partitionKey);
		}

		@Override
		public Future<SendResult> send() {
			Transaction t = null;
			if (m_config.isCatEnabled()) {
				t = Cat.newTransaction(CatConstants.TYPE_MESSAGE_PRODUCE_TRIED, m_msg.getTopic());
			}
			try {
				Topic topic = m_metaService.findTopicByName(m_msg.getTopic());

				if (topic == null) {
					log.error("Topic {} not found.", m_msg.getTopic());
					throw new IllegalArgumentException(String.format("Topic %s not found.", m_msg.getTopic()));
				}

				if (Storage.KAFKA.equals(topic.getStorageType())) {
					m_msg.setWithCatTrace(false);
				}
				m_msg.setBornTime(m_systemClockService.now());
				Future<SendResult> future = m_pipeline.put(m_msg);

				if (t != null) {
					t.setStatus(Transaction.SUCCESS);
				}
				return future;
			} catch (Exception e) {
				if (t != null) {
					Cat.logError(e);
					t.setStatus(e);
				}
				throw e;
			} finally {
				if (t != null) {
					t.complete();
				}
			}
		}

		@Override
		public MessageHolder withRefKey(String key) {
			if (key != null && key.length() > 90) {
				throw new IllegalArgumentException(String.format(
				      "RefKey's length must not larger than 90 characters(refKey=%s)", key));
			}

			m_msg.setKey(key);
			return this;
		}

		@Override
		public MessageHolder withPriority() {
			m_msg.setPriority(true);
			return this;
		}

		@Override
		public MessageHolder addProperty(String key, String value) {
			m_msg.addDurableAppProperty(key, value);
			return this;
		}

		@Override
		public MessageHolder setCallback(CompletionCallback<SendResult> callback) {
			m_msg.setCallback(callback);
			return this;
		}

		@Override
		public SendResult sendSync() throws MessageSendException {
			try {
				return send().get();
			} catch (ExecutionException e) {
				throw new MessageSendException(e, m_msg);
			} catch (InterruptedException e) {
				throw new MessageSendException(e, m_msg);
			}
		}

	}
}