defmodule MicrowavepropWeb.ContactLive.Index do
  @moduledoc false
  use MicrowavepropWeb, :live_view
  use LiveTable.LiveResource, schema: Microwaveprop.Radio.Contact

  import Ecto.Query

  alias Microwaveprop.Accounts.Scope
  alias Microwaveprop.Accounts.User
  alias Microwaveprop.Radio.Contact
  alias Microwaveprop.Repo

  def table_options do
    %{
      sorting: %{default_sort: [inserted_at: :desc]},
      exports: %{formats: [:csv]}
    }
  end

  def fields do
    [
      id: %{label: "ID", hidden: true},
      station1: %{label: "Station 1", sortable: true, searchable: true},
      grid1: %{label: "Grid 1", sortable: true, searchable: true},
      station2: %{label: "Station 2", sortable: true, searchable: true},
      grid2: %{label: "Grid 2", sortable: true, searchable: true},
      band: %{label: "Band", sortable: true, renderer: &band_cell/1},
      mode: %{label: "Mode", sortable: true, searchable: true},
      distance_km: %{label: "Distance", sortable: true, renderer: &distance_cell/1},
      hrrr_status: %{label: "Enriched", renderer: &enrichment_cell/2},
      qso_timestamp: %{label: "QSO (UTC)", sortable: true, renderer: &format_ts/1},
      inserted_at: %{label: "Added (UTC)", sortable: true, renderer: &format_ts/1},
      private: %{label: "Private", sortable: true, renderer: &private_cell/1}
    ]
  end

  def filters, do: []

  def actions do
    [
      view: fn %{record: contact} ->
        assigns = %{contact: contact}

        ~H"""
        <.link navigate={~p"/contacts/#{@contact.id}"} class="btn btn-xs btn-ghost">
          View
        </.link>
        """
      end
    ]
  end

  @impl true
  def mount(_params, _session, socket) do
    scope = socket.assigns[:current_scope]
    base_query = visible_query(scope)
    total = Repo.aggregate(base_query, :count, :id)

    {:ok,
     socket
     |> assign(:page_title, "Contacts")
     |> assign(:total_contacts, total)
     |> assign(:visible_fields, visible_fields_for(scope))
     |> assign(:data_provider, {__MODULE__, :visible_query_provider, [scope_token(scope)]})}
  end

  # Hide the Private column entirely unless this scope can actually see
  # at least one private contact — avoids an always-empty column for
  # visitors and owners who've never flagged one as private.
  defp visible_fields_for(scope) do
    if has_viewable_private?(scope) do
      fields()
    else
      Keyword.delete(fields(), :private)
    end
  end

  defp has_viewable_private?(%Scope{user: %User{is_admin: true}}) do
    Repo.exists?(from(c in Contact, where: c.private == true))
  end

  defp has_viewable_private?(%Scope{user: %User{id: user_id}}) do
    Repo.exists?(from(c in Contact, where: c.private == true and c.user_id == ^user_id))
  end

  defp has_viewable_private?(_), do: false

  @doc false
  # Called by LiveTable to get the base queryable. Must be a
  # named function because the helper persists MFA across renders.
  def visible_query_provider(scope_token) do
    scope_token
    |> scope_from_token()
    |> visible_query()
    |> from(as: :resource)
  end

  defp visible_query(%Scope{user: %User{is_admin: true}}), do: Contact

  defp visible_query(%Scope{user: %User{id: user_id}}) do
    from(c in Contact, where: c.private == false or c.user_id == ^user_id)
  end

  defp visible_query(_), do: from(c in Contact, where: c.private == false)

  # The data_provider MFA is serialized into the socket; a raw %Scope{}
  # with associations isn't safe to store there. Collapse to the minimum
  # needed to rebuild visibility and rehydrate on each invocation.
  defp scope_token(%Scope{user: %User{id: id, is_admin: is_admin}}), do: {id, is_admin}
  defp scope_token(_), do: nil

  defp scope_from_token({id, true}), do: %Scope{user: %User{id: id, is_admin: true}}
  defp scope_from_token({id, false}), do: %Scope{user: %User{id: id, is_admin: false}}
  defp scope_from_token(nil), do: nil

  defp private_cell(true) do
    assigns = %{}

    ~H"""
    <span class="badge badge-sm badge-warning">Yes</span>
    """
  end

  defp private_cell(_), do: ""

  @enrichment_fields [
    {:hrrr_status, "HRRR"},
    {:weather_status, "Weather"},
    {:terrain_status, "Terrain"},
    {:iemre_status, "IEMRE"}
  ]
  @done_statuses [:complete, :unavailable]

  defp enrichment_cell(_value, contact) do
    details =
      Enum.map(@enrichment_fields, fn {field, name} ->
        {name, Map.get(contact, field)}
      end)

    done = Enum.count(details, fn {_, s} -> s in @done_statuses end)
    failed = Enum.count(details, fn {_, s} -> s == :failed end)

    {label, class} =
      cond do
        done == 4 -> {"Complete", "badge-success"}
        failed > 0 -> {"Failed", "badge-error"}
        done > 0 -> {"Partial", "badge-warning"}
        true -> {"Pending", "badge-ghost"}
      end

    tooltip = Enum.map_join(details, ", ", fn {name, status} -> "#{name}: #{status}" end)

    assigns = %{label: label, class: class, tooltip: tooltip}

    ~H"""
    <span class={["badge badge-xs", @class]} title={@tooltip}>{@label}</span>
    """
  end

  defp band_cell(nil), do: ""
  defp band_cell(%Decimal{} = d), do: Decimal.to_string(d, :normal)
  defp band_cell(other), do: to_string(other)

  defp distance_cell(nil), do: ""
  defp distance_cell(km), do: Microwaveprop.Format.distance_km(km)

  defp format_count(n) when is_integer(n) do
    n
    |> Integer.to_string()
    |> String.reverse()
    |> String.replace(~r/(\d{3})(?=\d)/, "\\1,")
    |> String.reverse()
  end

  defp format_ts(nil), do: ""
  defp format_ts(%DateTime{} = dt), do: Calendar.strftime(dt, "%Y-%m-%d %H:%M")
  defp format_ts(%NaiveDateTime{} = dt), do: Calendar.strftime(dt, "%Y-%m-%d %H:%M")
  defp format_ts(other), do: to_string(other)

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-7xl">
      <.header>
        Contacts
        <:subtitle>
          {format_count(@total_contacts)} total — sort or search by callsign, grid, or mode.
        </:subtitle>
        <:actions>
          <.link navigate={~p"/submit"} class="btn btn-primary">
            <.icon name="hero-plus" class="w-5 h-5" /> Submit Contact
          </.link>
        </:actions>
      </.header>

      <.live_table
        fields={@visible_fields}
        filters={filters()}
        options={@options}
        streams={@streams}
        actions={actions()}
      />
    </Layouts.app>
    """
  end
end
