/*
 * 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) 2009-2022 Lightbend Inc. <https://www.lightbend.com>
 */

package docs.http.scaladsl.server.directives

import org.apache.pekko
import pekko.http.scaladsl.model.StatusCodes._
import pekko.http.scaladsl.model.headers.Host
import pekko.http.scaladsl.server.RoutingSpec
import pekko.http.scaladsl.server.{ Directive, Directive1, MissingQueryParamRejection, Route }
import docs.CompileOnlySpec

class CustomDirectivesExamplesSpec extends RoutingSpec with CompileOnlySpec {

  "labeling" in {
    // #labeling
    val getOrPut = get | put

    // tests:
    val route = getOrPut { complete("ok") }

    Get("/") ~> route ~> check {
      responseAs[String] shouldEqual "ok"
    }

    Put("/") ~> route ~> check {
      responseAs[String] shouldEqual "ok"
    }
    // #labeling
  }

  "map-0" in {
    // #map-0
    val textParam: Directive1[String] =
      parameter("text".as[String])

    val lengthDirective: Directive1[Int] =
      textParam.map(text => text.length)

    // tests:
    Get("/?text=abcdefg") ~> lengthDirective(x => complete(x.toString)) ~> check {
      responseAs[String] shouldEqual "7"
    }
    // #map-0
  }

  "tmap-1" in {
    // #tmap-1
    val twoIntParameters: Directive[(Int, Int)] =
      parameters("a".as[Int], "b".as[Int])

    val myDirective: Directive1[String] =
      twoIntParameters.tmap {
        case (a, b) => (a + b).toString
      }

    // tests:
    Get("/?a=2&b=5") ~> myDirective(x => complete(x)) ~> check {
      responseAs[String] shouldEqual "7"
    }
    // #tmap-1
  }

  "collect-1" in {
    // #collect-1
    val intParameter: Directive1[Int] = parameter("x".as[Int])

    val myRejection = MissingQueryParamRejection("test")

    val myDirective: Directive1[Int] =
      intParameter.collect({ case x if x % 2 == 0 => x + 1 }, myRejection)

    // tests:
    Get("/?x=2") ~> myDirective(x => complete(x.toString)) ~> check {
      responseAs[String] shouldEqual "3"
    }
    Get("/?x=3") ~> myDirective(x => complete(x.toString)) ~> check {
      rejection shouldEqual myRejection
    }

    // #collect-1
  }

  "flatMap-0" in {
    // #flatMap-0
    val intParameter: Directive1[Int] = parameter("a".as[Int])

    val myDirective: Directive1[Int] =
      intParameter.flatMap {
        case a if a > 0 => provide(2 * a)
        case _          => reject
      }

    // tests:
    Get("/?a=21") ~> myDirective(i => complete(i.toString)) ~> check {
      responseAs[String] shouldEqual "42"
    }
    Get("/?a=-18") ~> myDirective(i => complete(i.toString)) ~> check {
      handled shouldEqual false
    }
    // #flatMap-0
  }

  "scratch-1" in {
    // #scratch-1
    def hostnameAndPort: Directive[(String, Int)] = Directive[(String, Int)] { inner => ctx =>
      val authority = ctx.request.uri.authority
      inner((authority.host.address(), authority.port))(ctx)
    }

    // test
    val route = hostnameAndPort {
      (hostname, port) => complete(s"The hostname is $hostname and the port is $port")
    }

    Get() ~> Host("pekko.apache.org", 8080) ~> route ~> check {
      status shouldEqual OK
      responseAs[String] shouldEqual "The hostname is pekko.apache.org and the port is 8080"
    }
    // #scratch-1
  }

  "scratch-2" in {
    // #scratch-2
    object hostnameAndPort extends Directive[(String, Int)] {
      override def tapply(f: ((String, Int)) => Route): Route = { ctx =>
        val authority = ctx.request.uri.authority
        f((authority.host.address(), authority.port))(ctx)
      }
    }

    // test
    val route = hostnameAndPort {
      (hostname, port) => complete(s"The hostname is $hostname and the port is $port")
    }

    Get() ~> Host("pekko.apache.org", 8080) ~> route ~> check {
      status shouldEqual OK
      responseAs[String] shouldEqual "The hostname is pekko.apache.org and the port is 8080"
    }
    // #scratch-2
  }

}
