defmodule Mix.Tasks.Raxol.Check do
  @moduledoc """
  Run comprehensive quality checks on the Raxol codebase.

  This task runs multiple quality checks in sequence:
  1. Compilation check
  2. Format check
  3. Credo analysis
  4. Dialyzer (if available)
  5. Security audit
  6. Test suite

  ## Usage

      mix raxol.check [OPTIONS]

  ## Options

    * `--only CHECKS` - Run only specific checks (comma-separated)
    * `--skip CHECKS` - Skip specific checks (comma-separated)
    * `--strict` - Run all checks in strict mode
    * `--quick` - Run only fast checks (skip dialyzer)

  ## Examples

      # Run all checks
      mix raxol.check

      # Run only format and compile checks
      mix raxol.check --only compile,format

      # Skip dialyzer
      mix raxol.check --skip dialyzer

      # Quick check (no dialyzer)
      mix raxol.check --quick
  """

  use Mix.Task
  require Logger

  @shortdoc "Run comprehensive quality checks"

  @all_checks [:compile, :format, :credo, :dialyzer, :security, :test]
  @quick_checks [:compile, :format, :credo, :test]

  @impl Mix.Task
  def run(args) do
    {opts, _, _} =
      OptionParser.parse(args,
        strict: [
          only: :string,
          skip: :string,
          strict: :boolean,
          quick: :boolean
        ]
      )

    checks = determine_checks(opts)

    Mix.shell().info("Running Raxol quality checks: #{inspect(checks)}")
    Mix.shell().info("")

    results = Enum.map(checks, &run_check/1)

    print_summary(results)

    case Enum.any?(results, fn {_, status} -> status != :ok end) do
      true -> Mix.raise("Some checks failed")
      false -> :ok
    end
  end

  defp determine_checks(opts) do
    cond do
      opts[:only] ->
        parse_check_list(opts[:only])

      opts[:quick] ->
        @quick_checks

      true ->
        checks = @all_checks

        case opts[:skip] do
          nil ->
            checks

          skip_str ->
            skip = parse_check_list(skip_str)
            Enum.reject(checks, &(&1 in skip))
        end
    end
  end

  defp parse_check_list(str) do
    str
    |> String.split(",")
    |> Enum.map(&String.trim/1)
    |> Enum.map(&String.to_atom/1)
  end

  defp run_check(:compile) do
    Mix.shell().info("==> Running compilation check...")

    try do
      System.put_env("TMPDIR", "/tmp")
      System.put_env("SKIP_TERMBOX2_TESTS", "true")
      System.put_env("MIX_ENV", "test")

      Mix.Task.run("compile", ["--warnings-as-errors"])
      Mix.shell().info("    [OK] Compilation successful")
      {:compile, :ok}
    rescue
      e ->
        Mix.shell().error(
          "    [FAIL] Compilation failed: #{Exception.message(e)}"
        )

        {:compile, :error}
    end
  end

  defp run_check(:format) do
    Mix.shell().info("==> Checking code formatting...")

    case Mix.shell().cmd("mix format --check-formatted") do
      0 ->
        Mix.shell().info("    [OK] Code is properly formatted")
        {:format, :ok}

      _ ->
        Mix.shell().error("    [FAIL] Code formatting issues found")
        Mix.shell().info("    Run 'mix format' to fix")
        {:format, :warning}
    end
  end

  defp run_check(:credo) do
    Mix.shell().info("==> Running Credo analysis...")

    case Code.ensure_loaded?(Credo) do
      true ->
        case Mix.shell().cmd("mix credo --strict") do
          0 ->
            Mix.shell().info("    [OK] Credo analysis passed")
            {:credo, :ok}

          _ ->
            Mix.shell().error("    [FAIL] Credo found issues")
            {:credo, :warning}
        end

      false ->
        Mix.shell().info("    [WARN] Credo not available")
        {:credo, :skipped}
    end
  end

  defp run_check(:dialyzer) do
    Mix.shell().info("==> Running Dialyzer...")

    case Code.ensure_loaded?(Dialyxir) do
      true ->
        case Mix.shell().cmd("mix dialyzer") do
          0 ->
            Mix.shell().info("    [OK] Dialyzer analysis passed")
            {:dialyzer, :ok}

          _ ->
            Mix.shell().error("    [FAIL] Dialyzer found issues")
            {:dialyzer, :warning}
        end

      false ->
        Mix.shell().info("    [WARN] Dialyzer not available")
        {:dialyzer, :skipped}
    end
  end

  defp run_check(:security) do
    Mix.shell().info("==> Running security audit...")

    case Code.ensure_loaded?(Sobelow) do
      true ->
        case Mix.shell().cmd("mix sobelow --config") do
          0 ->
            Mix.shell().info("    [OK] Security audit passed")
            {:security, :ok}

          _ ->
            Mix.shell().error("    [FAIL] Security issues found")
            {:security, :warning}
        end

      false ->
        Mix.shell().info("    [WARN] Sobelow not available")
        {:security, :skipped}
    end
  end

  defp run_check(:test) do
    Mix.shell().info("==> Running test suite...")

    System.put_env("TMPDIR", "/tmp")
    System.put_env("SKIP_TERMBOX2_TESTS", "true")
    System.put_env("MIX_ENV", "test")

    # Exclude slow tests and benchmarks
    case Mix.shell().cmd(
           "mix test --exclude slow --exclude integration --exclude docker --exclude benchmark --max-failures 10"
         ) do
      0 ->
        Mix.shell().info("    [OK] All tests passed")
        {:test, :ok}

      _ ->
        Mix.shell().error("    [FAIL] Some tests failed")
        {:test, :error}
    end
  end

  defp run_check(unknown) do
    Mix.shell().error("Unknown check: #{unknown}")
    {unknown, :error}
  end

  defp print_summary(results) do
    Mix.shell().info("")
    Mix.shell().info("=================")
    Mix.shell().info("Check Summary:")
    Mix.shell().info("=================")

    Enum.each(results, fn {check, status} ->
      status_str =
        case status do
          :ok -> "[OK] PASSED"
          :warning -> "[WARN] WARNING"
          :error -> "[FAIL] FAILED"
          :skipped -> "[SKIP] SKIPPED"
        end

      Mix.shell().info("  #{check}: #{status_str}")
    end)

    Mix.shell().info("")
  end
end
