package com.google.sps.data;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.HashMap;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.api.mockito.PowerMockito;

/** */
@RunWith(PowerMockRunner.class) 
@PrepareForTest({ URL.class, UrlRequest.class })
public final class UrlRequestTest {

  private final static String EMPTY_STRING = "";
  private final static Map<String, String> EMPTY_PARAMS = new HashMap<>();
  private final static String BASE_PATH = "http://www.testing.com";
  private final static String URL_PATH = String.format("%s?%s", BASE_PATH, UrlRequest.getParamsString(EMPTY_PARAMS));

  private static HttpURLConnection mockUrlConnection(String urlPath) throws Exception {
    URL mockedUrl = PowerMockito.mock(URL.class);
    HttpURLConnection mockedConnection = PowerMockito.mock(HttpURLConnection.class);

    PowerMockito.whenNew(URL.class).withArguments(urlPath).thenReturn(mockedUrl);
    PowerMockito.when(mockedUrl.openConnection()).thenReturn(mockedConnection);
    return mockedConnection;
  }

  @Test
  public void errorUrlQuery() throws Exception {
    int responseCode = 400;
  
    HttpURLConnection mockedConnection = mockUrlConnection(URL_PATH);
    PowerMockito.when(mockedConnection.getResponseCode()).thenReturn(responseCode);

    String actual = UrlRequest.urlQuery(BASE_PATH, EMPTY_PARAMS);
    String expected = String.format("{\"error\":%s}", responseCode);
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void successfulURlQuery() throws Exception {
    int responseCode = 200;
    String content = "hello world";
    InputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8.toString()));

    HttpURLConnection mockedConnection = mockUrlConnection(URL_PATH);
    PowerMockito.when(mockedConnection.getResponseCode()).thenReturn(responseCode);
    PowerMockito.when(mockedConnection.getInputStream()).thenReturn(inputStream);

    String actual = UrlRequest.urlQuery(BASE_PATH, EMPTY_PARAMS);
    String expected = content;
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void formatEmptyParamsMap() {
    String actual = UrlRequest.getParamsString(EMPTY_PARAMS);
    String expected = EMPTY_STRING;
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void formatOneParam() {
    Map<String, String> params = new HashMap<>();
    params.put("hello", "world");
    String actual = UrlRequest.getParamsString(params);
    String expected = "hello=world";
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void formatMultipleParams() {
    Map<String, String> params = new HashMap<>();
    params.put("hello", "world");
    params.put("second", "param");
    params.put("third", "param");
    String actual = UrlRequest.getParamsString(params);
    String expected = "hello=world&second=param&third=param"; // You don't know the order of the params.
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void encodeEmptyString() {
    String term = EMPTY_STRING;
    String actual = UrlRequest.encodeTerm(term);
    String expected = EMPTY_STRING;
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void encodeWord() {
    String term = "hello";
    String actual = UrlRequest.encodeTerm(term);
    String expected = term;
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void encodeWordsAndSpace() {
    String term = "hello world";
    String actual = UrlRequest.encodeTerm(term);
    String expected = "hello+world";
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void encodeSpecialCharacters() {
    String term = "#$&+,/:;=?@";
    String actual = UrlRequest.encodeTerm(term);
    String expected = "%23%24%26%2B%2C%2F%3A%3B%3D%3F%40";
    Assert.assertEquals(expected, actual);
  }

  @Test
  public void encodeNonASCIICharacter() {
    String term = "😂";
    String actual = UrlRequest.encodeTerm(term);
    String expected = "%F0%9F%98%82";
    Assert.assertEquals(expected, actual);
  }
}

@@ -202,24 +202,72 @@
 il.HashMap;%0A
+import java.util.Arrays;%0Aimport java.util.List;%0A
 import java.
@@ -712,16 +712,140 @@
 Mockito;
+%0Aimport org.hamcrest.MatcherAssert;%0Aimport static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
 %0A%0A/** */
@@ -3352,32 +3352,33 @@
 %22second%22, %22param
+2
 %22);%0A    params.p
@@ -3395,16 +3395,17 @@
 , %22param
+3
 %22);%0A    
@@ -3403,38 +3403,40 @@
 3%22);%0A    String 
-actual
+response
  = UrlRequest.ge
@@ -3462,34 +3462,119 @@
 s);%0A    
+List%3C
 String
- expected = 
+%3E actual = Arrays.asList(response.split(%22&%22, 0));%0A    List%3CString%3E expected = Arrays.asList(
 %22hello=w
@@ -3577,17 +3577,20 @@
 lo=world
-&
+%22, %22
 second=p
@@ -3593,17 +3593,21 @@
 nd=param
-&
+2%22, %22
 third=pa
@@ -3613,94 +3613,120 @@
 aram
-%22; // You don't know the order of the params.%0A    Assert.assertEquals(expected, actual
+3%22);%0A    MatcherAssert.assertThat(%22Array equality without order%22, actual, containsInAnyOrder(expected.toArray())
 );%0A 

Fix formatMultipleParams test