/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.tools.pulse.tests;

import static com.jayway.awaitility.Awaitility.await;
import static org.assertj.core.api.Assertions.assertThat;

import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.test.dunit.IgnoredException;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
import org.apache.geode.test.dunit.rules.Locator;
import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

@Category(DistributedTest.class)
public class PulseDataExportTest extends JUnit4DistributedTestCase {

  @Rule
  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule();

  private Locator locator;
  private org.apache.geode.test.dunit.rules.Server server;
  private GfshShellConnectionRule gfshConnector;
  private HttpClient httpClient;
  private CookieStore cookieStore;


  @Before
  public void before() throws Throwable {
    IgnoredException
        .addIgnoredException("Failed to properly release resources held by the HTTP service:");
    IgnoredException.addIgnoredException("!STOPPED");

    locator = lsRule.startLocatorVMWithPulse(0, new Properties());

    gfshConnector = new GfshShellConnectionRule(locator);
    gfshConnector.connect();
    assertThat(gfshConnector.isConnected()).isTrue();

    server = lsRule.startServerVM(1, locator.getPort());

    gfshConnector.executeAndVerifyCommand("create region --name=regionA --type=REPLICATE");
    org.apache.geode.cache.Region<String, String> region = new ClientCacheFactory()
        .addPoolLocator("localhost", locator.getPort()).create()
        .<String, String>createClientRegionFactory(ClientRegionShortcut.PROXY).create("regionA");

    region.put("key1", "value1");
    region.put("key2", "value2");
    region.put("key3", "value3");
    cookieStore = new BasicCookieStore();
    httpClient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build();

    await().atMost(2, TimeUnit.MINUTES).until(this::pulseServerHasStarted);
  }


  @Test
  public void dataBrowserExportWorksAsExpected() throws Throwable {
    getAuthenticatedJSESSIONID();
    HttpContext authenticatedHttpContext = buildAuthenticatedHttpContext();

    HttpGet dataExportGET = buildDataExportGET();

    HttpResponse response = httpClient.execute(dataExportGET, authenticatedHttpContext);
    assertThat(response.getStatusLine().getStatusCode()).describedAs(response.toString())
        .isEqualTo(200);

    String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
    assertThat(responseBody).describedAs(response.toString()).isEqualTo(
        "{\"result\":[[\"java.lang.String\",\"value1\"],[\"java.lang.String\",\"value2\"],[\"java.lang.String\",\"value3\"]]}");
  }

  private HttpPost buildLoginPOST() {
    HttpPost httpPost = new HttpPost("http://localhost:7070/pulse/login");

    List<NameValuePair> formData = new ArrayList<>();
    formData.add(new BasicNameValuePair("username", "admin"));
    formData.add(new BasicNameValuePair("password", "admin"));

    httpPost.setEntity(new UrlEncodedFormEntity(formData, Consts.UTF_8));

    return httpPost;
  }

  private HttpGet buildDataExportGET() throws URISyntaxException {
    URIBuilder builder = new URIBuilder();
    builder.setScheme("http").setHost("localhost").setPort(7070).setPath("/pulse/dataBrowserExport")
        .setParameter("query", "select * from /regionA a order by a");
    return new HttpGet(builder.build());
  }

  private HttpContext buildAuthenticatedHttpContext() throws Throwable {
    HttpContext localContext = new BasicHttpContext();
    localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);

    return localContext;
  }

  public void getAuthenticatedJSESSIONID() throws Throwable {
    HttpResponse loginResponse = httpClient.execute(buildLoginPOST());
    assertThat(loginResponse.getStatusLine().getStatusCode()).describedAs(loginResponse.toString())
        .isEqualTo(302);

    String JSESSIONIDFromSetCookieHeader = Arrays.stream(loginResponse.getHeaders("SET-COOKIE"))
        .map(Header::getValue).filter(setCookie -> setCookie.contains("JSESSIONID")).findAny()
        .orElseThrow(() -> new AssertionError(
            "No JSESSIONID cookie was set in the login response: " + loginResponse.toString()));

    Cookie JESSIONIDFromCookieStore = cookieStore.getCookies().stream()
        .filter(cookie -> cookie.getName().equalsIgnoreCase("JSESSIONID")).findFirst()
        .orElseThrow(() -> new AssertionError("No JSESSIONID cookie was set in the cookie store"));

    assertThat(JSESSIONIDFromSetCookieHeader).contains(JESSIONIDFromCookieStore.getValue());
  }

  private boolean pulseServerHasStarted() {
    try {
      httpClient.execute(buildLoginPOST());
    } catch (IOException e) {
      e.printStackTrace();
      return false;
    }

    return true;
  }

}

@@ -2266,24 +2266,49 @@
 nit.Before;%0A
+import org.junit.Ignore;%0A
 import org.j
@@ -4139,16 +4139,39 @@
 );%0A  %7D%0A%0A
+  @Ignore // GEODE-2313
 %0A  @Test

disable broken test until it can be fixed