defmodule WhoNeedHelpWeb.RequestLive.Index do
  use WhoNeedHelpWeb, :live_view

  alias WhoNeedHelp.{Help, Pagination, Trust}

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

    {:ok,
     socket
     |> assign(:filters, %{"category_id" => "", "urgency" => ""})
     |> load()}
  end

  @impl true
  def handle_info({event, request}, socket) when event in [:request_created, :request_updated] do
    user = socket.assigns.current_scope.user

    {requests, requests_cursor} =
      update_entry(
        socket.assigns.requests,
        request,
        Help.visible_open_request?(
          socket.assigns.current_scope,
          request,
          socket.assigns.filters
        ),
        :open,
        socket.assigns.requests_cursor
      )

    {my_requests, my_requests_cursor} =
      update_entry(
        socket.assigns.my_requests,
        request,
        request.requester_id == user.id,
        :mine,
        socket.assigns.my_requests_cursor
      )

    socket =
      socket
      |> assign(:requests, requests)
      |> assign(:requests_cursor, requests_cursor)
      |> assign(:my_requests, my_requests)
      |> assign(:my_requests_cursor, my_requests_cursor)
      |> assign(:markers, Jason.encode!(Enum.flat_map(requests, &List.wrap(marker(&1)))))
      |> maybe_refresh_reputation(request, user.id)

    {:noreply, socket}
  end

  @impl true
  def handle_event("filter", %{"filters" => filters}, socket) do
    {:noreply, socket |> assign(:filters, filters) |> load()}
  end

  def handle_event("load-more-requests", _params, socket) do
    page =
      Help.paginate_open_requests(
        socket.assigns.current_scope,
        socket.assigns.filters,
        after: socket.assigns.requests_cursor
      )

    requests = append_unique(socket.assigns.requests, page.entries)

    {:noreply,
     socket
     |> assign(:requests, requests)
     |> assign(:requests_cursor, page.next_cursor)
     |> assign(:markers, Jason.encode!(Enum.flat_map(requests, &List.wrap(marker(&1)))))}
  end

  def handle_event("load-more-my-requests", _params, socket) do
    page =
      Help.paginate_my_requests(socket.assigns.current_scope,
        after: socket.assigns.my_requests_cursor
      )

    {:noreply,
     socket
     |> assign(:my_requests, append_unique(socket.assigns.my_requests, page.entries))
     |> assign(:my_requests_cursor, page.next_cursor)}
  end

  defp load(socket) do
    requests_page =
      Help.paginate_open_requests(socket.assigns.current_scope, socket.assigns.filters)

    my_requests_page = Help.paginate_my_requests(socket.assigns.current_scope)
    requests = requests_page.entries
    user = socket.assigns.current_scope.user

    socket
    |> assign(:page_title, gettext("Nearby help"))
    |> assign(:requests, requests)
    |> assign(:requests_cursor, requests_page.next_cursor)
    |> assign(:my_requests, my_requests_page.entries)
    |> assign(:my_requests_cursor, my_requests_page.next_cursor)
    |> assign(:reputation, Trust.reputation(user.id))
    |> assign(:categories, WhoNeedHelp.Catalog.list_categories())
    |> assign(:filter_form, to_form(socket.assigns.filters, as: :filters))
    |> assign(:markers, Jason.encode!(Enum.flat_map(requests, &List.wrap(marker(&1)))))
  end

  defp append_unique(existing, incoming) do
    existing_ids = MapSet.new(existing, & &1.id)
    existing ++ Enum.reject(incoming, &MapSet.member?(existing_ids, &1.id))
  end

  defp update_entry(entries, request, visible?, order, cursor) do
    window_limit = max(length(entries), Pagination.limit([]))
    entries = Enum.reject(entries, &(&1.id == request.id))

    entries =
      if visible?,
        do: [request | entries],
        else: entries

    entries =
      case order do
        :open -> Enum.sort_by(entries, &{&1.expires_at, &1.id}, :asc)
        :mine -> Enum.sort_by(entries, &{&1.inserted_at, &1.id}, :desc)
      end

    overflow? = length(entries) > window_limit
    entries = Enum.take(entries, window_limit)

    cursor =
      if overflow? do
        request = List.last(entries)
        timestamp = if order == :open, do: request.expires_at, else: request.inserted_at
        Pagination.encode(timestamp, request.id)
      else
        cursor
      end

    {entries, cursor}
  end

  defp maybe_refresh_reputation(socket, request, user_id) do
    assignment = request.assignment

    if assignment && assignment.status == :completed &&
         user_id in [request.requester_id, assignment.helper_id] do
      assign(socket, :reputation, Trust.reputation(user_id))
    else
      socket
    end
  end

  defp marker(request) do
    case Help.HelpRequest.public_coordinates(request) do
      nil ->
        nil

      coordinates ->
        Map.merge(coordinates, %{
          id: request.id,
          title: request.title,
          location: request.location_label
        })
    end
  end

  defp urgency_label(:now), do: gettext("Now")
  defp urgency_label("now"), do: gettext("Now")
  defp urgency_label(:today), do: gettext("Today")
  defp urgency_label("today"), do: gettext("Today")
  defp urgency_label(:scheduled), do: gettext("Scheduled")
  defp urgency_label("scheduled"), do: gettext("Scheduled")
  defp urgency_label(value), do: value

  defp status_label(:open), do: gettext("Open")
  defp status_label("open"), do: gettext("Open")
  defp status_label(:matched), do: gettext("Matched")
  defp status_label("matched"), do: gettext("Matched")
  defp status_label(:in_progress), do: gettext("In progress")
  defp status_label("in_progress"), do: gettext("In progress")
  defp status_label(:completed), do: gettext("Completed")
  defp status_label("completed"), do: gettext("Completed")
  defp status_label(:cancelled), do: gettext("Cancelled")
  defp status_label("cancelled"), do: gettext("Cancelled")
  defp status_label(:expired), do: gettext("Expired")
  defp status_label("expired"), do: gettext("Expired")
  defp status_label(value), do: value

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope}>
      <div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
        <div>
          <div class="text-sm font-semibold text-success">{gettext("NEARBY MUTUAL AID")}</div>
          <h1 class="mt-1 text-4xl font-black tracking-tight">{gettext("Who needs help?")}</h1>
          <p class="mt-2 text-base-content/65">
            {gettext("Only approximate areas are shown before a safe match.")}
          </p>
        </div>
        <.link navigate={~p"/requests/new"} class="btn btn-primary">
          {gettext("Create urgent request")}
        </.link>
      </div>

      <div class="alert alert-warning mt-5 text-sm">
        <.icon name="hero-exclamation-triangle" class="size-5" />
        <span>
          {gettext(
            "This is not an emergency service. Contact local emergency services for immediate danger."
          )}
        </span>
      </div>

      <.form
        for={@filter_form}
        id="request-filters"
        phx-change="filter"
        class="mt-5 grid gap-3 rounded-2xl bg-base-200 p-4 sm:grid-cols-2"
      >
        <.input
          field={@filter_form[:category_id]}
          type="select"
          label={gettext("Category")}
          options={[
            {gettext("All categories"), ""}
            | Enum.map(
                @categories,
                &{WhoNeedHelp.Catalog.category_path(
                   &1,
                   Gettext.get_locale(WhoNeedHelpWeb.Gettext)
                 ), &1.id}
              )
          ]}
        />
        <.input
          field={@filter_form[:urgency]}
          type="select"
          label={gettext("Urgency")}
          options={[
            {gettext("Any urgency"), ""},
            {gettext("Now"), "now"},
            {gettext("Today"), "today"},
            {gettext("Scheduled"), "scheduled"}
          ]}
        />
      </.form>

      <div class="mt-7 grid gap-7 lg:grid-cols-[minmax(0,1fr)_minmax(22rem,.8fr)]">
        <div class="space-y-4">
          <%= if @requests == [] do %>
            <div class="rounded-3xl border border-dashed border-base-300 p-12 text-center">
              <h2 class="text-xl font-bold">{gettext("No open requests yet")}</h2>
              <p class="mt-2 text-base-content/60">
                {gettext("You can be the first person to ask the community.")}
              </p>
            </div>
          <% end %>

          <.link
            :for={request <- @requests}
            id={"open-request-#{request.id}"}
            navigate={~p"/requests/#{request.id}"}
            class="help-card block rounded-3xl border border-base-300 bg-base-100 p-6"
          >
            <div class="flex items-start justify-between gap-4">
              <div>
                <div class="mb-3 flex flex-wrap gap-2">
                  <span class="badge badge-success badge-outline">
                    {WhoNeedHelp.Catalog.Category.name(
                      request.category,
                      Gettext.get_locale(WhoNeedHelpWeb.Gettext)
                    )}
                  </span>
                  <span class="badge badge-error badge-outline">{urgency_label(request.urgency)}</span>
                </div>
                <h2 class="text-xl font-bold">{request.title}</h2>
                <p class="mt-2 line-clamp-2 text-sm text-base-content/65">{request.description}</p>
              </div>
              <.icon name="hero-chevron-right" class="mt-2 size-5 shrink-0" />
            </div>
            <div class="mt-5 flex flex-wrap items-center gap-x-5 gap-y-2 border-t border-base-300 pt-4 text-xs text-base-content/55">
              <span>📍 {request.location_label}</span>
              <span>
                {gettext("by %{name}",
                  name: request.requester.display_name || gettext("Community member")
                )}
              </span>
              <span>
                {gettext("expires %{time}",
                  time: Calendar.strftime(request.expires_at, "%d.%m.%Y, %H:%M UTC")
                )}
              </span>
            </div>
          </.link>
          <button
            :if={@requests_cursor}
            type="button"
            phx-click="load-more-requests"
            class="btn btn-outline w-full"
          >
            {gettext("Load more")}
          </button>
        </div>

        <aside class="space-y-5">
          <div
            id="request-map"
            phx-hook="AidMap"
            phx-update="ignore"
            data-markers={@markers}
            data-map-unavailable-label={
              gettext("The map is unavailable in this browser. Request details remain usable.")
            }
            class="aid-map"
          />
          <div class="rounded-3xl bg-base-200 p-5">
            <h3 class="font-bold">{gettext("Your trust summary")}</h3>
            <div class="mt-4 grid grid-cols-3 gap-2 text-center">
              <div>
                <div class="text-2xl font-black">{@reputation.completed}</div><div class="text-[.65rem]">
                  {gettext("completed")}
                </div>
              </div>
              <div>
                <div class="text-2xl font-black">{@reputation.unique_people}</div><div class="text-[.65rem]">
                  {gettext("people")}
                </div>
              </div>
              <div>
                <div class="text-2xl font-black">{@reputation.verified_handovers}</div><div class="text-[.65rem]">
                  {gettext("verified")}
                </div>
              </div>
            </div>
          </div>
        </aside>
      </div>

      <section :if={@my_requests != []} class="mt-12">
        <h2 class="text-2xl font-bold">{gettext("Your requests")}</h2>
        <div class="mt-4 grid gap-3 md:grid-cols-2">
          <.link
            :for={request <- @my_requests}
            id={"my-request-#{request.id}"}
            navigate={~p"/requests/#{request.id}"}
            class="rounded-2xl bg-base-200 p-4"
          >
            <span class="badge badge-sm">{status_label(request.status)}</span>
            <div class="mt-2 font-semibold">{request.title}</div>
          </.link>
        </div>
        <button
          :if={@my_requests_cursor}
          type="button"
          phx-click="load-more-my-requests"
          class="btn btn-outline btn-sm mt-4"
        >
          {gettext("Load more")}
        </button>
      </section>
    </Layouts.app>
    """
  end
end
