defmodule Refuge.RemoteCache.ActionCache do
  @moduledoc """
  Implementation of the Bazel Remote Execution ActionCache service.
  """
  
  use GRPC.Server, service: Build.Bazel.Remote.Execution.V2.ActionCache.Service
  
  require Logger
  alias Refuge.RemoteCache.DiskStore
  alias Build.Bazel.Remote.Execution.V2.{
    GetActionResultRequest,
    UpdateActionResultRequest,
    ActionResult,
    Digest
  }
  alias Google.Protobuf

  @doc """
  Get an action result from the cache.
  """
  @spec get_action_result(GetActionResultRequest.t(), GRPC.Server.Stream.t()) :: ActionResult.t()
  def get_action_result(request, _stream) do
    Logger.debug("GetActionResult request for #{request.action_digest.hash}")
    
    # Validate the action digest
    if request.action_digest == nil || String.length(request.action_digest.hash) != 64 do
      raise GRPC.RPCError, status: :invalid_argument, message: "Invalid action digest"
    end
    
    # Retrieve the action result
    hash = request.action_digest.hash
    case DiskStore.get_action_result(hash) do
      {:ok, data} ->
        # Deserialize the stored action result
        {:ok, action_result} = ActionResult.decode(data)
        action_result
      {:error, :not_found} ->
        raise GRPC.RPCError, status: :not_found, message: "Action result not found"
      {:error, reason} ->
        Logger.error("Failed to get action result #{hash}: #{inspect(reason)}")
        raise GRPC.RPCError, status: :internal, message: "Failed to retrieve action result"
    end
  end

  @doc """
  Update an action result in the cache.
  """
  @spec update_action_result(UpdateActionResultRequest.t(), GRPC.Server.Stream.t()) :: ActionResult.t()
  def update_action_result(request, _stream) do
    Logger.debug("UpdateActionResult request for #{request.action_digest.hash}")
    
    # Validate the action digest
    if request.action_digest == nil || String.length(request.action_digest.hash) != 64 do
      raise GRPC.RPCError, status: :invalid_argument, message: "Invalid action digest"
    end
    
    # Validate the action result - it should not be empty
    if request.action_result == nil do
      raise GRPC.RPCError, status: :invalid_argument, message: "Empty action result"
    end
    
    # Serialize the action result
    binary_data = ActionResult.encode(request.action_result)
    
    # Store the action result
    hash = request.action_digest.hash
    case DiskStore.store_action_result(hash, binary_data) do
      :ok ->
        # Return the stored action result
        request.action_result
      {:error, reason} ->
        Logger.error("Failed to store action result #{hash}: #{inspect(reason)}")
        raise GRPC.RPCError, status: :internal, message: "Failed to store action result"
    end
  end
end
