# lib/forum/application.ex
defmodule Forum.Application do
  use Application

  @impl true
  def start(_type, _args) do
    db_config = Application.fetch_env!(:forum, Forum.DB)
    server_config = Application.fetch_env!(:forum, :server)

    children =
      [
        {Postgrex, [name: Forum.DB] ++ db_config},

        # Unique process names for forms, networks, public sites, and HTTP servers.
        {Registry, keys: :unique, name: Forum.ProcessRegistry},

        # WebSocket clients subscribe here for live forms.html updates.
        {Registry, keys: :duplicate, name: Forum.FormsSubscriberRegistry},

        Forum.LogStore,
        Forum.Networks,
        Forum.PublicSiteSupervisor,

        # Parses forms.html and supervises one QuickBEAM runtime per
        # <forms-> child element. Also serves Forum.Forms.html/0.
        Forum.Forms,

        # Re-parses forms.html whenever the file changes on disk.
        Forum.Forms.Watcher,

        # Main HTTP + WebSocket server.
        http_server_child(:main, Forum.Router, server_config[:port])
      ] ++ network_site_servers(server_config)

    with {:ok, sup} <- Supervisor.start_link(children, strategy: :one_for_one, name: Forum.Supervisor) do
      # DynamicSupervisor can't start children from its own init/1, so
      # we kick off the initial parse + spawn here.
      Forum.Forms.reload()
      Forum.Networks.start_networks()
      Forum.PublicSiteSupervisor.start_networks()
      {:ok, sup}
    end
  end

  defp network_site_servers(server_config) do
    base_port = Keyword.get(server_config, :network_port_base, 4001)

    Forum.Networks.network_slugs()
    |> Enum.with_index()
    |> Enum.map(fn {network, index} ->
      http_server_child(
        {:network, network},
        {Forum.PublicSiteRouter, network: network},
        base_port + index
      )
    end)
  end

  defp http_server_child(key, plug, port) do
    Supervisor.child_spec(
      {Bandit,
       plug: plug,
       port: port,
       thousand_island_options: [
         supervisor_options: [
           name: {:via, Registry, {Forum.ProcessRegistry, {:http_server, key}}}
         ]
       ]},
      id: {:http_server, key, port}
    )
  end
end
