defmodule BerrypodWeb.Admin.Reviews do
  @moduledoc """
  Admin interface for moderating product reviews.
  """

  use BerrypodWeb, :live_view

  import BerrypodWeb.ShopComponents.Content, only: [image_lightbox: 1]

  alias Berrypod.Reviews

  @valid_statuses ~w(pending approved rejected)

  @impl true
  def mount(_params, _session, socket) do
    socket =
      socket
      |> assign(:page_title, "Reviews")
      |> assign(:status_counts, Reviews.count_reviews_by_status())
      |> assign(:expanded_id, nil)

    {:ok, socket}
  end

  @impl true
  def handle_params(params, _uri, socket) do
    status = if params["status"] in @valid_statuses, do: params["status"], else: nil
    search = params["search"]
    page_num = Berrypod.Pagination.parse_page(params)

    page =
      Reviews.list_reviews_paginated(
        status: status,
        search: search,
        page: page_num
      )

    # Batch preload all images for the page in one query
    images_by_review = Reviews.preload_review_images(page.items)

    socket =
      socket
      |> assign(:status, status)
      |> assign(:search, search || "")
      |> assign(:pagination, page)
      |> assign(:reviews, page.items)
      |> assign(:images_by_review, images_by_review)

    {:noreply, socket}
  end

  @impl true
  def handle_event("filter", %{"status" => status}, socket) do
    status = if status == "", do: nil, else: status
    params = build_params(status: status, search: socket.assigns.search)
    {:noreply, push_patch(socket, to: ~p"/admin/reviews?#{params}")}
  end

  def handle_event("search", %{"search" => %{"query" => query}}, socket) do
    search = if query == "", do: nil, else: query
    params = build_params(status: socket.assigns.status, search: search)
    {:noreply, push_patch(socket, to: ~p"/admin/reviews?#{params}")}
  end

  def handle_event("expand", %{"id" => id}, socket) do
    # Toggle: click again to collapse
    new_id = if socket.assigns.expanded_id == id, do: nil, else: id
    {:noreply, assign(socket, :expanded_id, new_id)}
  end

  def handle_event("approve", %{"id" => id}, socket) do
    review = Reviews.get_review!(id)

    case Reviews.approve_review(review) do
      {:ok, updated} ->
        {:noreply,
         socket
         |> update_review_in_list(updated)
         |> update_counts()
         |> put_flash(:info, "Review approved")}

      {:error, _changeset} ->
        {:noreply, put_flash(socket, :error, "Failed to approve review")}
    end
  end

  def handle_event("reject", %{"id" => id}, socket) do
    review = Reviews.get_review!(id)

    case Reviews.reject_review(review) do
      {:ok, updated} ->
        {:noreply,
         socket
         |> update_review_in_list(updated)
         |> update_counts()
         |> put_flash(:info, "Review rejected")}

      {:error, _changeset} ->
        {:noreply, put_flash(socket, :error, "Failed to reject review")}
    end
  end

  def handle_event("delete", %{"id" => id}, socket) do
    review = Reviews.get_review!(id)

    case Reviews.delete_review(review) do
      {:ok, _} ->
        reviews = Enum.reject(socket.assigns.reviews, &(&1.id == id))
        images_by_review = Map.delete(socket.assigns.images_by_review, id)

        {:noreply,
         socket
         |> assign(:reviews, reviews)
         |> assign(:images_by_review, images_by_review)
         |> assign(:expanded_id, nil)
         |> update_counts()
         |> put_flash(:info, "Review deleted")}

      {:error, _changeset} ->
        {:noreply, put_flash(socket, :error, "Failed to delete review")}
    end
  end

  @impl true
  def render(assigns) do
    ~H"""
    <.header>
      Reviews
    </.header>

    <div class="admin-filter-row">
      <.status_tab status={nil} label="All" count={total_count(@status_counts)} active={@status} />
      <.status_tab
        status="pending"
        label="Pending"
        count={@status_counts["pending"]}
        active={@status}
      />
      <.status_tab
        status="approved"
        label="Approved"
        count={@status_counts["approved"]}
        active={@status}
      />
      <.status_tab
        status="rejected"
        label="Rejected"
        count={@status_counts["rejected"]}
        active={@status}
      />
    </div>

    <div class="admin-filter-row admin-filter-row-end">
      <.form for={%{}} phx-submit="search" as={:search} class="admin-row">
        <input
          type="text"
          name="search[query]"
          value={@search}
          placeholder="Search reviews"
          class="admin-input admin-input-sm"
        />
        <button type="submit" class="admin-btn admin-btn-sm admin-btn-ghost">
          <.icon name="hero-magnifying-glass-mini" class="size-4" />
        </button>
      </.form>
    </div>

    <div id="reviews-list" class="admin-review-list">
      <div :if={@reviews == []} class="admin-stream-empty">
        No reviews to show.
      </div>
      <.review_row
        :for={review <- @reviews}
        id={"review-#{review.id}"}
        review={review}
        images={@images_by_review[review.id] || []}
        expanded={@expanded_id == review.id}
      />
    </div>

    <.admin_pagination
      page={@pagination}
      patch={~p"/admin/reviews"}
      params={build_params(status: @status, search: @search)}
    />
    """
  end

  # ── Components ──

  defp status_tab(assigns) do
    active = assigns.status == assigns.active
    count = assigns.count || 0
    show_badge = assigns.status == "pending" and count > 0

    assigns =
      assigns
      |> assign(:is_active, active)
      |> assign(:show_badge, show_badge)
      |> assign(:count, count)

    ~H"""
    <button
      phx-click="filter"
      phx-value-status={@status || ""}
      class={[
        "admin-btn admin-btn-sm",
        @is_active && "admin-btn-primary",
        !@is_active && "admin-btn-ghost"
      ]}
    >
      {@label}
      <span
        :if={@show_badge}
        class="admin-badge admin-badge-sm admin-badge-warning admin-badge-count"
      >
        {@count}
      </span>
    </button>
    """
  end

  defp review_row(assigns) do
    ~H"""
    <article id={@id} class={["admin-review-row", @expanded && "admin-review-row-expanded"]}>
      <button type="button" class="admin-review-header" phx-click="expand" phx-value-id={@review.id}>
        <div class="admin-review-meta">
          <.status_badge status={@review.status} />
          <span class="admin-review-product">{@review.product.title}</span>
          <span class="admin-review-rating">
            <.icon name="hero-star-solid" class="size-4 admin-star" />
            {@review.rating}
          </span>
        </div>
        <div class="admin-review-info">
          <span class="admin-review-author">{@review.author_name}</span>
          <span class="admin-review-email">{@review.email}</span>
          <time class="admin-review-date" datetime={DateTime.to_iso8601(@review.inserted_at)}>
            {relative_time(@review.inserted_at)}
          </time>
        </div>
        <.icon
          name={if @expanded, do: "hero-chevron-up-mini", else: "hero-chevron-down-mini"}
          class="size-5 admin-review-chevron"
        />
      </button>

      <div :if={@expanded} class="admin-review-detail">
        <div class="admin-review-content">
          <h4 :if={@review.title} class="admin-review-title">{@review.title}</h4>
          <p :if={@review.body} class="admin-review-body">{@review.body}</p>
          <p :if={!@review.title && !@review.body} class="admin-review-empty">
            No written review — rating only.
          </p>

          <div :if={@images != []} class="admin-review-photos">
            <button
              :for={{image, idx} <- Enum.with_index(@images)}
              type="button"
              class="admin-review-photo"
              phx-click={
                Phoenix.LiveView.JS.exec("data-show", to: "#admin-review-#{@review.id}-lightbox")
                |> Phoenix.LiveView.JS.set_attribute(
                  {"data-current-index", to_string(idx)},
                  to: "#admin-review-#{@review.id}-lightbox"
                )
              }
            >
              <img src={thumb_url(image)} alt="Customer photo" loading="lazy" />
            </button>

            <.image_lightbox
              id={"admin-review-#{@review.id}-lightbox"}
              images={Enum.map(@images, &image_url/1)}
              caption="Customer photo"
            />
          </div>
        </div>

        <div class="admin-review-actions">
          <.link
            navigate={~p"/p/#{@review.product.slug}"}
            class="admin-btn admin-btn-sm admin-btn-ghost"
          >
            View product
          </.link>
          <button
            :if={@review.status != "approved"}
            phx-click="approve"
            phx-value-id={@review.id}
            class="admin-btn admin-btn-sm admin-btn-primary"
          >
            <.icon name="hero-check-mini" class="size-4" /> Approve
          </button>
          <button
            :if={@review.status != "rejected"}
            phx-click="reject"
            phx-value-id={@review.id}
            class="admin-btn admin-btn-sm admin-btn-ghost"
          >
            <.icon name="hero-x-mark-mini" class="size-4" /> Reject
          </button>
          <button
            phx-click="delete"
            phx-value-id={@review.id}
            data-confirm="Delete this review permanently?"
            class="admin-btn admin-btn-sm admin-btn-danger"
          >
            <.icon name="hero-trash-mini" class="size-4" /> Delete
          </button>
        </div>
      </div>
    </article>
    """
  end

  defp status_badge(assigns) do
    {class, icon} =
      case assigns.status do
        "pending" -> {"admin-status-pending", "hero-clock-mini"}
        "approved" -> {"admin-status-approved", "hero-check-circle-mini"}
        "rejected" -> {"admin-status-rejected", "hero-x-circle-mini"}
        _ -> {"", "hero-question-mark-circle-mini"}
      end

    assigns = assign(assigns, class: class, icon: icon)

    ~H"""
    <span class={["admin-status-badge", @class]}>
      <.icon name={@icon} class="size-3.5" />
      {@status}
    </span>
    """
  end

  # ── Helpers ──

  defp total_count(counts) do
    (counts["pending"] || 0) + (counts["approved"] || 0) + (counts["rejected"] || 0)
  end

  defp relative_time(datetime) do
    now = DateTime.utc_now()
    diff = DateTime.diff(now, datetime, :second)

    cond do
      diff < 60 -> "just now"
      diff < 3600 -> "#{div(diff, 60)}m ago"
      diff < 86400 -> "#{div(diff, 3600)}h ago"
      diff < 604_800 -> "#{div(diff, 86400)}d ago"
      true -> Calendar.strftime(datetime, "%d %b %Y")
    end
  end

  defp build_params(opts) do
    %{}
    |> then(fn p -> if opts[:status], do: Map.put(p, "status", opts[:status]), else: p end)
    |> then(fn p -> if opts[:search], do: Map.put(p, "search", opts[:search]), else: p end)
  end

  defp update_counts(socket) do
    assign(socket, :status_counts, Reviews.count_reviews_by_status())
  end

  defp update_review_in_list(socket, updated) do
    reviews =
      Enum.map(socket.assigns.reviews, fn review ->
        if review.id == updated.id, do: %{updated | product: review.product}, else: review
      end)

    assign(socket, :reviews, reviews)
  end

  defp thumb_url(image) do
    if image.variants_status == "complete" do
      "/image_cache/#{image.id}-thumb.jpg"
    else
      "/images/#{image.id}"
    end
  end

  defp image_url(image) do
    if image.variants_status == "complete" do
      "/image_cache/#{image.id}-800.webp"
    else
      "/images/#{image.id}"
    end
  end
end
