defmodule Openpod.Boundary.ArchiveServer do
  @moduledoc false

  use GenServer, restart: :temporary
  require Logger
  alias Openpod
  alias Openpod.Provider.Archive

  @registry :podcast_registry
  @archive_supervisor :archive_supervisor

  ##########
  ### API

  def start_link(identifier) do
    GenServer.start_link(__MODULE__, identifier, name: via(identifier))
  end

  def cache_or_fetch(identifier) do
    case DynamicSupervisor.start_child(@archive_supervisor, {__MODULE__, identifier}) do
      {:ok, pid} -> {:ok, pid}
      {:error, {:already_started, pid}} -> {:ok, pid}
      error -> error
    end
    # %{id: Worker, start: { Worker, :start_link,  [child_name]}, restart: :transient})
  end

  def get_feed(identifier) do
    cache_or_fetch(identifier)
    GenServer.call(via(identifier), :get_feed)
  end

  def reload(pid) when is_pid(pid) do
    Logger.debug "starting reload..."
    GenServer.cast(pid, :reload)
  end

  def reload(identifier) do
    cache_or_fetch(identifier)
    GenServer.call(via(identifier), :reload)
  end

  def count(pid) when is_pid(pid) do
    GenServer.call(pid, :count)
  end

  def count(identifier) when is_binary(identifier) do
    GenServer.call(via(identifier), :count)
  end

  ##########
  ### SERVER

  def init(identifier) do
    {:ok, %{count: 1, identifier: identifier, feed_data: fetch_feed_data(identifier)}}
  end

  def handle_call(:get_feed, _from, feed = %{count: count, feed_data: feed_data}) do
    Logger.debug "retrieve cached feed data for #{feed[:identifier]}..."
    {:reply, feed_data, %{feed | count: count + 1}, Application.fetch_env!(:openpod, :cache_duration)}
  end

  def handle_call(:reload, _from, feed = %{count: count, identifier: identifier}) do
    Logger.debug "discard cached feed data for #{identifier}..."
    feed_data = fetch_feed_data(identifier)
    {:reply, feed_data, %{feed | count: count + 1, feed_data: feed_data}}
  end

  def handle_call(:count, _from, feed = %{identifier: identifier, count: count}) do
    {:reply, %{identifier: identifier, count: count}, feed}
  end

  def handle_cast(:reload, feed = %{identifier: identifier}) do
    Logger.debug "discard cached feed data for #{identifier}..."
    {:noreply, %{feed | feed_data: fetch_feed_data(identifier)}}
  end

  def handle_info(:timeout, %{identifier: identifier}) do
    Logger.debug "shutting down #{identifier}..."
    {:stop, :normal, []}
  end

  ##########
  ### PRIVATES

  defp fetch_feed_data(identifier) do
    Logger.debug "fetching archive metadata for #{identifier}..."
    Archive.Parser.by_identifier(identifier)
  end

  defp via(identifier) do
    {:via, Registry, {@registry, identifier}}
  end
end
