/**
 * Copyright © 2016 Jeremy Custenborder ([email protected])
 *
 * Licensed 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 com.github.jcustenborder.kafka.connect.splunk;

import com.fasterxml.jackson.core.JsonFactory;
import com.github.jcustenborder.kafka.connect.utils.VersionUtil;
import com.github.jcustenborder.kafka.connect.utils.data.SourceRecordConcurrentLinkedDeque;
import org.apache.kafka.connect.errors.ConnectException;
import org.apache.kafka.connect.source.SourceRecord;
import org.apache.kafka.connect.source.SourceTask;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class SplunkHttpSourceTask extends SourceTask {
  static final Logger log = LoggerFactory.getLogger(SplunkHttpSourceTask.class);
  SplunkHttpSourceConnectorConfig config;
  Server server;
  EventServlet eventServlet;
  SourceRecordConcurrentLinkedDeque sourceRecordConcurrentLinkedDeque;

  @Override
  public String version() {
    return VersionUtil.version(this.getClass());
  }

  @Override
  public void start(Map<String, String> map) {
    this.config = new SplunkHttpSourceConnectorConfig(map);
    this.server = new Server();

    HttpConfiguration httpsConfiguration = new HttpConfiguration();
    httpsConfiguration.addCustomizer(new SecureRequestCustomizer());

    SslContextFactory sslContextFactory = this.config.sslContextFactory();
    ServerConnector sslConnector = new ServerConnector(
        server,
        new SslConnectionFactory(sslContextFactory, "http/1.1"),
        new HttpConnectionFactory(httpsConfiguration)
    );
    sslConnector.setPort(this.config.port);

    server.setConnectors(new ServerConnector[]{sslConnector});

    log.info("Configuring Splunk Event Collector Servlet for {}", this.config.eventCollectorUrl);

    ServletContextHandler servletContextHandler = new ServletContextHandler(server, "/", ServletContextHandler.NO_SESSIONS);
    servletContextHandler.addServlet(DefaultServlet.class, "/");
    ServletHolder holder = servletContextHandler.addServlet(EventServlet.class, this.config.eventCollectorUrl);

    this.sourceRecordConcurrentLinkedDeque = new SourceRecordConcurrentLinkedDeque(this.config.batchSize, this.config.backoffMS);

    try {
      log.info("Starting web server on port {}", this.config.port);
      server.start();
    } catch (Exception e) {
      throw new IllegalStateException("Exception thrown while starting server", e);
    }

    try {
      this.eventServlet = (EventServlet) holder.ensureInstance();
    } catch (ServletException e) {
      throw new ConnectException("This is really broken", e);
    }

    JsonFactory jsonFactory = new JsonFactory();

    this.eventServlet.configure(this.config, jsonFactory, this.sourceRecordConcurrentLinkedDeque);
  }

  @Override
  public List<SourceRecord> poll() throws InterruptedException {
    List<SourceRecord> records = new ArrayList<>(this.config.batchSize);

    while (!this.sourceRecordConcurrentLinkedDeque.drain(records)) {
      log.trace("No records received. Sleeping.");
    }

    return records;
  }

  @Override
  public void stop() {
    try {
      this.server.stop();
    } catch (Exception e) {
      log.error("Exception thrown calling server.stop()", e);
    }
  }
}