defmodule Mix.Tasks.Thesis.Install do
  use Mix.Task
  import Mix.Thesis.Utils

  @migrations [
    "create_thesis_tables",
    "add_meta_to_thesis_page_contents",
    "add_indexes_to_tables",
    "add_template_and_redirect_url_to_thesis_pages",
    "change_content_default_for_page_content",
    "create_thesis_files_table",
    "create_thesis_backups_table",
    "change_and_convert_page_data_type_in_backups_table"
  ]
  @template_files [
    {"priv/templates/thesis.install/thesis_auth.exs", "lib/YOURAPP/thesis_auth.ex"}
  ]

  @shortdoc "Generates Thesis code in your Phoenix app"

  @moduledoc """
  Installs Thesis by adding these to your host app:

  * lib/thesis_auth.ex - Contains a function to handle authorization
  * migration - creates the two tables needed for Thesis.EctoStore
  * config/config.exs - adds Thesis config code
  * web/web.ex - adds Thesis `use` statements

  You should be able to run this mix task multiple times without harm. It will
  automatically detect when a file or line of code exists and skip that step.
  """

  @doc false
  def run(_args) do
    thesis_templates()
    thesis_config()
    thesis_web()

    status_msg("done",
      "Now run #{IO.ANSI.blue}mix ecto.migrate#{IO.ANSI.reset} to ensure your database is up to date.")
  end

  @doc false
  def thesis_templates do
    migration_files = @migrations
                      |> Enum.filter(&migration_missing?/1)
                      |> Enum.with_index
                      |> Enum.map(&migration_tuple/1)
    all_templates = @template_files ++ migration_files

    all_templates
    |> Stream.map(&render_eex/1)
    |> Stream.map(&replace_yourapp(&1, to_string(Mix.Phoenix.otp_app))) # TODO: better way to infer app path?
    |> Stream.map(&copy_to_target/1)
    |> Stream.run
  end

  @doc false
  def thesis_config do
    status_msg("updating", "config/config.exs")
    dest_file_path = Path.join [File.cwd! | ~w(config config.exs)]
    dest_file_path
    |> File.read!()
    |> insert_thesis()
    |> overwrite_file(dest_file_path)
  end

  @doc false
  def thesis_web do
    status_msg("updating", "web.ex")

    # Phoenix 1.2 style
    phx_12 = "web/web.ex"
    phx_13 = List.first(Path.wildcard("lib/*_web.ex")) || ""

    dest_file_path =
      (File.exists?(phx_12) && phx_12)
      || (File.exists?(phx_13) && phx_13)

    if dest_file_path do
      dest_file_path
      |> File.read!()
      |> insert_controller()
      |> insert_view()
      |> insert_router()
      |> overwrite_file(dest_file_path)
    else
      status_msg("warning", "couldn't find web.ex! See https://github.com/infinitered/thesis-phoenix/blob/master/README_INSTALL.md.")
    end
  end

  defp insert_controller(source) do
    insert_in(source, "controller", "\n      use Thesis.Controller\n")
  end

  defp insert_view(source) do
    insert_in(source, "view", "\n      use Thesis.View\n")
  end

  defp insert_router(source) do
    insert_in(source, "router", "\n      use Thesis.Router\n")
  end

  defp insert_in(source, block, insertion) do
    if String.contains?(source, insertion) do
      source
    else
      match =
        Regex.scan(~r/def #{block} do\n\s.+end\n\s*end/Usm, source)
        |> List.flatten
        |> List.first

      if match do
        inserted = String.replace(match, "    end\n  end", "#{insertion}    end\n  end")
        String.replace(source, match, inserted)
      else
        source
      end
    end
  end

  # defp insert_at(source, pattern, inserted) do
  #   if String.contains?(source, inserted) do
  #     source
  #   else
  #     String.replace(source, pattern, pattern <> inserted)
  #   end
  # end

  defp insert_thesis(source) do
    if String.contains? source, "config :thesis," do
      status_msg("skipping", "thesis config. It already exists.")
      :skip
    else
      source <> """

      # Thesis Main Config
      config :thesis,
        store: Thesis.EctoStore,
        authorization: #{Mix.Phoenix.base}.ThesisAuth,
        uploader: Thesis.RepoUploader

      # Thesis Store Config
      config :thesis, Thesis.EctoStore, repo: #{Mix.Phoenix.base}.Repo

      # Thesis Notifications Config
      # config :thesis, :notifications,
      #   add_page: [],
      #   page_settings: [],
      #   import_export_restore: []

      # Thesis Dynamic Pages Config
      # config :thesis, :dynamic_pages,
      #   view: #{Mix.Phoenix.base}Web.PageView,
      #   templates: ["index.html", "otherview.html"],
      #   not_found_view: #{Mix.Phoenix.base}Web.ErrorView,
      #   not_found_template: "404.html"
      
      """
    end
  end

  defp migration_missing?(filename) do
    "priv/repo/migrations"
    |> File.ls!
    |> Enum.all?(fn (f) -> !String.contains?(f, filename) end)
  end

  defp migration_tuple({filename, i}) do
    ts = String.to_integer(timestamp()) + i
    {"priv/templates/thesis.install/#{filename}.exs", "priv/repo/migrations/#{ts}_#{filename}.exs"}
  end
end
