defmodule Mix.Tasks.Raxol.New do
  @moduledoc """
  Generates a new Raxol application.

  ## Usage

      mix raxol.new my_app

  When run without flags, an interactive prompt guides you through setup.
  Pass flags to skip prompts.

  ## Options

    * `--module` - The main module name (default: derived from app name)
    * `--sup` - Generate an OTP application with a supervision tree
    * `--ssh` - Include SSH server boilerplate for remote access
    * `--liveview` - Include Phoenix LiveView bridge for browser rendering
    * `--template` - Starter template: `counter` (default), `blank`, `todo`, `dashboard`
    * `--ci` - Generate GitHub Actions CI workflow
    * `--no-test` - Skip generating test files
    * `--install` - Run `mix deps.get`, compile, and test after generation
    * `--list` - Show available templates with descriptions

  ## Examples

      mix raxol.new dashboard
      mix raxol.new my_tui --module MyTUI
      mix raxol.new my_app --sup --template todo --ci
      mix raxol.new my_app --ssh --install
      mix raxol.new --list
  """

  use Mix.Task

  @compile {:no_warn_undefined, Mix.Raxol.Templates}
  @compile {:no_warn_undefined, Mix.Raxol.Generator}

  alias Mix.Raxol.{Generator, Templates}

  @shortdoc "Generate a new Raxol application"

  @raxol_version Mix.Project.config()[:version]

  @switches [
    module: :string,
    no_test: :boolean,
    sup: :boolean,
    ssh: :boolean,
    liveview: :boolean,
    template: :string,
    install: :boolean,
    list: :boolean,
    ci: :boolean
  ]

  @templates Templates.templates()

  @impl Mix.Task
  def run(args) do
    case OptionParser.parse(args, strict: @switches) do
      {opts, [], _} -> handle_no_args(opts)
      {opts, [name], _} -> handle_name_arg(name, opts)
      _ -> print_usage()
    end
  end

  defp handle_no_args(opts) do
    if Keyword.get(opts, :list, false) do
      Templates.print_template_list()
    else
      print_usage()
    end
  end

  defp handle_name_arg(name, opts) do
    if Keyword.get(opts, :list, false) do
      Templates.print_template_list()
    else
      generate_app(name, opts)
    end
  end

  defp generate_app(name, opts) do
    opts = maybe_prompt_interactive(opts)
    template = Keyword.get(opts, :template, "counter")

    if template not in @templates do
      Mix.raise(
        "Unknown template: #{template}. Available: #{Enum.join(@templates, ", ")}"
      )
    end

    Generator.generate(
      name,
      Keyword.put(opts, :template, template),
      @raxol_version
    )
  end

  # ---------------------------------------------------------------------------
  # Interactive prompts
  # ---------------------------------------------------------------------------

  defp maybe_prompt_interactive(opts) do
    has_flags =
      Keyword.has_key?(opts, :template) or
        Keyword.has_key?(opts, :sup) or
        Keyword.has_key?(opts, :ssh) or
        Keyword.has_key?(opts, :liveview) or
        Keyword.has_key?(opts, :ci)

    if has_flags do
      opts
    else
      prompt_all(opts)
    end
  end

  defp prompt_all(opts) do
    opts
    |> Templates.prompt_template()
    |> prompt_yes_no(:sup, "Generate OTP supervision tree?")
    |> prompt_yes_no(:ssh, "Include SSH server?")
    |> prompt_yes_no(:liveview, "Include LiveView bridge?")
    |> prompt_yes_no(:ci, "Generate GitHub Actions CI?")
  end

  defp prompt_yes_no(opts, key, question) do
    if Keyword.has_key?(opts, key) do
      opts
    else
      answer =
        Mix.shell().prompt("#{question} [y/N]")
        |> String.trim()
        |> String.downcase()

      Keyword.put(opts, key, answer in ~w(y yes))
    end
  end

  # ---------------------------------------------------------------------------
  # Usage
  # ---------------------------------------------------------------------------

  defp print_usage do
    Mix.shell().error("Usage: mix raxol.new APP_NAME [options]")
    Mix.shell().error("")
    Mix.shell().error("Options:")

    Mix.shell().error(
      "  --module NAME      Module name (default: derived from app name)"
    )

    Mix.shell().error(
      "  --sup              Generate OTP application with supervision tree"
    )

    Mix.shell().error("  --ssh              Include SSH server boilerplate")
    Mix.shell().error("  --liveview         Include Phoenix LiveView bridge")

    Mix.shell().error(
      "  --template NAME    Starter template: #{Enum.join(@templates, ", ")}"
    )

    Mix.shell().error(
      "  --ci               Generate GitHub Actions CI workflow"
    )

    Mix.shell().error("  --no-test          Skip test files")

    Mix.shell().error(
      "  --install          Run mix deps.get + compile + test after generation"
    )

    Mix.shell().error(
      "  --list             Show available templates with descriptions"
    )

    Mix.shell().error("")
    Mix.shell().error("Example: mix raxol.new my_app --sup --template todo")
  end
end
