defmodule Refuge.GRPC.EventStore do
  @moduledoc """
  GenServer for storing and broadcasting build events from gRPC.
  Acts as a central event store that LiveViews can subscribe to.
  """
  use GenServer
  require Logger

  # Maximum number of events to store
  @max_events 100

  # Client API

  def start_link(_) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  @doc """
  Store a new event with a given type and data.
  The event will be timestamped and added to the store.
  """
  def store_event(type, data) do
    GenServer.cast(__MODULE__, {:store_event, type, data})
  end

  @doc """
  Subscribe the calling process to receive build events.
  """
  def subscribe do
    GenServer.call(__MODULE__, :subscribe)
  end

  @doc """
  List all events currently in the store.
  """
  def list_events do
    GenServer.call(__MODULE__, :list_events)
  end

  @doc """
  Get events from the store.
  """
  def get_events do
    GenServer.call(__MODULE__, :get_events)
  end

  # Server Callbacks

  @impl true
  def init(_) do
    {:ok, %{events: [], subscribers: []}}
  end

  @impl true
  def handle_call(:subscribe, {from, _}, state) do
    # Add the calling process to the subscribers list
    Logger.info("Process #{inspect(from)} subscribed to event store")
    Process.monitor(from)
    {:reply, :ok, %{state | subscribers: [from | state.subscribers]}}
  end

  @impl true
  def handle_call(:list_events, _from, state) do
    {:reply, state.events, state}
  end

  @impl true
  def handle_call(:get_events, _from, state) do
    {:reply, state.events, state}
  end

  @impl true
  def handle_cast({:store_event, type, data}, state) do
    # Create a timestamped event
    event = %{
      id: System.unique_integer([:positive]),
      timestamp: DateTime.utc_now(),
      type: type,
      data: data
    }

    # Add to events list, keeping only the most recent @max_events
    events = [event | state.events] |> Enum.take(@max_events)

    # Broadcast to all subscribers
    for subscriber <- state.subscribers do
      # Send with the expected :new_build_event format for LiveView
      if type == :new_build_event do
        send(subscriber, {type, data})
      else
        # For backward compatibility, also send original format
        send(subscriber, {type, data})
      end
    end

    {:noreply, %{state | events: events}}
  end

  @impl true
  def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do
    # Remove the subscriber if it goes down
    subscribers = Enum.reject(state.subscribers, &(&1 == pid))
    {:noreply, %{state | subscribers: subscribers}}
  end
end
