defmodule Mix.Tasks.Ecto.Gen.Repo do
  use Mix.Task

  import Mix.Ecto
  import Mix.Generator

  @shortdoc "Generates a new repository"

  @switches [
    repo: [:string, :keep],
  ]

  @aliases [
    r: :repo,
  ]

  @moduledoc """
  Generates a new repository.

  The repository will be placed in the `lib` directory.

  ## Examples

      $ mix ecto.gen.repo -r Custom.Repo

  This generator will automatically open the config/config.exs
  after generation if you have `ECTO_EDITOR` set in your environment
  variable.

  ## Command line options

    * `-r`, `--repo` - the repo to generate

  """

  @impl true
  def run(args) do
    no_umbrella!("ecto.gen.repo")
    {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)

    repo =
      case Keyword.get_values(opts, :repo) do
        [] -> Mix.raise "ecto.gen.repo expects the repository to be given as -r MyApp.Repo"
        [repo] -> Module.concat([repo])
        [_ | _] -> Mix.raise "ecto.gen.repo expects a single repository to be given"
      end

    config      = Mix.Project.config()
    underscored = Macro.underscore(inspect(repo))

    base = Path.basename(underscored)
    file = Path.join("lib", underscored) <> ".ex"
    app  = config[:app] || :YOUR_APP_NAME
    opts = [mod: repo, app: app, base: base]

    create_directory Path.dirname(file)
    create_file file, repo_template(opts)
    config_path = config[:config_path] || "config/config.exs"

    case File.read(config_path) do
      {:ok, contents} ->
        check = String.contains?(contents, "import Config")
        config_first_line = get_first_config_line(check) <> "\n"
        new_contents = config_first_line <> "\n" <> config_template(opts)
        Mix.shell().info [:green, "* updating ", :reset, config_path]
        File.write! config_path, String.replace(contents, config_first_line, new_contents)

      {:error, _} ->
        create_file config_path, "import Config\n\n" <> config_template(opts)
    end

    open?(config_path, 3)

    Mix.shell().info """
    Don't forget to add your new repo to your supervision tree
    (typically in lib/#{app}/application.ex):

        def start(_type, _args) do
          children = [
            #{inspect repo},
          ]

    And to add it to the list of Ecto repositories in your
    configuration files (so Ecto tasks work as expected):

        config #{inspect app},
          ecto_repos: [#{inspect repo}]

    """
  end

  defp get_first_config_line(true), do: "import Config"
  defp get_first_config_line(false), do: "use Mix.Config"

  embed_template :repo, """
  defmodule <%= inspect @mod %> do
    use Ecto.Repo,
      otp_app: <%= inspect @app %>,
      adapter: Ecto.Adapters.Postgres
  end
  """

  embed_template :config, """
  config <%= inspect @app %>, <%= inspect @mod %>,
    database: "<%= @app %>_<%= @base %>",
    username: "user",
    password: "pass",
    hostname: "localhost"
  """
end
