defmodule MicrowavepropWeb.BeaconLive.Index do
  @moduledoc false
  use MicrowavepropWeb, :live_view
  use LiveTable.LiveResource, schema: Microwaveprop.Beacons.Beacon

  alias Microwaveprop.Beacons
  alias Microwaveprop.Beacons.Beacon

  def table_options do
    %{exports: %{formats: [:csv]}}
  end

  def fields do
    [
      id: %{label: "ID", hidden: true},
      frequency_mhz: %{
        label: "Frequency (MHz)",
        sortable: true,
        searchable: false,
        renderer: &freq_cell/1
      },
      callsign: %{label: "Call", sortable: true, searchable: true},
      grid: %{label: "Grid", sortable: true, searchable: true},
      power_mw: %{label: "EIRP (mW)", sortable: true, renderer: &mw_cell/1},
      height_ft: %{label: "Height AGL (ft)", sortable: true},
      keying: %{label: "Keying", sortable: true, renderer: &keying_cell/1},
      on_the_air: %{label: "On air", sortable: true, renderer: &on_air_cell/1}
    ]
  end

  def filters, do: []

  def actions do
    [
      show: fn %{record: beacon} ->
        assigns = %{beacon: beacon}

        ~H"""
        <.link navigate={~p"/beacons/#{@beacon.id}"} class="btn btn-xs btn-ghost">Details</.link>
        """
      end
    ]
  end

  @impl true
  def mount(_params, _session, socket) do
    if connected?(socket), do: Beacons.subscribe_beacons()

    beacons = Beacons.list_beacons()

    pending =
      if admin?(socket.assigns.current_scope) do
        Beacons.list_pending_beacons()
      else
        []
      end

    {:ok,
     socket
     |> assign(:page_title, "Beacons")
     |> assign(:pending, pending)
     |> assign(:beacons_json, encode_beacons(beacons))
     |> assign(:data_provider, {Beacons, :approved_beacons_query, []})
     |> stream(:pending, pending)}
  end

  @impl true
  def handle_event("delete", %{"id" => id}, socket) do
    if admin?(socket.assigns.current_scope) do
      beacon = Beacons.get_beacon!(id)
      {:ok, _} = Beacons.delete_beacon(beacon)

      {:noreply,
       socket
       |> stream_delete(:pending, beacon)
       |> push_patch(to: path_with_prefix(socket.assigns.current_path))}
    else
      {:noreply, put_flash(socket, :error, "Admins only.")}
    end
  end

  def handle_event("approve", %{"id" => id}, socket) do
    if admin?(socket.assigns.current_scope) do
      beacon = Beacons.get_beacon!(id)
      {:ok, approved} = Beacons.approve_beacon(beacon)

      {:noreply,
       socket
       |> put_flash(:info, "Approved #{approved.callsign}.")
       |> stream_delete(:pending, beacon)
       |> push_patch(to: path_with_prefix(socket.assigns.current_path))}
    else
      {:noreply, put_flash(socket, :error, "Admins only.")}
    end
  end

  @impl true
  def handle_info({type, %Beacon{}}, socket) when type in [:created, :updated, :deleted] do
    beacons = Beacons.list_beacons()

    pending =
      if admin?(socket.assigns.current_scope) do
        Beacons.list_pending_beacons()
      else
        []
      end

    {:noreply,
     socket
     |> assign(:pending, pending)
     |> assign(:beacons_json, encode_beacons(beacons))
     |> stream(:pending, pending, reset: true)
     |> push_patch(to: path_with_prefix(socket.assigns.current_path))}
  end

  defp admin?(%{user: %{is_admin: true}}), do: true
  defp admin?(_), do: false

  defp path_with_prefix(path) when is_binary(path), do: "/" <> path
  defp path_with_prefix(_), do: "/beacons"

  defp encode_beacons(beacons) do
    beacons
    |> Enum.filter(&(is_number(&1.lat) and is_number(&1.lon)))
    |> Enum.map(fn b ->
      %{
        id: b.id,
        callsign: b.callsign,
        lat: b.lat,
        lon: b.lon,
        frequency_mhz: b.frequency_mhz,
        grid: b.grid,
        keying: Beacon.keying_label(b.keying),
        on_the_air: b.on_the_air
      }
    end)
    |> Jason.encode!()
  end

  defp freq_cell(freq), do: Beacon.format_freq(freq)
  defp mw_cell(power), do: Beacon.format_mw(power)
  defp keying_cell(keying), do: Beacon.keying_label(keying)

  defp on_air_cell(on_the_air) do
    assigns = %{on_the_air: on_the_air}

    ~H"""
    <span class={[
      "badge badge-sm",
      (@on_the_air && "badge-success") || "badge-ghost"
    ]}>
      {if @on_the_air, do: "Yes", else: "No"}
    </span>
    """
  end

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-6xl">
      <.header>
        Beacons
        <:subtitle>Microwave beacons tracked by NTMS.</:subtitle>
        <:actions>
          <.button variant="primary" navigate={~p"/beacons/new"}>
            <.icon name="hero-plus" /> Submit Beacon
          </.button>
        </:actions>
      </.header>

      <div
        id="beacons-map"
        phx-hook="BeaconsListMap"
        phx-update="ignore"
        data-beacons={@beacons_json}
        class="h-80 w-full rounded-lg border border-base-300 mb-6 z-0"
      >
      </div>

      <.live_table
        fields={fields()}
        filters={filters()}
        options={@options}
        streams={@streams}
        actions={actions()}
      />

      <div :if={admin?(@current_scope) and @pending != []} class="mt-10">
        <.header>
          Pending approval
          <:subtitle>Submitted beacons waiting for admin review.</:subtitle>
        </.header>

        <.table
          id="pending-beacons"
          rows={@streams.pending}
          row_click={fn {_id, beacon} -> JS.navigate(~p"/beacons/#{beacon}") end}
        >
          <:col :let={{_id, beacon}} label="Frequency (MHz)">
            {Beacon.format_freq(beacon.frequency_mhz)}
          </:col>
          <:col :let={{_id, beacon}} label="Call">{beacon.callsign}</:col>
          <:col :let={{_id, beacon}} label="Grid">{beacon.grid}</:col>
          <:col :let={{_id, beacon}} label="EIRP (mW)">{Beacon.format_mw(beacon.power_mw)}</:col>
          <:col :let={{_id, beacon}} label="Height AGL (ft)">{beacon.height_ft}</:col>
          <:col :let={{_id, beacon}} label="Submitted">
            {Calendar.strftime(beacon.inserted_at, "%Y-%m-%d %H:%M UTC")}
          </:col>
          <:action :let={{_id, beacon}}>
            <.link
              phx-click={JS.push("approve", value: %{id: beacon.id})}
              data-confirm="Approve this beacon?"
            >
              Approve
            </.link>
          </:action>
          <:action :let={{id, beacon}}>
            <.link
              phx-click={JS.push("delete", value: %{id: beacon.id}) |> hide("##{id}")}
              data-confirm="Delete this beacon?"
            >
              Delete
            </.link>
          </:action>
        </.table>
      </div>
    </Layouts.app>
    """
  end
end
