/*
 * Copyright 2015 Julien Viet
 *
 * 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 io.termd.core.http.websocket;

import io.termd.core.http.websocket.client.Client;
import io.termd.core.http.websocket.server.TaskStatusUpdateEvent;
import io.termd.core.http.websocket.server.TermServer;
import io.termd.core.pty.Status;
import io.termd.core.util.MockProcess;
import io.termd.core.util.Wait;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.net.HttpURLConnection;
import java.net.URL;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

/**
 * @author <a href="mailto:matejonnet@gmail.com">Matej Lazar</a>
 */
public class UndertowServerTest {

  private static final String TEST_COMMAND = "java -cp ./target/test-classes/ " + MockProcess.class.getName() + " 1 100";

  TermServer termServer;

  @Before
  public void startTermServer() throws InterruptedException {
    termServer = TermServer.start();
  }

  @Test
  public void serverShouldStart() throws Exception {
    URL url = new URL("http://" + Configurations.HOST + ":" + termServer.getPort() + "/");
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    connection.setDoOutput(true);

    Assert.assertEquals(200, connection.getResponseCode());
  }

  @Test
  public void remoteCommandShouldBeExecutedAndResponsesReceived() throws Exception {
    doRemoteCommandShouldBeExecutedAndResponsesReceived("");
  }

  @Test
  public void remoteCommandShouldBeExecutedAndResponsesReceivedWithContext() throws Exception {
    doRemoteCommandShouldBeExecutedAndResponsesReceived("/the_context");
  }

  @Test
  public void clientShouldBeAbleToReConnectToARunningSession() throws Exception {
    String context = "/my-context";
    String processSocketUrl = "http://" + Configurations.HOST + ":" + termServer.getPort() +  Configurations.TERM_PATH + context;
    StringBuilder responseData = new StringBuilder();
    Consumer<String> responseConsumer = (data) -> {
      responseData.append(data);
    };
    Client client = Client.connectCommandExecutingClient(processSocketUrl, Optional.of(responseConsumer));
    Client.executeRemoteCommand(client, TEST_COMMAND);

    Wait.forCondition(() -> responseData.toString().contains(MockProcess.WELCOME_MESSAGE), 60, ChronoUnit.SECONDS);

    client.close();

    StringBuilder reConnectingResponseData = new StringBuilder();
    Consumer<String> reConnectingResponseConsumer = (data) -> {
      reConnectingResponseData.append(data);
    };
    Client reConnectingClient = Client.connectCommandExecutingClient(processSocketUrl, Optional.of(reConnectingResponseConsumer));

    Wait.forCondition(() -> reConnectingResponseData.toString().contains(MockProcess.FINAL_MESSAGE), 60, ChronoUnit.SECONDS);
    Assert.assertTrue("Missing response data.", reConnectingResponseData.toString().contains(MockProcess.FINAL_MESSAGE));
    Assert.assertTrue("Missing response data.", reConnectingResponseData.toString().contains(MockProcess.FINAL_MESSAGE));
  }

  @Test
  public void clientShouldBeAbleToConnectToARunningSession() throws Exception {
    String context = "/my-multi-client-context";
    String processSocketUrl = "http://" + Configurations.HOST + ":" + termServer.getPort() +  Configurations.TERM_PATH + context;
    StringBuilder responseData = new StringBuilder();
    Consumer<String> responseConsumer = (data) -> {
      responseData.append(data);
    };
    Client client = Client.connectCommandExecutingClient(processSocketUrl, Optional.of(responseConsumer));
    Client.executeRemoteCommand(client, TEST_COMMAND);

    Wait.forCondition(() -> responseData.toString().contains(MockProcess.WELCOME_MESSAGE), 60, ChronoUnit.SECONDS);

    StringBuilder reConnectingResponseData = new StringBuilder();
    Consumer<String> reConnectingResponseConsumer = (data) -> {
      reConnectingResponseData.append(data);
    };
    Client reConnectingClient = Client.connectCommandExecutingClient(processSocketUrl, Optional.of(reConnectingResponseConsumer));

    Wait.forCondition(() -> reConnectingResponseData.toString().contains(MockProcess.FINAL_MESSAGE), 60, ChronoUnit.SECONDS);
    Assert.assertTrue("Missing response data.", reConnectingResponseData.toString().contains(MockProcess.FINAL_MESSAGE));
    Assert.assertTrue("Missing response data.", reConnectingResponseData.toString().contains(MockProcess.FINAL_MESSAGE));
  }

  private void doRemoteCommandShouldBeExecutedAndResponsesReceived(String context) throws Exception {
    String updatesSocketUrl = "http://" + Configurations.HOST + ":" + termServer.getPort() + Configurations.PROCESS_UPDATES_PATH + context;
    List<Status> receivedEventUpdates = new ArrayList<>();
    Consumer<TaskStatusUpdateEvent> onStatusUpdate = (statusUpdateEvent) -> {
      receivedEventUpdates.add(statusUpdateEvent.getNewStatus());
    };
    Client.connectStatusListenerClient(updatesSocketUrl, onStatusUpdate);

    String processSocketUrl = "http://" + Configurations.HOST + ":" + termServer.getPort() +  Configurations.TERM_PATH + context;
    StringBuilder responseData = new StringBuilder();
    Consumer<String> responseConsumer = (data) -> {
      responseData.append(data);
    };
    Client client = Client.connectCommandExecutingClient(processSocketUrl, Optional.of(responseConsumer));

    Client.executeRemoteCommand(client, TEST_COMMAND);


    Wait.forCondition(() -> receivedEventUpdates.contains(Status.COMPLETED), 60, ChronoUnit.SECONDS);

    Assert.assertTrue("Missing response data.", responseData.toString().contains(MockProcess.FINAL_MESSAGE));
  }

}

@@ -1411,17 +1411,17 @@
 ) + %22 1 
-1
+5
 00%22;%0A%0A  

Increase command execution time.