defmodule SpazioSolazzoWeb.Admin.BookingManagementLive do
  @moduledoc """
  Admin booking management tool for reviewing and managing all booking requests.
  Refactored to use URL as the Single Source of Truth.
  """

  use SpazioSolazzoWeb, :live_view

  import SpazioSolazzoWeb.AdminBookingManagementComponents
  alias SpazioSolazzo.BookingSystem

  @pending_limit 10
  @history_limit 10

  def mount(_params, _session, socket) do
    {:ok, spaces} = SpazioSolazzo.BookingSystem.list_spaces()

    if connected?(socket) do
      Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:created")
      Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:approved")
      Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:cancelled")
      Phoenix.PubSub.subscribe(SpazioSolazzo.PubSub, "booking:rejected")
    end

    {:ok,
     assign(socket,
       spaces: spaces,
       expanded_booking_ids: MapSet.new(),
       show_reject_modal: false,
       rejecting_booking_id: nil,
       rejection_reason: ""
     )}
  end

  def handle_params(params, _uri, socket) do
    # Destructure params with defaults. This ensures all keys exist.
    %{
      "space" => space_slug,
      "email" => email,
      "date" => date_str,
      "pending_page" => pending_str,
      "history_page" => history_str
    } =
      Map.merge(
        %{
          "space" => nil,
          "email" => "",
          "date" => nil,
          "pending_page" => "1",
          "history_page" => "1"
        },
        params
      )

    socket =
      socket
      |> assign(
        filter_space: parse_string(space_slug),
        filter_email: email,
        filter_date: parse_date(date_str),
        pending_page_number: parse_page(pending_str),
        history_page_number: parse_page(history_str)
      )
      |> fetch_bookings()

    {:noreply, socket}
  end

  def handle_event(
        "filter_bookings",
        %{"date" => date, "space" => space, "email" => email},
        socket
      ) do
    params = %{
      "date" => date,
      "space" => space,
      "email" => email,
      # Reset pagination to page 1 when filtering
      "pending_page" => "1",
      "history_page" => "1"
    }

    {:noreply, push_patch(socket, to: build_filter_path(socket, params), replace: true)}
  end

  def handle_event("clear_filters", _, socket) do
    {:noreply, push_patch(socket, to: ~p"/admin/bookings", replace: true)}
  end

  def handle_event("pending_page_change", %{"page" => page}, socket) do
    {:noreply, push_patch(socket, to: build_filter_path(socket, %{"pending_page" => page}))}
  end

  def handle_event("history_page_change", %{"page" => page}, socket) do
    {:noreply, push_patch(socket, to: build_filter_path(socket, %{"history_page" => page}))}
  end

  def handle_event("toggle_expand", %{"booking_id" => booking_id}, socket) do
    expanded =
      if MapSet.member?(socket.assigns.expanded_booking_ids, booking_id) do
        MapSet.delete(socket.assigns.expanded_booking_ids, booking_id)
      else
        MapSet.put(socket.assigns.expanded_booking_ids, booking_id)
      end

    {:noreply, assign(socket, expanded_booking_ids: expanded)}
  end

  def handle_event("approve_booking", %{"booking_id" => booking_id}, socket) do
    case Ash.get(BookingSystem.Booking, booking_id) do
      {:ok, booking} ->
        BookingSystem.approve_booking(booking)
        {:noreply, socket}

      {:error, _} ->
        {:noreply, put_flash(socket, :error, "Booking not found")}
    end
  end

  def handle_event("show_reject_modal", %{"booking_id" => booking_id}, socket) do
    {:noreply,
     assign(socket,
       show_reject_modal: true,
       rejecting_booking_id: booking_id,
       rejection_reason: ""
     )}
  end

  def handle_event("hide_reject_modal", _, socket) do
    {:noreply, assign(socket, show_reject_modal: false, rejecting_booking_id: nil)}
  end

  def handle_event("stop_propagation", _, socket), do: {:noreply, socket}

  def handle_event("update_rejection_reason", %{"reason" => reason}, socket) do
    {:noreply, assign(socket, rejection_reason: reason)}
  end

  def handle_event("confirm_reject", _, socket) do
    %{rejecting_booking_id: id, rejection_reason: reason} = socket.assigns

    if String.trim(reason) == "" do
      {:noreply, put_flash(socket, :error, "Please provide a rejection reason")}
    else
      case Ash.get(BookingSystem.Booking, id) do
        {:ok, booking} ->
          BookingSystem.reject_booking(booking, reason)

          socket =
            socket
            |> assign(show_reject_modal: false, rejecting_booking_id: nil)
            |> put_flash(:info, "Booking rejected")

          {:noreply, socket}

        {:error, _} ->
          {:noreply, put_flash(socket, :error, "Booking not found")}
      end
    end
  end

  def handle_info(%{topic: "booking:" <> _}, socket) do
    {:noreply, fetch_bookings(socket)}
  end

  def handle_info(_msg, socket), do: {:noreply, socket}

  defp fetch_bookings(socket) do
    %{
      filter_space: space_slug,
      filter_email: email,
      filter_date: date,
      pending_page_number: pending_page,
      history_page_number: history_page,
      spaces: spaces
    } = socket.assigns

    space_id =
      spaces
      |> Enum.find(%{}, fn s -> s.slug == space_slug end)
      |> Map.get(:id, nil)

    query_email = parse_string(email)
    pending_offset = (pending_page - 1) * @pending_limit
    history_offset = (history_page - 1) * @history_limit

    {:ok, pending_data} =
      BookingSystem.read_pending_bookings(space_id, query_email, date,
        page: [limit: @pending_limit, offset: pending_offset, count: true],
        load: [:space, :user]
      )

    {:ok, history_data} =
      BookingSystem.read_booking_history(space_id, query_email, date,
        page: [limit: @history_limit, offset: history_offset, count: true],
        load: [:space, :user]
      )

    assign(socket, pending_page: pending_data, history_page: history_data)
  end

  defp build_filter_path(socket, overrides) do
    current = %{
      "space" => socket.assigns.filter_space,
      "email" => socket.assigns.filter_email,
      "date" => socket.assigns.filter_date,
      "pending_page" => socket.assigns.pending_page_number,
      "history_page" => socket.assigns.history_page_number
    }

    # Merge overrides -> Filter empty values -> Encode
    params =
      current
      |> Map.merge(overrides)
      |> Enum.reduce(%{}, fn
        {_k, nil}, acc -> acc
        {_k, ""}, acc -> acc
        {"date", %Date{} = d}, acc -> Map.put(acc, "date", Date.to_iso8601(d))
        {k, v}, acc -> Map.put(acc, k, v)
      end)

    ~p"/admin/bookings"
    |> URI.parse()
    |> URI.append_query(URI.encode_query(params))
    |> URI.to_string()
  end

  defp parse_page(nil), do: 1
  defp parse_page(""), do: 1

  defp parse_page(value) do
    case Integer.parse(value) do
      {n, _} when n > 0 -> n
      _ -> 1
    end
  end

  defp parse_date(nil), do: nil
  defp parse_date(""), do: nil

  defp parse_date(value) do
    case Date.from_iso8601(value) do
      {:ok, date} -> date
      _ -> nil
    end
  end

  defp parse_string(nil), do: nil
  defp parse_string(""), do: nil
  defp parse_string(value), do: value
end
