defmodule Credo.Check.Readability.AliasAs do
  use Credo.Check,
    id: "EX3001",
    base_priority: :low,
    tags: [:experimental],
    param_defaults: [
      ignore: []
    ],
    explanations: [
      check: """
      Aliases can be "renamed" using the `:as` option, but that sometimes
      makes the code more difficult to read.

          # preferred

          def MyModule do
            alias MyApp.Module1

            def my_function(foo) do
              Module1.run(foo)
            end
          end

          # NOT preferred

          def MyModule do
            alias MyApp.Module1, as: M1

            def my_function(foo) do
              # what the heck is `M1`?
              M1.run(foo)
            end
          end

      Please note that you might want to deactivate this check for cases in which you have an alias that
      is used tons throughout your codebase.

      If, for example, you are using a third-party module named `FlupsyTopsyDataRetentionServiceServer`
      in half your modules, it is of course reasonable to alias it to `Server`.

      Like all `Readability` issues, this one is not a technical concern.
      But you can improve the odds of others reading and liking your code by making
      it easier to follow.
      """,
      params: [
        ignore: "List of modules to ignore and allow to `alias Module, as: ...`"
      ]
    ]

  @doc false
  @impl true
  def run(%SourceFile{} = source_file, params) do
    ctx = Context.build(source_file, params, __MODULE__)
    ctx = %{ctx | params: %{ignore: Enum.map(ctx.params.ignore, &Credo.Code.Name.full/1)}}
    result = Credo.Code.prewalk(source_file, &walk/2, ctx)
    result.issues
  end

  defp walk(
         {:alias, _, [{_, _, _} = name, [as: {_, meta, _}]]},
         %{params: %{ignore: ignore}} = ctx
       ) do
    fullname = Credo.Code.Name.full(name)

    if Enum.member?(ignore, fullname) do
      {nil, ctx}
    else
      {nil, put_issue(ctx, issue_for(ctx, meta))}
    end
  end

  defp walk({:alias, _, [{_, _, _}, [as: {_, meta, _}]]}, ctx) do
    {nil, put_issue(ctx, issue_for(ctx, meta))}
  end

  defp walk(ast, ctx) do
    {ast, ctx}
  end

  defp issue_for(ctx, meta) do
    format_issue(
      ctx,
      message: "Avoid using the `:as` option with `alias`.",
      trigger: "as:",
      line_no: meta[:line]
    )
  end
end
