defmodule Refuge.BuildEventService do
  @moduledoc """
  gRPC implementation of the Build Event Protocol (BEP) `PublishBuildEvent` service.

  This minimal implementation simply logs incoming build events and acknowledges
  them so that Bazel does not treat the service as unimplemented. It can be
  extended later to persist events in a database, broadcast over PubSub, or feed
  a UI.
  """

  use GRPC.Server, service: Google.Devtools.Build.V1.PublishBuildEvent.Service

  require Logger

  alias Google.Devtools.Build.V1.{
    PublishLifecycleEventRequest,
    PublishBuildToolEventStreamRequest,
    PublishBuildToolEventStreamResponse
  }

  alias Google.Protobuf.Empty
  alias GRPC.Server.Stream

  @impl true
  @spec publish_lifecycle_event(PublishLifecycleEventRequest.t(), Stream.t()) :: Empty.t()
  def publish_lifecycle_event(request, _stream) do
    Logger.debug("[BEP] lifecycle event: #{inspect(request, limit: 10_000)}")

    # Store the lifecycle event in the EventStore
    Refuge.GRPC.EventStore.store_event(:new_build_event, %{
      "stream_id" => %{"build_id" => request.build_event.stream_id.build_id},
      "event_type" => "lifecycle",
      "event" => request.build_event.event |> Map.from_struct()
    })

    %Empty{}
  end

  @impl true
  @spec publish_build_tool_event_stream(Enumerable.t(), Stream.t()) :: any()
  def publish_build_tool_event_stream(request_enum, stream) do
    Enum.each(request_enum, fn %PublishBuildToolEventStreamRequest{} = req ->
      seq = req.ordered_build_event.sequence_number
      stream_id = req.ordered_build_event.stream_id
      build_event = req.ordered_build_event.event

      Logger.debug("[BEP] build tool event seq=#{seq}")

      # Store the build event in the EventStore
      Refuge.GRPC.EventStore.store_event(:new_build_event, %{
        "stream_id" => %{
          "build_id" => stream_id.build_id,
          "invocation_id" => stream_id.invocation_id
        },
        "event_type" => "build_tool",
        "sequence_number" => seq,
        "event" => build_event |> Map.from_struct()
      })

      ack = %PublishBuildToolEventStreamResponse{
        stream_id: stream_id,
        sequence_number: seq
      }

      GRPC.Server.send_reply(stream, ack)
    end)

    # When client closes the stream we just return :ok
    :ok
  end
end
