defmodule Firehose.Checks.NoConnShadowing do
  use Credo.Check,
    base_priority: :normal,
    category: :readability,
    explanations: [
      check: """
      Conn shadowing (`conn = get(conn, ...)`) makes Phoenix controller tests
      noisy. Use pipe chains instead:

          body = conn |> get("/path") |> html_response(200)

      Run `./refactor_conn_aliasing.sh <file>` to fix automatically.
      """
    ]

  @http_verbs ~w(get post put patch delete head options)a

  @impl true
  def run(%SourceFile{} = source_file, params) do
    issue_meta = IssueMeta.for(source_file, params)

    source_file
    |> Credo.Code.prewalk(&traverse(&1, &2, issue_meta))
    |> Enum.reverse()
  end

  defp traverse(
         {:=, meta, [{:conn, _, _}, {verb, _, [{:conn, _, _} | _]}]} = ast,
         issues,
         issue_meta
       )
       when verb in @http_verbs do
    issue = issue_for(issue_meta, meta[:line], verb)
    {ast, [issue | issues]}
  end

  defp traverse(ast, issues, _issue_meta) do
    {ast, issues}
  end

  defp issue_for(issue_meta, line_no, verb) do
    format_issue(
      issue_meta,
      message:
        "Conn shadowing detected (`conn = #{verb}(conn, ...)`). Run `./refactor_conn_aliasing.sh <file>` to fix.",
      line_no: line_no
    )
  end
end
