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

  alias WhoNeedHelp.{Activities, Catalog, Pagination}
  alias WhoNeedHelp.Activities.Activity

  @impl true
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Activities.subscribe()
      Activities.subscribe_user(socket.assigns.current_scope)
    end

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

  @impl true
  def handle_info({event, activity}, socket)
      when event in [:activity_created, :activity_updated] do
    user_id = socket.assigns.current_scope.user.id
    already_mine? = Enum.any?(socket.assigns.my_activities, &(&1.id == activity.id))

    {activities, activities_cursor} =
      update_entry(
        socket.assigns.activities,
        activity,
        Activities.visible_open_activity?(
          socket.assigns.current_scope,
          activity,
          socket.assigns.filters
        ),
        :asc,
        socket.assigns.activities_cursor
      )

    {my_activities, my_activities_cursor} =
      update_entry(
        socket.assigns.my_activities,
        activity,
        already_mine? or activity.creator_id == user_id,
        :desc,
        socket.assigns.my_activities_cursor
      )

    {:noreply,
     socket
     |> assign(:activities, activities)
     |> assign(:activities_cursor, activities_cursor)
     |> assign(:my_activities, my_activities)
     |> assign(:my_activities_cursor, my_activities_cursor)
     |> assign(:markers, Jason.encode!(Enum.flat_map(activities, &List.wrap(marker(&1)))))}
  end

  def handle_info({:my_activity_updated, activity, member?}, socket) do
    {my_activities, my_activities_cursor} =
      update_entry(
        socket.assigns.my_activities,
        activity,
        member?,
        :desc,
        socket.assigns.my_activities_cursor
      )

    {:noreply,
     socket
     |> assign(:my_activities, my_activities)
     |> assign(:my_activities_cursor, my_activities_cursor)}
  end

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

  def handle_event("load-more-activities", _params, socket) do
    page =
      Activities.paginate_open_activities(
        socket.assigns.current_scope,
        socket.assigns.filters,
        after: socket.assigns.activities_cursor
      )

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

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

  def handle_event("load-more-my-activities", _params, socket) do
    page =
      Activities.paginate_my_activities(socket.assigns.current_scope,
        after: socket.assigns.my_activities_cursor
      )

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

  defp load(socket) do
    activities_page =
      Activities.paginate_open_activities(
        socket.assigns.current_scope,
        socket.assigns.filters
      )

    my_activities_page = Activities.paginate_my_activities(socket.assigns.current_scope)
    activities = activities_page.entries

    socket
    |> assign(:page_title, gettext("Activities"))
    |> assign(:activities, activities)
    |> assign(:activities_cursor, activities_page.next_cursor)
    |> assign(:my_activities, my_activities_page.entries)
    |> assign(:my_activities_cursor, my_activities_page.next_cursor)
    |> assign(:categories, Catalog.list_categories(:activity))
    |> assign(:filter_form, to_form(socket.assigns.filters, as: :filters))
    |> assign(:markers, Jason.encode!(Enum.flat_map(activities, &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, activity, visible?, direction, cursor) do
    window_limit = max(length(entries), Pagination.limit([]))
    entries = Enum.reject(entries, &(&1.id == activity.id))
    entries = if visible?, do: [activity | entries], else: entries
    entries = Enum.sort_by(entries, &{&1.starts_at, &1.id}, direction)
    overflow? = length(entries) > window_limit
    entries = Enum.take(entries, window_limit)

    cursor =
      if overflow? do
        activity = List.last(entries)
        Pagination.encode(activity.starts_at, activity.id)
      else
        cursor
      end

    {entries, cursor}
  end

  defp marker(activity) do
    case Activity.public_coordinates(activity) do
      nil ->
        nil

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

  defp status_label(:open), do: gettext("Open")
  defp status_label(:completed), do: gettext("Completed")
  defp status_label(:cancelled), do: gettext("Cancelled")
  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-info">{gettext("OPTIONAL SOCIAL PLANS")}</div>
          <h1 class="mt-1 text-4xl font-black tracking-tight">
            {gettext("Find people to join")}
          </h1>
          <p class="mt-2 max-w-2xl text-base-content/65">
            {gettext(
              "Coffee, cinema, walks, and hikes are separate from urgent help and never affect helper ratings."
            )}
          </p>
        </div>
        <.link navigate={~p"/activities/new"} class="btn btn-info">
          {gettext("Create activity")}
        </.link>
      </div>

      <div class="alert alert-info mt-5 text-sm">
        <.icon name="hero-shield-check" class="size-5" />
        <span>
          {gettext(
            "Meet in public first, keep exact details inside the approved group, and leave or report anything that feels unsafe."
          )}
        </span>
      </div>

      <.form
        for={@filter_form}
        id="activity-filters"
        phx-change="filter"
        class="mt-5 rounded-2xl bg-base-200 p-4"
      >
        <.input
          field={@filter_form[:category_id]}
          type="select"
          label={gettext("Activity type")}
          options={[
            {gettext("All activities"), ""}
            | Enum.map(
                @categories,
                &{Catalog.category_path(&1, Gettext.get_locale(WhoNeedHelpWeb.Gettext)), &1.id}
              )
          ]}
        />
      </.form>

      <div class="mt-7 grid gap-7 lg:grid-cols-[minmax(0,1fr)_minmax(22rem,.8fr)]">
        <div class="space-y-4">
          <div
            :if={@activities == []}
            class="rounded-3xl border border-dashed border-base-300 p-12 text-center"
          >
            <h2 class="text-xl font-bold">{gettext("No open activities yet")}</h2>
            <p class="mt-2 text-base-content/60">
              {gettext("Create a clear plan in a public place.")}
            </p>
          </div>

          <.link
            :for={activity <- @activities}
            id={"open-activity-#{activity.id}"}
            navigate={~p"/activities/#{activity.id}"}
            class="block rounded-3xl border border-base-300 bg-base-100 p-6 transition hover:border-info/50"
          >
            <div class="flex items-start justify-between gap-4">
              <div>
                <div class="mb-3 flex flex-wrap gap-2">
                  <span class="badge badge-info badge-outline">
                    {Catalog.category_path(
                      activity.category,
                      Gettext.get_locale(WhoNeedHelpWeb.Gettext)
                    )}
                  </span>
                  <span class="badge badge-outline">
                    {activity.approved_participant_count}/{activity.capacity}
                  </span>
                </div>
                <h2 class="text-xl font-bold">{activity.title}</h2>
                <p class="mt-2 line-clamp-2 text-sm text-base-content/65">{activity.description}</p>
              </div>
              <.icon name="hero-chevron-right" class="mt-2 size-5 shrink-0" />
            </div>
            <div class="mt-5 flex flex-wrap gap-x-5 gap-y-2 border-t border-base-300 pt-4 text-xs text-base-content/55">
              <span>📍 {activity.location_label}</span>
              <span>🕒 {Calendar.strftime(activity.starts_at, "%d.%m.%Y, %H:%M UTC")}</span>
              <span>
                {gettext("by %{name}",
                  name: activity.creator.display_name || gettext("Community member")
                )}
              </span>
            </div>
          </.link>
          <button
            :if={@activities_cursor}
            type="button"
            phx-click="load-more-activities"
            class="btn btn-outline w-full"
          >
            {gettext("Load more")}
          </button>
        </div>

        <aside>
          <div
            id="activity-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"
          />
        </aside>
      </div>

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