defmodule Mix.Tasks.Margaret.CreateUser do
  @moduledoc """
  Creates a user.
  """

  @shortdoc "Creates a user."

  use Mix.Task
  import Mix, only: [shell: 0]
  import Mix.Ecto, only: [ensure_started: 2]

  alias Margaret.{Repo, Accounts}
  alias Accounts.User

  @switches [
    email: :string,
    username: :string,
    admin: :boolean,
    employee: :boolean
  ]

  @doc false
  def run(args) do
    ensure_started(Repo, [])

    args
    |> parse_opts()
    |> do_run()
  end

  defp do_run(opts) do
    {_, attrs} =
      {opts, %{}}
      |> get_email()
      |> get_username()
      |> get_is_admin()
      |> get_is_employee()

    token =
      attrs
      |> create_user()
      |> get_token()

    shell().info("User created successfully.")
    shell().info("Token: #{token}")
  end

  defp get_email({opts, _attrs} = tuple) do
    email =
      case Keyword.get(opts, :email) do
        nil ->
          "Email:"
          |> shell().prompt()
          |> String.trim()

        email ->
          email
      end

    put_email(tuple, email)
  end

  defp get_username({opts, _attrs} = tuple) do
    username =
      case Keyword.get(opts, :username) do
        nil ->
          "Username (leave blank for an autogenerated uuid4):"
          |> shell().prompt()
          |> String.trim()
          |> case do
            "" -> UUID.uuid4()
            username -> username
          end

        username ->
          username
      end

    put_username(tuple, username)
  end

  defp get_is_admin({opts, _attrs} = tuple) do
    is_admin =
      case Keyword.get(opts, :admin) do
        nil -> shell().yes?("Admin?")
        is_admin -> is_admin
      end

    put_is_admin(tuple, is_admin)
  end

  defp get_is_employee({opts, _attrs} = tuple) do
    is_employee =
      case Keyword.get(opts, :employee) do
        nil -> shell().yes?("Employee?")
        is_employee -> is_employee
      end

    put_is_employee(tuple, is_employee)
  end

  defp put_email(tuple, email), do: put_attr(:email).(tuple, email)

  defp put_username(tuple, username), do: put_attr(:username).(tuple, username)

  defp put_is_admin(tuple, is_admin), do: put_attr(:is_admin).(tuple, is_admin)

  defp put_is_employee(tuple, is_employee), do: put_attr(:is_employee).(tuple, is_employee)

  defp put_attr(key), do: fn {opts, attrs}, value -> {opts, Map.put(attrs, key, value)} end

  defp parse_opts(args) do
    {opts, _, _} = OptionParser.parse(args, switches: @switches)

    opts
  end

  defp create_user(attrs), do: Accounts.insert_user!(attrs)

  defp get_token(%User{} = user) do
    {:ok, token, _} = MargaretWeb.Guardian.encode_and_sign(user)

    token
  end
end
