package serilogj.sinks.seq;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;

import serilogj.core.LoggingLevelSwitch;
import serilogj.debugging.SelfLog;
import serilogj.events.LogEvent;
import serilogj.events.LogEventLevel;
import serilogj.formatting.ITextFormatter;
import serilogj.formatting.json.JsonFormatter;
import serilogj.sinks.periodicbatching.PeriodicBatchingSink;

public class SeqSink extends PeriodicBatchingSink {
	public static final int DefaultBatchPostingLimit = 1000;
	public static final Duration DefaultPeriod = Duration.ofSeconds(2);
	private static final Duration RequiredLevelCheckInterval = Duration.ofMinutes(2);
	private static final String BulkUploadResource = "api/events/raw";
	private static final String ApiKeyHeaderName = "X-Seq-ApiKey";

	private final URL baseUrl;
	private final Long eventBodyLimitBytes;
	private LoggingLevelSwitch levelSwitch;
	private LocalDateTime nextRequiredLevelCheck = LocalDateTime.now().plus(RequiredLevelCheckInterval);
    private final Map<String, String> httpHeaders;

    public SeqSink(String serverUrl, String apiKey, Integer batchSizeLimit, Duration period, Long eventBodyLimitBytes,
                   LoggingLevelSwitch levelSwitch) {
        this(serverUrl, apiKey, null, batchSizeLimit, period, eventBodyLimitBytes, levelSwitch);
    }

	public SeqSink(String serverUrl, String apiKey, Map<String,String> customHttpHeaders, Integer batchSizeLimit, Duration period, Long eventBodyLimitBytes,
			LoggingLevelSwitch levelSwitch) {
		super(batchSizeLimit == null ? DefaultBatchPostingLimit : batchSizeLimit,
				period == null ? DefaultPeriod : period);

		if (serverUrl == null) {
			throw new IllegalArgumentException("serverUrl");
		}

		if (!serverUrl.endsWith("/")) {
			serverUrl += "/";
		}
		this.eventBodyLimitBytes = eventBodyLimitBytes;
		this.levelSwitch = levelSwitch;

		try {
			baseUrl = new URL(serverUrl + BulkUploadResource);
		} catch (MalformedURLException e) {
			SelfLog.writeLine("Invalid server url format: %s", serverUrl + BulkUploadResource);
			throw new IllegalArgumentException("serverUrl");
		}

        Map<String, String> httpHeaders = new HashMap<>();
		httpHeaders.put("Content-Type", "application/json; charset=utf-8");
        if(customHttpHeaders != null) {
            httpHeaders.putAll(customHttpHeaders);
        }
        if (apiKey != null && !apiKey.equals("")) {
            httpHeaders.put(ApiKeyHeaderName, apiKey);
        }
        this.httpHeaders = Collections.unmodifiableMap(httpHeaders);
	}

	@Override
	protected void emitBatch(Queue<LogEvent> events) {
		nextRequiredLevelCheck = LocalDateTime.now().plus(RequiredLevelCheckInterval);

		StringWriter payload = new StringWriter();
		payload.write("{\"Events\":[");

		ITextFormatter formatter = new JsonFormatter(false, "", false, null);
		String delimStart = "";
		try {
			for (LogEvent logEvent : events) {
				if (eventBodyLimitBytes != null) {
					StringWriter buffer = new StringWriter();
					formatter.format(logEvent, buffer);
					String buffered = buffer.toString();

					if (buffered.length() > eventBodyLimitBytes) {
						SelfLog.writeLine(
								"Event JSON representation exceeds the byte size limit of %d set for this sink and will be dropped; data: %s",
								eventBodyLimitBytes, buffered);
					} else {
						payload.write(delimStart);
						formatter.format(logEvent, payload);
						delimStart = ",";
					}
				} else {
					payload.write(delimStart);
					formatter.format(logEvent, payload);
					delimStart = ",";
				}
			}
		} catch (IOException e) {
			// Never happens
		}

		payload.write("]}");

		try {
			HttpURLConnection con = (HttpURLConnection) baseUrl.openConnection();
			con.setRequestMethod("POST");
            httpHeaders.forEach(con::setRequestProperty);
			con.setDoOutput(true);

			OutputStream os = con.getOutputStream();
			os.write(payload.toString().getBytes("UTF8"));
			os.flush();
			os.close();

			InputStream stream;
			int responseCode = con.getResponseCode();
			if (responseCode < 200 || responseCode >= 300) {
				stream = con.getErrorStream();
			} else {
				stream = con.getInputStream();
			}

			String line = "";
			String response = "";
			BufferedReader in = new BufferedReader(new InputStreamReader(stream, "UTF8"));
			while ((line = in.readLine()) != null) {
				response += line;
			}
			in.close();

			if (responseCode < 200 || responseCode >= 300) {
				throw new IOException(response);
			}

			LogEventLevel level = SeqApi.readEventInputResult(response);
			if (level != null && levelSwitch == null) {
				levelSwitch = new LoggingLevelSwitch(level);
			}
		} catch (IOException e) {
			SelfLog.writeLine("Error sending events to seq, exception %s", e.getMessage());
		}
	}

	@Override
	protected void onEmptyBatch() {
		if (levelSwitch != null && nextRequiredLevelCheck.isBefore(LocalDateTime.now())) {
			emitBatch(new LinkedList<LogEvent>());
		}
	}
}

@@ -1393,20 +1393,17 @@
 erval);%0A
-    
+%09
 private 
@@ -1442,20 +1442,17 @@
 aders;%0A%0A
-    
+%09
 public S
@@ -1562,27 +1562,12 @@
 es,%0A
-                   
+%09%09%09%09
 Logg
@@ -1596,24 +1596,18 @@
 itch) %7B%0A
-        
+%09%09
 this(ser
@@ -1687,20 +1687,17 @@
 witch);%0A
-    
+%09
 %7D%0A%0A%09publ
@@ -2477,24 +2477,18 @@
 );%0A%09%09%7D%0A%0A
-        
+%09%09
 Map%3CStri
@@ -2600,24 +2600,18 @@
 tf-8%22);%0A
-        
+%09%09
 if(custo
@@ -2630,36 +2630,27 @@
  != null) %7B%0A
-            
+%09%09%09
 httpHeaders.
@@ -2676,34 +2676,22 @@
 aders);%0A
-        %7D%0A        
+%09%09%7D%0A%09%09
 if (apiK
@@ -2726,28 +2726,19 @@
 (%22%22)) %7B%0A
-            
+%09%09%09
 httpHead
@@ -2776,26 +2776,14 @@
 y);%0A
-        %7D%0A        
+%09%09%7D%0A%09%09
 this
@@ -4055,20 +4055,11 @@
 %22);%0A
-            
+%09%09%09
 http

fix whitespaces