/*
 * Copyright (C) Lightbend Inc. <https://www.lightbend.com>
 */
package play.grpc.specs2

import org.junit.runner.RunWith
import org.specs2.runner.JUnitRunner

import play.api.inject.bind
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.libs.ws.WSClient
import play.api.libs.ws.WSRequest
import play.api.routing.Router
import play.api.test._

import akka.grpc.internal.GrpcProtocolNative

import example.myapp.helloworld.grpc.helloworld._

import io.grpc.Status

/**
 * Test for the Play gRPC Specs2 APIs
 */
@RunWith(classOf[JUnitRunner])
class PlaySpecs2Spec extends ForServer with ServerGrpcClient with PlaySpecification with ApplicationFactories {

  protected def applicationFactory: ApplicationFactory =
    withGuiceApp(GuiceApplicationBuilder().overrides(bind[Router].to[GreeterServiceImpl]))

  // RICH: Still need to work out how to make WSClient work properly with endpoints
  def wsUrl(path: String)(implicit running: RunningServer): WSRequest = {
    val ws  = running.app.injector.instanceOf[WSClient]
    val url = running.endpoints.httpEndpoint.get.pathUrl(path)
    ws.url(url)
  }

  "A Play server bound to a gRPC router" should {
    "give a 404 when routing a non-gRPC request" >> { implicit rs: RunningServer =>
      val result = await(wsUrl("/").get)
      result.status must ===(404) // Maybe should be a 426, see #396
    }
    "give a 415 error when not using a gRPC content-type" >> { implicit rs: RunningServer =>
      val result = await(wsUrl(s"/${GreeterService.name}/FooBar").get)
      result.status must ===(415) // Maybe should be a 426, see #396
    }
    "give a grpc UNIMPLEMENTED when routing a non-existent gRPC method" >> { implicit rs: RunningServer =>
      val result = await(
        wsUrl(s"/${GreeterService.name}/FooBar")
          .addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
          .get,
      )
      result.status must ===(200) // Maybe should be a 426, see #396
      result.header("grpc-status") must beSome(Status.Code.UNIMPLEMENTED.value().toString)
    }
    "give a grpc INVALID_ARGUMENT error when routing an empty request to a gRPC method" >> {
      implicit rs: RunningServer =>
        val result = await(
          wsUrl(s"/${GreeterService.name}/SayHello")
            .addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
            .get,
        )
        result.status must ===(200) // Maybe should be a 426, see #396

        // grpc-status 3 means INVALID_ARGUMENT error. See https://developers.google.com/maps-booking/reference/grpc-api/status_codes
        result.header("grpc-status") must beSome(Status.Code.INVALID_ARGUMENT.value().toString)
    }
    "work with a gRPC client" >> { implicit rs: RunningServer =>
      withGrpcClient[GreeterServiceClient] { client: GreeterServiceClient =>
        val reply = await(client.sayHello(HelloRequest("Alice")))
        reply.message must ===("Hello, Alice!")
      }
    }
  }
}