/*
 * 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.knox.gateway.filter.rewrite.impl.json;

import com.jayway.jsonassert.JsonAssert;
import org.apache.commons.io.IOUtils;
import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterApplyDescriptor;
import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterBufferDescriptor;
import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterDescriptor;
import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterDetectDescriptor;
import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor;
import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptorFactory;
import org.apache.knox.test.TestUtils;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

public class JsonFilterReaderTest {

  @Test
  public void testValueNumberWithBuffering() throws Exception {
    String input = "{ \"apps\" : {\"app\":[{\"id\":\"one\", \"progress\":100.0, \"startedTime\":1399975176760}]} }";

    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter-1" );
    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "text/json" );
    UrlRewriteFilterBufferDescriptor bufferConfig = contentConfig.addBuffer( "$.apps.app[*]" );
    UrlRewriteFilterApplyDescriptor applyConfig = bufferConfig.addApply( "$.id", "test-rule" );
    assertNotNull(applyConfig);

    JsonFilterReader filter = new JsonFilterReader( new StringReader( input ), contentConfig );
    String output = IOUtils.toString( filter );
    assertThat( output, containsString( "\"startedTime\":1399975176760}" ) );
  }

  @Test
  public void testString() throws IOException {
    String inputJson = "\"abc\"";
    StringReader inputReader = new StringReader( inputJson );
    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
    JsonAssert.with( outputJson ).assertThat( "$", is( "abc" ) );
  }

  @Test
  public void testNumber() throws IOException {
    int num = ThreadLocalRandom.current().nextInt();
    String inputJson = String.valueOf(num);
    StringReader inputReader = new StringReader( inputJson );
    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
    JsonAssert.with( outputJson ).assertThat( "$", is( num ) );
  }

  @Test
  public void testBoolean() throws IOException {
    List<Boolean> booleans = Arrays.asList(true, false);
    for(boolean bool : booleans) {
      String inputJson = String.valueOf(bool);
      StringReader inputReader = new StringReader(inputJson);
      JsonFilterReader filterReader = new TestJsonFilterReader(inputReader, null);
      String outputJson = new String(IOUtils.toCharArray(filterReader));
      JsonAssert.with(outputJson).assertThat("$", is(bool));
    }
  }

  @Test
  public void testNull() throws IOException {
    String inputJson = "null";
    StringReader inputReader = new StringReader( inputJson );
    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
    assertThat(inputJson, is(outputJson));
  }

  @Test
  public void testSimple() throws IOException {
    String inputJson = "{ \"test-name\" : \"test-value\" }";
    StringReader inputReader = new StringReader( inputJson );
    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
    String outputJson = new String( IOUtils.toCharArray( filterReader ) );

    JsonAssert.with( outputJson ).assertThat( "name<test-name>", is( "value:null<test-value>" ) );
  }

  @Test
  public void testRootArray() throws Exception {
    String inputJson = "[\"test-value-1\",\"test-value-2\",\"test-value-3\"]";
    StringReader inputReader = new StringReader( inputJson );
    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
    JsonAssert.with( outputJson ).assertThat( "$.[0]", is( "value:null<test-value-1>" ) );
    JsonAssert.with( outputJson ).assertThat( "$.[1]", is( "value:null<test-value-2>" ) );
    JsonAssert.with( outputJson ).assertThat( "$.[2]", is( "value:null<test-value-3>" ) );

    inputJson = "[777,42]";
    inputReader = new StringReader( inputJson );
    filterReader = new TestJsonFilterReader( inputReader, null );
    outputJson = new String( IOUtils.toCharArray( filterReader ) );
    JsonAssert.with( outputJson ).assertThat( "$.[0]", is( 777 ) );
    JsonAssert.with( outputJson ).assertThat( "$.[1]", is( 42 ) );
  }

  @Test
  public void testEmptyObject() throws IOException {
    String inputJson = "{}";
    StringReader inputReader = new StringReader( inputJson );
    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
    String outputJson = new String( IOUtils.toCharArray( filterReader ) );

    assertThat( outputJson, is( "{}" ) );
  }

  @Test
  public void testEmptyArray() throws IOException {
    String inputJson = "[]";
    StringReader inputReader = new StringReader( inputJson );
    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
    String outputJson = new String( IOUtils.toCharArray( filterReader ) );

    assertThat( outputJson, is( "[]" ) );