defmodule PlausibleWeb.Live.Flash do
  @moduledoc """
  Flash component for LiveViews - works also when embedded within dead views
  """

  use Phoenix.Component
  alias Phoenix.LiveView.JS
  alias Phoenix.Flash

  @flash_timer_assigns %{
    success: :clear_flash_success,
    error: :clear_flash_error
  }

  @flash_timer_timeout_ms 5000

  defmacro __using__(_opts) do
    quote do
      import PlausibleWeb.Live.Flash, only: [put_live_flash: 3, flash_messages: 1]

      def handle_info({:clear_live_flash, key}, socket) do
        {:noreply, Phoenix.LiveView.clear_flash(socket, key)}
      end
    end
  end

  def put_live_flash(socket, key, message) when key in [:success, :error] do
    timer_key = Map.fetch!(@flash_timer_assigns, key)

    if flash_timer = socket.assigns[timer_key] do
      Process.cancel_timer(flash_timer)
    end

    flash_timer =
      Process.send_after(
        self(),
        {:clear_live_flash, key},
        @flash_timer_timeout_ms
      )

    socket
    |> Phoenix.LiveView.put_flash(key, message)
    |> assign(timer_key, flash_timer)
  end

  def flash_messages(assigns) do
    ~H"""
    <div>
      <div
        :if={@flash != %{} or Application.get_env(:plausible, :environment) == "dev"}
        class="inset-0 z-50 fixed flex flex-col-reverse items-center sm:items-end justify-start sm:justify-end px-4 py-6 pointer-events-none sm:p-6"
      >
        <.flash :if={Flash.get(@flash, :success)} key="success">
          <:icon>
            <.icon_success />
          </:icon>
          <:title>
            {Flash.get(@flash, :success_title) || "Success!"}
          </:title>
          <:message>
            {Flash.get(@flash, :success)}
          </:message>
        </.flash>
        <.flash :if={Flash.get(@flash, :error)} key="error">
          <:icon>
            <.icon_error />
          </:icon>
          <:title>
            {Flash.get(@flash, :error_title) || "Error!"}
          </:title>
          <:message>
            {Flash.get(@flash, :error)}
          </:message>
        </.flash>
        <.flash
          :if={Application.get_env(:plausible, :environment) == "dev"}
          id="live-view-connection-status"
          class="hidden"
          phx-disconnected={JS.show()}
          phx-connected={JS.hide()}
          on_close={JS.hide()}
        >
          <:icon>
            <.icon_error />
          </:icon>
          <:title>
            Oops, a server blip
          </:title>
          <:message>
            Live socket disconnected.
          </:message>
        </.flash>
      </div>
    </div>
    """
  end

  slot(:icon, required: true)
  slot(:title, required: true)
  slot(:message, required: true)
  attr(:key, :string, default: nil)
  attr(:on_close, :any, default: "lv:clear-flash")
  attr(:class, :string, default: "")
  attr(:rest, :global)

  def flash(assigns) do
    ~H"""
    <div
      class={[
        @class,
        "mb-4 max-w-sm w-full bg-white dark:bg-gray-800 shadow-lg rounded-lg pointer-events-auto"
      ]}
      {@rest}
    >
      <div class="rounded-lg ring-1/5 ring-black overflow-hidden">
        <div class="p-4">
          <div class="flex items-start">
            {render_slot(@icon)}
            <div class="ml-3 w-0 flex-1 pt-0.5">
              <p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
                {render_slot(@title)}
              </p>
              <p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">
                {render_slot(@message)}
              </p>
            </div>
            <div class="ml-4 shrink-0 flex">
              <.clear_flash_button on_close={@on_close} key={@key} />
            </div>
          </div>
        </div>
      </div>
    </div>
    """
  end

  def icon_success(assigns) do
    ~H"""
    <div class="shrink-0">
      <svg
        class="h-6 w-6 text-green-400"
        xmlns="http://www.w3.org/2000/svg"
        fill="none"
        viewBox="0 0 24 24"
        stroke="currentColor"
      >
        <path
          stroke-linecap="round"
          stroke-linejoin="round"
          stroke-width="2"
          d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
        />
      </svg>
    </div>
    """
  end

  def icon_error(assigns) do
    ~H"""
    <svg
      class="w-6 h-6 text-red-400"
      fill="none"
      stroke="currentColor"
      viewBox="0 0 24 24"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="2"
        d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
      >
      </path>
    </svg>
    """
  end

  def clear_flash_button(assigns) do
    ~H"""
    <button
      class="inline-flex text-gray-400 focus:outline-hidden focus:text-gray-500 dark:focus:text-gray-200 transition ease-in-out duration-150"
      phx-click={@on_close}
      phx-value-key={@key}
    >
      <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
        <path
          fill-rule="evenodd"
          d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
          clip-rule="evenodd"
        />
      </svg>
    </button>
    """
  end
end
