mdb_socket = System.get_env("MDBSOCKET")

socket? =
  match?({:unix, _}, :os.type()) and List.to_integer(:erlang.system_info(:otp_release)) >= 19 and
    is_binary(mdb_socket) and File.exists?(mdb_socket)

ExUnit.configure(
  exclude: [
    ssl_tests: true,
    json: System.get_env("JSON_SUPPORT") != "true",
    socket: not socket?,
    geometry: System.get_env("GEOMETRY_SUPPORT") == "false"
  ]
)

ExUnit.start()

run_cmd = fn cmd ->
  key = :ecto_setup_cmd_output
  Process.put(key, "")
  # TODO: Not relay on Mix.Shell.cmd
  cmd_fun =
    cond do
      function_exported?(Mix.Shell, :cmd, 2) -> &Mix.Shell.cmd/2
      function_exported?(Mix.Shell, :cmd, 3) -> &Mix.Shell.cmd(&1, [], &2)
    end

  status =
    cmd_fun.(cmd, fn data ->
      current = Process.get(key)
      Process.put(key, current <> data)
    end)

  output = Process.get(key)
  Process.put(key, "")
  {status, output}
end

mysql_pass_switch =
  if mysql_root_pass = System.get_env("MYSQL_ROOT_PASSWORD") do
    "-p#{mysql_root_pass}"
  else
    ""
  end

mysql_port = System.get_env("MDBPORT") || 3306
mysql_host = System.get_env("MDBHOST") || "localhost"

mysql_connect =
  "-u root #{mysql_pass_switch} --host=#{mysql_host} --port=#{mysql_port} --protocol=tcp"

sql = """
  CREATE TABLE test1 (id serial, title text);
  INSERT INTO test1 VALUES(1, 'test');
  INSERT INTO test1 VALUES(2, 'test2');
  DROP TABLE test1;
"""

create_user =
  case System.get_env("DB") do
    "mysql:5.6" -> "CREATE USER"
    _ -> "CREATE USER IF NOT EXISTS"
  end

cmds = [
  ~s(mysql #{mysql_connect} -e "DROP DATABASE IF EXISTS mariaex_test;"),
  ~s(mysql #{mysql_connect} -e "CREATE DATABASE mariaex_test DEFAULT CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';"),
  ~s(mysql #{mysql_connect} -e "#{create_user} 'mariaex_user'@'%' IDENTIFIED BY 'mariaex_pass';"),
  ~s(mysql #{mysql_connect} -e "GRANT ALL ON *.* TO 'mariaex_user'@'%' WITH GRANT OPTION"),
  ~s(mysql --host=#{mysql_host} --port=#{mysql_port} --protocol=tcp -u mariaex_user -pmariaex_pass mariaex_test -e "#{
    sql
  }")
]

Enum.each(cmds, fn cmd ->
  {status, output} = run_cmd.(cmd)
  IO.puts("--> #{output}")

  if status != 0 do
    IO.puts("""
    Command:
    #{cmd}
    error'd with:
    #{output}
    Please verify the user "root" exists and it has permissions to
    create databases and users.
    If the "root" user requires a password, set the environment
    variable MYSQL_ROOT_PASSWORD to its value.
    Beware that the password may be visible in the process list!
    """)

    System.halt(1)
  end
end)

defmodule Mariaex.TestHelper do
  defmacro query(stat, params, opts \\ []) do
    quote do
      pid = var!(context)[:pid]

      case Mariaex.Connection.query(pid, unquote(stat), unquote(params), unquote(opts)) do
        {:ok, %Mariaex.Result{rows: nil}} -> :ok
        {:ok, %Mariaex.Result{rows: rows}} -> rows
        {:error, err} -> err
      end
    end
  end

  defmacro execute_text(stat, params, opts \\ []) do
    quote do
      opts = [query_type: :text] ++ unquote(opts)

      case Mariaex.query(var!(context)[:pid], unquote(stat), unquote(params), opts) do
        {:ok, %Mariaex.Result{rows: nil}} -> :ok
        {:ok, %Mariaex.Result{rows: rows}} -> rows
        {:error, err} -> err
      end
    end
  end

  defmacro with_prepare!(name, stat, params, opts \\ []) do
    quote do
      conn = var!(context)[:pid]
      query = Mariaex.prepare!(conn, unquote(name), unquote(stat), unquote(opts))

      case Mariaex.execute!(conn, query, unquote(params)) do
        %Mariaex.Result{rows: nil} -> :ok
        %Mariaex.Result{rows: rows} -> rows
      end
    end
  end

  defmacro prepare(stat, opts \\ []) do
    quote do
      case Mariaex.prepare(var!(context)[:pid], unquote(stat), unquote(opts)) do
        {:ok, %Mariaex.Query{} = query} -> query
        {:error, err} -> err
      end
    end
  end

  defmacro prepared do
    quote do
      [[_name, count]] = execute_text("SHOW GLOBAL STATUS LIKE '%Prepared_stmt_count%'", [])
      String.to_integer(count)
    end
  end

  defmacro execute(query, params, opts \\ []) do
    quote do
      case Mariaex.execute(var!(context)[:pid], unquote(query), unquote(params), unquote(opts)) do
        {:ok, %Mariaex.Query{}, %Mariaex.Result{rows: nil}} -> :ok
        {:ok, %Mariaex.Query{}, %Mariaex.Result{rows: rows}} -> rows
        {:error, err} -> err
      end
    end
  end

  defmacro prepare_execute(name, statement, params, opts \\ []) do
    quote do
      case Mariaex.prepare_execute(var!(context)[:pid], unquote(name), unquote(statement), unquote(params), unquote(opts)) do
        {:ok, %Mariaex.Query{} = query, %Mariaex.Result{rows: rows}} -> {query, rows}
        {:error, err} -> err
      end
    end
  end

  defmacro close(query, opts \\ []) do
    quote do
      case Mariaex.close(var!(context)[:pid], unquote(query), unquote(opts)) do
        :ok -> :ok
        {:error, err} -> err
      end
    end
  end

  def length_encode_row(row) do
    Enum.map_join(row, &(<<String.length(&1)>> <> &1))
  end
end
