defmodule ExGuard do
  @moduledoc """
  This module is the main process which listens to file changes,
  triggers commands and notifications
  """

  defstruct changed_files: []

  use GenServer
  alias ExGuard.Guard
  alias ExGuard.Config

  @doc false
  def start_link do
    :ok = Application.start(:fs, :permanent)
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  @doc false
  def init(_) do
    :erlang.send_after(2000, self(), :handle_file_changes)
    :ok = :fs.subscribe()
    {:ok, %ExGuard{}}
  end

  @doc """
  Executes on file changes
  """
  def handle_info({_pid, {:fs, :file_event}, {path, _event}}, state) do
    matched? =
      path
      |> to_string
      |> Config.match_guards()

    changed_files =
      if matched? == [] do
        state.changed_files
      else
        Enum.uniq(state.changed_files ++ [to_string(path)])
      end

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

  @doc false
  def handle_info(:handle_file_changes, state) do
    case state.changed_files do
      [] ->
        :noop

      changed_files ->
        changed_files
        |> Enum.map(&Config.match_guards/1)
        |> List.flatten()
        |> Enum.uniq()
        |> execute_guards
    end

    :erlang.send_after(2000, self(), :handle_file_changes)
    {:noreply, %{state | changed_files: []}}
  end

  @doc false
  def execute_guards(guards) do
    guards
    |> Enum.map(&Guard.execute(&1))
    |> Enum.each(&Guard.notify(&1))
  end
end
