defmodule TeslaMate.Mqtt do
  use Supervisor

  alias __MODULE__.{Publisher, PubSub, Handler}

  # API

  def start_link(opts) do
    Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
  end

  @impl true
  def init(opts) do
    client_id = generate_client_id()

    children = [
      {Tortoise311.Connection, connection_config(opts) ++ [client_id: client_id]},
      {Publisher, client_id: client_id},
      {PubSub, namespace: opts[:namespace]}
    ]

    Supervisor.init(children, strategy: :one_for_one)
  end

  # Private

  alias Tortoise311.Transport

  defp connection_config(opts) do
    socket_opts =
      if opts[:ipv6],
        do: [:inet6],
        else: []

    server =
      if opts[:tls] do
        verify =
          if opts[:accept_invalid_certs],
            do: :verify_none,
            else: :verify_peer

        {Transport.SSL,
         host: opts[:host],
         port: opts[:port] || 8883,
         cacertfile: CAStore.file_path(),
         verify: verify,
         opts: socket_opts}
      else
        {Transport.Tcp, host: opts[:host], port: opts[:port] || 1883, opts: socket_opts}
      end

    [
      user_name: opts[:username],
      password: opts[:password],
      server: server,
      handler: {Handler, []},
      subscriptions: []
    ]
  end

  defp generate_client_id do
    "TESLAMATE_" <> (:rand.uniform() |> to_string() |> Base.encode16() |> String.slice(0..10))
  end
end
