/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * license agreements; and to You under the Apache License, version 2.0:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * This file is part of the Apache Pekko project, which was derived from Akka.
 */

/*
 * Copyright (C) 2016-2022 Lightbend Inc. <https://www.lightbend.com>
 */

package docs.http.javadsl.server.directives;

import org.apache.pekko.http.javadsl.model.HttpRequest;
import org.apache.pekko.http.javadsl.model.StatusCodes;
import org.apache.pekko.http.javadsl.server.Route;
import org.apache.pekko.http.javadsl.testkit.JUnitRouteTest;
import org.apache.pekko.http.javadsl.unmarshalling.StringUnmarshallers;
import org.junit.Test;

import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;

// #parameter
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameter;

// #parameter
// #parameters
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameter;

// #parameters
// #optional
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameter;
import static org.apache.pekko.http.javadsl.server.Directives.parameterOptional;

// #optional
// #required-value
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameterRequiredValue;

// #required-value
// #mapped-value
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameter;

// #mapped-value
// #parameterMap
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameterMap;

// #parameterMap
// #parameterMultiMap
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameterMultiMap;

// #parameterMultiMap
// #parameterSeq
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.parameterList;

// #parameterSeq

public class ParameterDirectivesExamplesTest extends JUnitRouteTest {

  @Test
  public void testParameter() {
    // #parameter
    final Route route = parameter("color", color -> complete("The color is '" + color + "'"));

    // tests:
    testRoute(route).run(HttpRequest.GET("/?color=blue")).assertEntity("The color is 'blue'");

    testRoute(route)
        .run(HttpRequest.GET("/"))
        .assertStatusCode(StatusCodes.NOT_FOUND)
        .assertEntity("Request is missing required query parameter 'color'");
    // #parameter
  }

  @Test
  public void testParameters() {
    // #parameters
    final Route route =
        parameter(
            "color",
            color ->
                parameter(
                    "backgroundColor",
                    backgroundColor ->
                        complete(
                            "The color is '"
                                + color
                                + "' and the background is '"
                                + backgroundColor
                                + "'")));

    // tests:
    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&backgroundColor=red"))
        .assertEntity("The color is 'blue' and the background is 'red'");

    testRoute(route)
        .run(HttpRequest.GET("/?color=blue"))
        .assertStatusCode(StatusCodes.NOT_FOUND)
        .assertEntity("Request is missing required query parameter 'backgroundColor'");
    // #parameters
  }

  @Test
  public void testParameterOptional() {
    // #optional
    final Route route =
        parameter(
            "color",
            color ->
                parameterOptional(
                    "backgroundColor",
                    backgroundColor ->
                        complete(
                            "The color is '"
                                + color
                                + "' and the background is '"
                                + backgroundColor.orElse("undefined")
                                + "'")));

    // tests:
    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&backgroundColor=red"))
        .assertEntity("The color is 'blue' and the background is 'red'");

    testRoute(route)
        .run(HttpRequest.GET("/?color=blue"))
        .assertEntity("The color is 'blue' and the background is 'undefined'");
    // #optional
  }

  @Test
  public void testParameterRequiredValue() {
    // #required-value
    final Route route =
        parameter(
            "color",
            color ->
                parameterRequiredValue(
                    StringUnmarshallers.BOOLEAN,
                    true,
                    "action",
                    () -> complete("The color is '" + color + "'.")));

    // tests:
    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&action=true"))
        .assertStatusCode(StatusCodes.OK)
        .assertEntity("The color is 'blue'.");

    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&action=false"))
        .assertStatusCode(StatusCodes.NOT_FOUND)
        .assertEntity("Request is missing required value 'true' for query parameter 'action'");
    // #required-value
  }

  @Test
  public void testParameterMappedValue() {
    // #mapped-value
    final Route route =
        parameter(
            "color",
            color ->
                parameter(
                    StringUnmarshallers.INTEGER,
                    "count",
                    count ->
                        complete(
                            "The color is '" + color + "' and you have " + count + " of it.")));
    // tests:
    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&count=42"))
        .assertEntity("The color is 'blue' and you have 42 of it.");

    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&count=blub"))
        .assertStatusCode(StatusCodes.BAD_REQUEST)
        .assertEntity(
            "The query parameter 'count' was malformed:\n'blub'"
                + " is not a valid 32-bit signed integer value");
    // #mapped-value
  }

  @Test
  public void testParameterMap() {
    // #parameterMap
    final Function<Entry, String> paramString =
        entry -> entry.getKey() + " = '" + entry.getValue() + "'";

    final Route route =
        parameterMap(
            params -> {
              final String pString =
                  params.entrySet().stream()
                      .map(paramString::apply)
                      .collect(Collectors.joining(", "));
              return complete("The parameters are " + pString);
            });

    // tests:
    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&count=42"))
        .assertEntity("The parameters are color = 'blue', count = '42'");

    testRoute(route).run(HttpRequest.GET("/?x=1&x=2")).assertEntity("The parameters are x = '2'");
    // #parameterMap
  }

  @Test
  public void testParameterMultiMap() {
    // #parameterMultiMap
    final Route route =
        parameterMultiMap(
            params -> {
              final String pString =
                  params.entrySet().stream()
                      .map(e -> e.getKey() + " -> " + e.getValue().size())
                      .collect(Collectors.joining(", "));
              return complete("There are parameters " + pString);
            });

    // tests:
    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&count=42"))
        .assertEntity("There are parameters color -> 1, count -> 1");

    testRoute(route)
        .run(HttpRequest.GET("/?x=23&x=42"))
        .assertEntity("There are parameters x -> 2");
    // #parameterMultiMap
  }

  @Test
  public void testParameterSeq() {
    // #parameterSeq
    final Function<Entry, String> paramString =
        entry -> entry.getKey() + " = '" + entry.getValue() + "'";

    final Route route =
        parameterList(
            params -> {
              final String pString =
                  params.stream().map(paramString::apply).collect(Collectors.joining(", "));

              return complete("The parameters are " + pString);
            });

    // tests:
    testRoute(route)
        .run(HttpRequest.GET("/?color=blue&count=42"))
        .assertEntity("The parameters are color = 'blue', count = '42'");

    testRoute(route)
        .run(HttpRequest.GET("/?x=1&x=2"))
        .assertEntity("The parameters are x = '1', x = '2'");
    // #parameterSeq
  }
}
