defmodule Mix.Tasks.Lvn.Gen do
  use Mix.Task

  alias Mix.LiveViewNative.Context

  import Mix.LiveViewNative.Context, only: [
    last?: 2
  ]

  @shortdoc "Generates the Native module"

  @moduledoc """
  #{@shortdoc}

      $ mix lvn.gen

  Instructions will be printed for configuring your application. And a
  new `Native` module will be copied into the `lib/` directory of your application.

  ## Options

  * `--no-live-form` - don't include `LiveViewNative.LiveForm` content in the `Native` module
  * `--no-gettext` - don't include Gettext support
  """

  @impl true
  @doc false
  def run(args) do
    if Mix.Project.umbrella?() do
      Mix.raise(
        "mix lvn.gen must be invoked from within your *_web application root directory"
      )
    end

    args
    |> Context.build(__MODULE__)
    |> gen()
  end

  def gen(context) do
    files = files_to_be_generated(context)
    Context.prompt_for_conflicts(files)
    copy_new_files(context, files)
  end

  @doc false
  def switches, do: [
    context_app: :string,
    web: :string,
    live_form: :boolean,
    gettext: :boolean
  ]

  @doc false
  def validate_args!([]), do: [nil]
  def validate_args!(_args) do
    Mix.raise("""
    mix lvn.gen does not take any arguments, only the following switches:

    --no-live-form
    --context-app
    --no-gettext
    --web
    """)
  end

  defp files_to_be_generated(context) do
    path =
      context.context_app
      |> Mix.Phoenix.web_path("..")
      |> Path.relative_to(File.cwd!())

    file = Macro.underscore(context.native_module) <> ".ex"

    [{:eex, "app_name_native.ex", Path.join(path, file)}]
  end

  defp copy_new_files(%Context{} = context, files) do
    plugins = Mix.LiveViewNative.plugins() |> Map.values()

    plugins? = length(plugins) > 0

    apps = Mix.Project.deps_apps()

    live_form? =
      Keyword.get(context.opts, :live_form, true) && Enum.member?(apps, :live_view_native_live_form)

    gettext? =
      Keyword.get(context.opts, :gettext, true) && Enum.member?(apps, :gettext)

    binding = [
      context: context,
      plugins: plugins,
      plugins?: plugins?,
      last?: &last?/2,
      assigns: %{
        live_form?: live_form?,
        gettext: gettext?,
        formats: formats(),
        layouts: layouts(context.web_module)
      }
    ]

    Mix.Phoenix.copy_from([".", :live_view_native], "priv/templates/lvn.gen", binding, files)

    context
  end

  defp formats do
    LiveViewNative.available_formats()
  end

  defp layouts(web_module) do
    Enum.map(formats(), fn(format) ->
      format_module =
        format
        |> LiveViewNative.fetch_plugin!()
        |> Map.fetch!(:module_suffix)

      {format, {Module.concat([web_module, Layouts, format_module]), :app}}
    end)
  end
end
