defmodule Mix.Tasks.Compile.UnusedTest do
  use ExUnit.Case, async: false

  alias Mix.Tasks.Compile.Unused
  alias MixUnused.Analyzer

  setup_all do
    # Compute the heavy Analyzer state once and stash it so each Unused.run/1
    # call below skips re-scanning every BEAM file.
    {:ok, state: Analyzer.compute_state()}
  end

  setup %{state: state} do
    # Each test runs in its own process — no need to clean up the dict.
    Process.put(:mix_unused_extra_analyze_opts, precomputed_state: state)
    :ok
  end

  describe "manifests/0" do
    test "returns an empty list (no manifest tracking)" do
      assert Unused.manifests() == []
    end
  end

  describe "clean/0" do
    test "returns :ok" do
      assert Unused.clean() == :ok
    end
  end

  describe "run/1" do
    test "manifests/0 and clean/0 are idempotent" do
      assert Unused.manifests() == Unused.manifests()
      assert Unused.clean() == :ok
      assert Unused.clean() == :ok
    end

    test "exercises run/1 across all severity arg variants" do
      # Each Unused.run/1 call invokes the analyzer (which scans all BEAMs
      # and is the slow part). We exercise every severity branch in a
      # single test so we only pay analyze cost a small number of times.
      arg_lists = [
        [],
        ["--severity", "error"],
        ["--severity", "warning"],
        ["--severity", "info"],
        ["--severity", "garbage"]
      ]

      assert Unused.manifests() == []

      ExUnit.CaptureIO.capture_io(fn ->
        for args <- arg_lists do
          result = Unused.run(args)
          # credo:disable-for-next-line Jump.CredoChecks.ConditionalAssertion
          assert match?({:ok, _}, result) or match?({:error, _}, result)
          # compile result legitimately returns ok or error
        end
      end)
    end
  end
end
