defmodule Pillar.MigrationsMacro do
  @moduledoc """
  Migration's mechanism

  For generation migration files use task `Mix.Tasks.Pillar.Gen.Migration`

  For launching migration define own mix task or release task with code below:

  ```
  conn = Pillar.Connection.new(connection_string)
  Pillar.Migrations.migrate(conn)
  ```
  """

  defmacro __using__(args) when is_list(args) do
    quote do
      alias Pillar.Connection
      alias Pillar.Migrations.Generator
      alias Pillar.Migrations.Migrate
      alias Pillar.Migrations.Rollback

      @default_path_suffix "priv/pillar_migrations"
      @path_suffix Keyword.get(unquote(args), :path_suffix, @default_path_suffix)
      @options Keyword.get(unquote(args), :options, []) |> Enum.into(%{})

      def generate(name) do
        template = Generator.migration_template(name)

        with filepath <- Generator.migration_filepath(name, migrations_path()),
             :ok <- File.mkdir_p!(Path.dirname(filepath)),
             :ok <- File.write!(filepath, template) do
          filepath
        end
      end

      def migrate(%Connection{} = conn, path \\ nil) do
        Migrate.run_all_migrations(conn, path || migrations_path(), @options)
      end

      def rollback(%Connection{} = conn, path \\ nil, count_of_migrations \\ 1) do
        Rollback.rollback_n_migrations(
          conn,
          path || migrations_path(),
          count_of_migrations,
          @options
        )
      end

      @doc """
      Directory, where contains list of migrations.

      Warning: In case of build releases directory should be copied with app
      """
      def migrations_path do
        Path.join([get_path_prefix(), @path_suffix])
      end

      defp get_path_prefix do
        case Keyword.get(unquote(args), :path_prefix) do
          nil ->
            case Keyword.get(unquote(args), :otp_app) do
              nil ->
                ""

              otp_app when is_atom(otp_app) ->
                Application.app_dir(otp_app)
            end

          path_prefix when is_binary(path_prefix) ->
            path_prefix
        end
      end
    end
  end
end
