package com.hubspot.smtp.client;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import io.netty.handler.codec.smtp.SmtpCommand;
import io.netty.handler.codec.smtp.SmtpResponse;

public class CompositeSendInterceptor implements SendInterceptor {
  private final SendInterceptor rootSendInterceptor;

  public static CompositeSendInterceptor of(SendInterceptor... sendInterceptors) {
    return new CompositeSendInterceptor(Lists.newArrayList(sendInterceptors));
  }

  public static CompositeSendInterceptor of(List<SendInterceptor> interceptors) {
    return new CompositeSendInterceptor(interceptors);
  }

  private CompositeSendInterceptor(List<SendInterceptor> sendInterceptors) {
    Preconditions.checkNotNull(sendInterceptors);
    Preconditions.checkArgument(!sendInterceptors.isEmpty(), "sendInterceptors must not be empty");

    SendInterceptor root = sendInterceptors.get(0);

    for (int i = 1; i < sendInterceptors.size(); i++) {
      root = new InterceptorWrapper(root, sendInterceptors.get(i));
    }

    rootSendInterceptor = root;
  }

  @Override
  public CompletableFuture<List<SmtpResponse>> aroundCommand(SmtpCommand command, Supplier<CompletableFuture<List<SmtpResponse>>> next) {
    return rootSendInterceptor.aroundCommand(command, next);
  }

  @Override
  public CompletableFuture<List<SmtpResponse>> aroundData(Supplier<CompletableFuture<List<SmtpResponse>>> next) {
    return rootSendInterceptor.aroundData(next);
  }

  @Override
  public CompletableFuture<List<SmtpResponse>> aroundPipelinedSequence(Supplier<CompletableFuture<List<SmtpResponse>>> next) {
    return rootSendInterceptor.aroundPipelinedSequence(next);
  }

  private static class InterceptorWrapper implements SendInterceptor {
    private final SendInterceptor thisSendInterceptor;
    private final SendInterceptor nextSendInterceptor;

    public InterceptorWrapper(SendInterceptor thisSendInterceptor, SendInterceptor nextSendInterceptor) {
      this.thisSendInterceptor = thisSendInterceptor;
      this.nextSendInterceptor = nextSendInterceptor;
    }

    @Override
    public CompletableFuture<List<SmtpResponse>> aroundCommand(SmtpCommand command, Supplier<CompletableFuture<List<SmtpResponse>>> next) {
      return thisSendInterceptor.aroundCommand(command, () -> nextSendInterceptor.aroundCommand(command, next));
    }

    @Override
    public CompletableFuture<List<SmtpResponse>> aroundData(Supplier<CompletableFuture<List<SmtpResponse>>> next) {
      return thisSendInterceptor.aroundData(() -> nextSendInterceptor.aroundData(next));
    }

    @Override
    public CompletableFuture<List<SmtpResponse>> aroundPipelinedSequence(Supplier<CompletableFuture<List<SmtpResponse>>> next) {
      return thisSendInterceptor.aroundData(() -> nextSendInterceptor.aroundPipelinedSequence(next));
    }
  }
}

@@ -417,28 +417,24 @@
 rceptor root
-Send
 Interceptor;
@@ -996,16 +996,27 @@
 tor root
+Interceptor
  = sendI
@@ -1102,16 +1102,27 @@
     root
+Interceptor
  = new I
@@ -1143,16 +1143,27 @@
 per(root
+Interceptor
 , sendIn
@@ -1193,24 +1193,25 @@
  %7D%0A%0A    
+this.
 root
-Send
 Intercep
@@ -1220,16 +1220,27 @@
 r = root
+Interceptor
 ;%0A  %7D%0A%0A 
@@ -1395,36 +1395,32 @@
 %0A    return root
-Send
 Interceptor.arou
@@ -1583,36 +1583,32 @@
 %0A    return root
-Send
 Interceptor.arou
@@ -1780,20 +1780,16 @@
 urn root
-Send
 Intercep
@@ -1929,36 +1929,32 @@
 Interceptor this
-Send
 Interceptor;%0A   
@@ -1980,36 +1980,32 @@
 Interceptor next
-Send
 Interceptor;%0A%0A  
@@ -2048,28 +2048,24 @@
 rceptor this
-Send
 Interceptor,
@@ -2081,28 +2081,24 @@
 rceptor next
-Send
 Interceptor)
@@ -2111,28 +2111,24 @@
    this.this
-Send
 Interceptor 
@@ -2129,28 +2129,24 @@
 eptor = this
-Send
 Interceptor;
@@ -2157,28 +2157,24 @@
    this.next
-Send
 Interceptor 
@@ -2175,28 +2175,24 @@
 eptor = next
-Send
 Interceptor;
@@ -2362,36 +2362,32 @@
      return this
-Send
 Interceptor.arou
@@ -2407,36 +2407,32 @@
 mand, () -%3E next
-Send
 Interceptor.arou
@@ -2604,36 +2604,32 @@
      return this
-Send
 Interceptor.arou
@@ -2637,36 +2637,32 @@
 dData(() -%3E next
-Send
 Interceptor.arou
@@ -2843,20 +2843,16 @@
 urn this
-Send
 Intercep
@@ -2876,20 +2876,16 @@
  -%3E next
-Send
 Intercep

Rename for clarity