/*
 * 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.ExceptionHandler;
import org.apache.pekko.http.javadsl.server.PathMatchers;
import org.apache.pekko.http.javadsl.server.RejectionHandler;
import org.apache.pekko.http.javadsl.server.Rejections;
import org.apache.pekko.http.javadsl.server.Route;
import org.apache.pekko.http.javadsl.server.ValidationRejection;
import org.apache.pekko.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Test;

import static org.apache.pekko.http.javadsl.server.PathMatchers.integerSegment;

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

// #handleExceptions
// #handleRejections
import org.apache.pekko.http.javadsl.server.Directives;

import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.handleRejections;
import static org.apache.pekko.http.javadsl.server.Directives.pathPrefix;
import static org.apache.pekko.http.javadsl.server.Directives.reject;

// #handleRejections
// #handleNotFoundWithDefails
import org.apache.pekko.http.javadsl.server.Directives;

import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.extractUnmatchedPath;
import static org.apache.pekko.http.javadsl.server.Directives.handleRejections;
import static org.apache.pekko.http.javadsl.server.Directives.reject;

// #handleNotFoundWithDefails

public class ExecutionDirectivesExamplesTest extends JUnitRouteTest {

  @Test
  public void testHandleExceptions() {
    // #handleExceptions
    final ExceptionHandler divByZeroHandler =
        ExceptionHandler.newBuilder()
            .match(
                ArithmeticException.class,
                x -> complete(StatusCodes.BAD_REQUEST, "You've got your arithmetic wrong, fool!"))
            .build();

    final Route route =
        path(
            PathMatchers.segment("divide").slash(integerSegment()).slash(integerSegment()),
            (a, b) ->
                handleExceptions(divByZeroHandler, () -> complete("The result is " + (a / b))));

    // tests:
    testRoute(route).run(HttpRequest.GET("/divide/10/5")).assertEntity("The result is 2");
    testRoute(route)
        .run(HttpRequest.GET("/divide/10/0"))
        .assertStatusCode(StatusCodes.BAD_REQUEST)
        .assertEntity("You've got your arithmetic wrong, fool!");
    // #handleExceptions
  }

  @Test
  public void testHandleRejections() {
    // #handleRejections
    final RejectionHandler totallyMissingHandler =
        RejectionHandler.newBuilder()
            .handleNotFound(
                complete(StatusCodes.NOT_FOUND, "Oh man, what you are looking for is long gone."))
            .handle(
                ValidationRejection.class,
                r -> complete(StatusCodes.INTERNAL_SERVER_ERROR, r.message()))
            .build();

    final Route route =
        pathPrefix(
            "handled",
            () ->
                handleRejections(
                    totallyMissingHandler,
                    () ->
                        Directives.concat(
                            path("existing", () -> complete("This path exists")),
                            path(
                                "boom",
                                () ->
                                    reject(Rejections.validationRejection("This didn't work."))))));

    // tests:
    testRoute(route).run(HttpRequest.GET("/handled/existing")).assertEntity("This path exists");
    // applies default handler
    testRoute(route)
        .run(HttpRequest.GET("/missing"))
        .assertStatusCode(StatusCodes.NOT_FOUND)
        .assertEntity("The requested resource could not be found.");
    testRoute(route)
        .run(HttpRequest.GET("/handled/missing"))
        .assertStatusCode(StatusCodes.NOT_FOUND)
        .assertEntity("Oh man, what you are looking for is long gone.");
    testRoute(route)
        .run(HttpRequest.GET("/handled/boom"))
        .assertStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
        .assertEntity("This didn't work.");
    // #handleRejections
  }

  @Test
  public void testHandleRejectionsWithDefails() {
    // #handleNotFoundWithDefails
    final RejectionHandler totallyMissingHandler =
        RejectionHandler.newBuilder()
            .handleNotFound(
                extractUnmatchedPath(
                    path ->
                        complete(StatusCodes.NOT_FOUND, "The path " + path + " was not found!")))
            .build();

    final Route route =
        handleRejections(
            totallyMissingHandler,
            () ->
                pathPrefix(
                    "handled",
                    () -> Directives.concat(path("existing", () -> complete("This path exists")))));

    // tests:
    testRoute(route).run(HttpRequest.GET("/handled/existing")).assertEntity("This path exists");
    // applies default handler
    testRoute(route)
        .run(HttpRequest.GET("/missing"))
        .assertStatusCode(StatusCodes.NOT_FOUND)
        .assertEntity("The path /missing was not found!");
    testRoute(route)
        .run(HttpRequest.GET("/handled/missing"))
        .assertStatusCode(StatusCodes.NOT_FOUND)
        .assertEntity("The path /handled/missing was not found!");
    // #handleNotFoundWithDefails
  }
}
