defmodule ClipperBot.Application do
  alias ClipperBot.LogFilter
  alias ClipperBot.Alarm
  alias Bot.{Clip, Register}
  alias Db.Repo
  alias SegmentsDir
  alias ClipDir

  alias Bot.HTTPApiRateLimiterSupervisor, as: RateLimiter

  alias Ater.Command

  use Application

  @impl true
  def start(_type, _args) do
    :logger.add_primary_filter(:dbg_filter, {&LogFilter.filter/2, []})

    :ok = Alarm.swap_default_handler()

    # int or :global
    guild_id = Application.fetch_env!(:clipper_bot, :guild_id)
    # int
    app_id = Application.fetch_env!(:clipper_bot, :app_id)
    # int
    intents = Application.fetch_env!(:clipper_bot, :intents)
    # string
    token = Application.fetch_env!(:clipper_bot, :bot_token)

    app = %{
      id: app_id,
      bot: %Pana.Bot{
        intents: intents,
        token: token
      }
    }

    commands = Clip.commands() ++ Register.commands()

    # Strict matching, because `:global` is too sensitive
    # to be a default.
    commands =
      case guild_id do
        :global ->
          commands

        # Fill in the guild ids unless already filled.
        guild_id when is_integer(guild_id) ->
          Enum.map(commands, fn
            %Command{guild_id: nil} = cmd -> %{cmd | guild_id: guild_id}
            %Command{guild_id: _} = cmd -> cmd
          end)
      end

    ater_config = %Ater.Config{
      app: app,
      commands: commands,
      fallback: Bot.OnGuildJoin,
      guild_id: guild_id,
      rate_limiter: RateLimiter
    }

    segment_dir = Application.get_env(:clipper_bot, :segment_dir)
    segment_dir_max_size = Application.get_env(:clipper_bot, :segment_dir_max_size)
    clip_dir = Application.get_env(:clipper_bot, :clip_dir)
    clip_dir_max_size = Application.get_env(:clipper_bot, :clip_dir_max_size)

    children = [
      Repo,
      {ClipDir, {clip_dir, clip_dir_max_size}},
      Web.Endpoint,
      Bot.Cache,
      {SegmentsDir, {segment_dir, segment_dir_max_size}},
      # For Discord's HTTP API
      {Pana.HTTPApi.RateLimiterSupervisor, name: RateLimiter},
      # Channel registry that manages /register commands
      {ChnRegistry, chn_reg_callback_init(app)},
      # Discord commands library
      {Ater, {ater_config, name: Ater}}
    ]

    opts = [strategy: :one_for_one, name: Web.Supervisor]
    Supervisor.start_link(children, opts)
  end

  defp chn_reg_callback_init(app) do
    Bot.Register.watcher_callbacks(app, RateLimiter)
  end
end
