// Copyright 2018 Google LLC
//
// 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 com.google.cloud.healthcare;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.eclipse.jetty.http.MultiPartParser;
import com.google.api.client.http.HttpMediaType;
import com.google.api.client.testing.http.HttpTesting;
import com.google.cloud.healthcare.util.FakeWebServer;
import com.google.cloud.healthcare.util.TestUtils;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class DicomWebClientTest {

  private FakeWebServer fakeDicomWebServer;
  private DicomWebClient client;

  @Before
  public void setUp() {
    fakeDicomWebServer = new FakeWebServer();
    client = new DicomWebClient(fakeDicomWebServer.createRequestFactory(), HttpTesting.SIMPLE_URL, "/studies");
  }

  @Test
  public void testDicomWebClient_Wado() throws Exception {
    byte[] dicomInstance = TestUtils.readTestFile(TestUtils.TEST_MR_FILE);

    fakeDicomWebServer.addWadoResponse(dicomInstance);
    InputStream responseStream = client.wadoRs("instanceName");

    assertNotNull(responseStream);

    byte[] actual = ByteStreams.toByteArray(responseStream);
    if (!Arrays.equals(dicomInstance, actual)) {
      fail("wadoRs returned unexpected DICOM bytes");
    }
  }

  @Test
  public void testDicomWebClient_WadoError() throws Exception {
    fakeDicomWebServer.addResponseWithStatusCode(404);
    assertThrows(IDicomWebClient.DicomWebException.class, () -> client.wadoRs("instanceName"));
  }

  @Test
  public void testDicomWebClient_Qido() throws Exception {
    String qidoResponse =
        "[{\"0020000E\":{\"vr\":\"UI\",\"Value\":[\"1.2.840.113619.2.176.3596.3364818.7819.1259708454.108\"]}}]";
    fakeDicomWebServer.addJsonResponse(qidoResponse);
    JSONArray jsonArray = client.qidoRs("query");
    assertThat(jsonArray.length()).isEqualTo(1);
    JSONObject tag = jsonArray.getJSONObject(0).getJSONObject("0020000E");
    JSONArray tagArray = tag.getJSONArray("Value");
    assertThat(tagArray.length()).isEqualTo(1);
    String tagValue = tagArray.getString(0);
    assertThat(tagValue).isEqualTo("1.2.840.113619.2.176.3596.3364818.7819.1259708454.108");
  }

  @Test
  public void testDicomWebClient_QidoError() throws Exception {
    fakeDicomWebServer.addResponseWithStatusCode(404);
    assertThrows(IDicomWebClient.DicomWebException.class, () -> client.qidoRs("query"));
  }

  @Test
  public void testDicomWebClient_NothingInjected() throws Exception {
    assertThrows(IDicomWebClient.DicomWebException.class, () -> client.qidoRs("query"));
  }

  // This interface isn't well documented, but you can find the implementation on GitHub
  // at https://github.com/eclipse/jetty.project/blob/jetty-9.4.20.v20190813/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartParser.java.
  private static class MultipartHandler implements MultiPartParser.Handler {
    ByteBuffer lastItem;

    @Override
    public boolean content​(ByteBuffer item, boolean last) {
      lastItem = item;

      // Returning true seems to trigger early abort of the parser.
      return false;
    }
  }

  @Test
  public void testDicomWebClient_Stow() throws Exception {
    fakeDicomWebServer.addResponseWithStatusCode(200);

    String data = "some data";
    client.stowRs(new ByteArrayInputStream(data.getBytes()));

    FakeWebServer.Request req = fakeDicomWebServer.getRequests().firstElement();

    ByteArrayOutputStream body = new ByteArrayOutputStream();
    req.request.getStreamingContent().writeTo(body);

    String contentType = req.request.getContentType();
    HttpMediaType mediaType = new HttpMediaType(contentType);
    String boundary = mediaType.getParameter("boundary");

    MultipartHandler handler = new MultipartHandler();
    MultiPartParser parser = new MultiPartParser(handler,boundary);
    parser.parse(ByteBuffer.wrap(body.toByteArray()), true);

    assertThat(StandardCharsets.UTF_8.decode(handler.lastItem).toString()).isEqualTo(data);
  }

  @Test
  public void testDicomWebClient_StowError() throws Exception {
    fakeDicomWebServer.addResponseWithStatusCode(404);
    assertThrows(
        IDicomWebClient.DicomWebException.class,
        () -> client.stowRs(new ByteArrayInputStream(new byte[0])));
  }
}