defmodule WhoNeedHelpWeb.ModerationLive do
  use WhoNeedHelpWeb, :live_view

  alias WhoNeedHelp.{Accounts, Catalog, Trust}

  @impl true
  def mount(_params, _session, socket) do
    {:ok,
     socket |> assign(:page_title, gettext("Moderation")) |> assign(:evidence, nil) |> load()}
  end

  @impl true
  def handle_event("moderate-report", %{"id" => id, "moderation" => params}, socket) do
    respond(
      socket,
      Trust.moderate_report(socket.assigns.current_scope, id, params),
      gettext("Report updated.")
    )
  end

  def handle_event("view-evidence", %{"id" => id}, socket) do
    case Trust.report_evidence(socket.assigns.current_scope, id) do
      {:ok, evidence} -> {:noreply, assign(socket, :evidence, evidence)}
      {:error, reason} -> {:noreply, put_flash(socket, :error, error_message(reason))}
    end
  end

  def handle_event("close-evidence", _, socket), do: {:noreply, assign(socket, :evidence, nil)}

  def handle_event("moderate-signal", %{"id" => id, "moderation" => params}, socket) do
    respond(
      socket,
      Trust.moderate_signal(socket.assigns.current_scope, id, params),
      gettext("Signal updated.")
    )
  end

  def handle_event("moderate-user", %{"id" => id, "moderation" => params}, socket) do
    respond(
      socket,
      Trust.moderate_user(socket.assigns.current_scope, id, params),
      gettext("User status updated.")
    )
  end

  def handle_event("moderate-role", %{"id" => id, "moderation" => params}, socket) do
    respond(
      socket,
      Trust.moderate_role(socket.assigns.current_scope, id, params),
      gettext("User role updated.")
    )
  end

  def handle_event("hide-request", %{"id" => id, "moderation" => %{"note" => note}}, socket) do
    respond(
      socket,
      Trust.hide_request(socket.assigns.current_scope, id, note),
      gettext("Request hidden.")
    )
  end

  def handle_event("restore-request", %{"id" => id}, socket) do
    respond(
      socket,
      Trust.restore_request(socket.assigns.current_scope, id),
      gettext("Request restored.")
    )
  end

  def handle_event("hide-activity", %{"id" => id, "moderation" => %{"note" => note}}, socket) do
    respond(
      socket,
      Trust.hide_activity(socket.assigns.current_scope, id, note),
      gettext("Activity hidden.")
    )
  end

  def handle_event("restore-activity", %{"id" => id}, socket) do
    respond(
      socket,
      Trust.restore_activity(socket.assigns.current_scope, id),
      gettext("Activity restored.")
    )
  end

  def handle_event("approve-proposal", %{"id" => id, "category" => params}, socket) do
    names =
      %{
        "en" => params["name_en"],
        "uk" => params["name_uk"],
        "ru" => params["name_ru"]
      }
      |> Map.reject(fn {_locale, name} -> name in [nil, ""] end)

    descriptions =
      %{
        "en" => params["description_en"],
        "uk" => params["description_uk"],
        "ru" => params["description_ru"]
      }
      |> Map.reject(fn {_locale, description} -> description in [nil, ""] end)

    with {:ok, structured_fields} <- decode_structured_fields(params["structured_fields_json"]),
         true <- is_map(structured_fields) do
      attrs = %{
        "slug" => params["slug"],
        "names" => names,
        "descriptions" => descriptions,
        "structured_fields" => structured_fields,
        "moderation_note" => params["moderation_note"]
      }

      respond(
        socket,
        Catalog.approve_proposal(socket.assigns.current_scope, id, attrs),
        gettext("Category created and proposal approved.")
      )
    else
      _ ->
        {:noreply,
         put_flash(
           socket,
           :error,
           gettext("Structured fields must be a valid JSON object.")
         )}
    end
  end

  def handle_event("reject-proposal", %{"id" => id, "moderation" => %{"note" => note}}, socket) do
    respond(
      socket,
      Catalog.reject_proposal(socket.assigns.current_scope, id, note),
      gettext("Proposal rejected.")
    )
  end

  def handle_event(
        "merge-proposal",
        %{"id" => id, "moderation" => %{"category_id" => category_id, "note" => note}},
        socket
      ) do
    respond(
      socket,
      Catalog.merge_proposal(socket.assigns.current_scope, id, category_id, note),
      gettext("Proposal merged.")
    )
  end

  def handle_event("load-more-reports", _params, socket) do
    page =
      Trust.paginate_reports(socket.assigns.current_scope, nil,
        after: socket.assigns.reports_cursor
      )

    {:noreply, append_page(socket, :reports, page)}
  end

  def handle_event("load-more-signals", _params, socket) do
    page =
      Trust.paginate_abuse_signals(socket.assigns.current_scope, :open,
        after: socket.assigns.signals_cursor
      )

    {:noreply, append_page(socket, :signals, page)}
  end

  def handle_event("load-more-proposals", _params, socket) do
    page =
      Catalog.paginate_proposals_for_moderation(socket.assigns.current_scope,
        after: socket.assigns.proposals_cursor
      )

    {:noreply, append_page(socket, :proposals, page)}
  end

  def handle_event("load-more-users", _params, socket) do
    page =
      Accounts.paginate_users_for_moderation(socket.assigns.current_scope,
        after: socket.assigns.users_cursor
      )

    {:noreply, append_page(socket, :users, page)}
  end

  defp respond(socket, {:ok, _value}, message) do
    {:noreply, socket |> put_flash(:info, message) |> load()}
  end

  defp respond(socket, {:error, reason}, _message) do
    {:noreply, put_flash(socket, :error, error_message(reason))}
  end

  defp decode_structured_fields(value) when value in [nil, ""], do: {:ok, %{}}
  defp decode_structured_fields(value) when is_binary(value), do: Jason.decode(value)
  defp decode_structured_fields(_value), do: {:error, :invalid_type}

  defp load(socket) do
    reports = Trust.paginate_reports(socket.assigns.current_scope)
    signals = Trust.paginate_abuse_signals(socket.assigns.current_scope)
    proposals = Catalog.paginate_proposals_for_moderation(socket.assigns.current_scope)
    users = Accounts.paginate_users_for_moderation(socket.assigns.current_scope)

    socket
    |> assign(:reports, reports.entries)
    |> assign(:reports_cursor, reports.next_cursor)
    |> assign(:signals, signals.entries)
    |> assign(:signals_cursor, signals.next_cursor)
    |> assign(:proposals, proposals.entries)
    |> assign(:proposals_cursor, proposals.next_cursor)
    |> assign(:users, users.entries)
    |> assign(:users_cursor, users.next_cursor)
    |> assign(:categories, Catalog.list_all_categories())
  end

  defp append_page(socket, name, page) do
    existing = socket.assigns[name]
    existing_ids = MapSet.new(existing, & &1.id)
    entries = existing ++ Enum.reject(page.entries, &MapSet.member?(existing_ids, &1.id))

    socket
    |> assign(name, entries)
    |> assign(cursor_assign(name), page.next_cursor)
  end

  defp cursor_assign(:reports), do: :reports_cursor
  defp cursor_assign(:signals), do: :signals_cursor
  defp cursor_assign(:proposals), do: :proposals_cursor
  defp cursor_assign(:users), do: :users_cursor

  defp error_message(:forbidden), do: gettext("Moderator access is required.")
  defp error_message(:proposal_closed), do: gettext("This proposal has already been reviewed.")

  defp error_message(:cannot_restrict_self),
    do: gettext("You cannot restrict your own moderator account.")

  defp error_message(:last_admin),
    do: gettext("The last administrator cannot demote themselves.")

  defp error_message(:not_found), do: gettext("The selected record is no longer available.")
  defp error_message(%Ecto.Changeset{}), do: gettext("Please check the submitted fields.")

  defp error_message(_reason), do: gettext("Could not complete moderation. Please try again.")

  defp scoped_form(data, as, scope) do
    to_form(data, as: as, id: "#{as}-#{scope}")
  end

  defp reason_label(:dangerous_request), do: gettext("Dangerous request")
  defp reason_label(:harassment), do: gettext("Harassment")
  defp reason_label(:fraud), do: gettext("Fraud")
  defp reason_label(:prohibited_item), do: gettext("Prohibited item")
  defp reason_label(:spam), do: gettext("Spam")
  defp reason_label(:impersonation), do: gettext("Impersonation")
  defp reason_label(:other), do: gettext("Other")
  defp reason_label(value), do: to_string(value)

  defp status_label(:open), do: gettext("Open")
  defp status_label(:reviewing), do: gettext("Reviewing")
  defp status_label(:resolved), do: gettext("Resolved")
  defp status_label(:dismissed), do: gettext("Dismissed")
  defp status_label(:reviewed), do: gettext("Reviewed")
  defp status_label(:approved), do: gettext("Approved")
  defp status_label(:rejected), do: gettext("Rejected")
  defp status_label(:merged), do: gettext("Merged")
  defp status_label(:active), do: gettext("Active")
  defp status_label(:restricted), do: gettext("Restricted")
  defp status_label(:suspended), do: gettext("Suspended")
  defp status_label(value), do: to_string(value)

  defp role_label(:user), do: gettext("User")
  defp role_label(:moderator), do: gettext("Moderator")
  defp role_label(:admin), do: gettext("Administrator")
  defp role_label(value), do: to_string(value)

  defp signal_label(:velocity), do: gettext("Unusual action rate")
  defp signal_label(:repeated_pair), do: gettext("Repeated participant pair")

  defp signal_label(:handover_without_location_evidence),
    do: gettext("Handover without location evidence")

  defp signal_label(:location_without_movement), do: gettext("Location without movement")
  defp signal_label(:reciprocal_ring), do: gettext("Reciprocal rating ring")
  defp signal_label(:manual), do: gettext("Manual review")
  defp signal_label(value), do: to_string(value)

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope}>
      <div class="text-sm font-semibold text-warning">{gettext("RESTRICTED WORKSPACE")}</div>
      <h1 class="mt-1 text-4xl font-black">{gettext("Moderation")}</h1>
      <p class="mt-2 text-base-content/60">
        {gettext("Decisions and access to reported chat evidence are written to the audit log.")}
      </p>

      <section class="mt-8">
        <h2 class="text-2xl font-bold">{gettext("Reports")}</h2>
        <div class="mt-4 space-y-4">
          <article :for={report <- @reports} class="rounded-2xl border border-base-300 p-5">
            <% hide_form = scoped_form(%{"note" => ""}, :moderation, "report-hide-#{report.id}") %>
            <% report_form =
              scoped_form(
                %{"status" => to_string(report.status), "resolution_note" => ""},
                :moderation,
                "report-review-#{report.id}"
              ) %>
            <div class="flex flex-wrap items-center gap-2">
              <span class="badge badge-error">{reason_label(report.reason)}</span>
              <span class="badge">{status_label(report.status)}</span>
              <span :if={report.activity_id} class="badge badge-info badge-outline">
                {gettext("activity")}
              </span>
              <span :if={report.activity_message_id} class="badge badge-info badge-outline">
                {gettext("activity message")}
              </span>
              <span class="text-xs text-base-content/50">
                {gettext("by %{name}", name: report.reporter.display_name)}
              </span>
            </div>
            <p class="mt-3 whitespace-pre-wrap text-sm">{report.details}</p>
            <div class="mt-3 flex flex-wrap gap-2">
              <button
                phx-click="view-evidence"
                phx-value-id={report.id}
                class="btn btn-sm btn-outline"
              >
                {gettext("View scoped evidence")}
              </button>
              <.form
                :if={report.request_id}
                for={hide_form}
                phx-submit="hide-request"
                phx-value-id={report.request_id}
                class="flex gap-2"
              >
                <.input field={hide_form[:note]} placeholder={gettext("Hide reason")} />
                <.button class="btn btn-sm btn-error self-end">
                  {gettext("Hide request")}
                </.button>
              </.form>
              <.form
                :if={report.activity_id}
                for={hide_form}
                phx-submit="hide-activity"
                phx-value-id={report.activity_id}
                class="flex gap-2"
              >
                <.input field={hide_form[:note]} placeholder={gettext("Hide reason")} />
                <.button class="btn btn-sm btn-error self-end">
                  {gettext("Hide activity")}
                </.button>
              </.form>
            </div>
            <.form
              for={report_form}
              phx-submit="moderate-report"
              phx-value-id={report.id}
              class="mt-4 grid gap-2 md:grid-cols-[12rem_1fr_auto]"
            >
              <.input
                field={report_form[:status]}
                type="select"
                options={[
                  {gettext("Reviewing"), "reviewing"},
                  {gettext("Resolved"), "resolved"},
                  {gettext("Dismissed"), "dismissed"}
                ]}
              />
              <.input
                field={report_form[:resolution_note]}
                placeholder={gettext("Resolution note")}
              />
              <.button class="btn btn-primary self-end">{gettext("Save")}</.button>
            </.form>
          </article>
          <p :if={@reports == []} class="text-base-content/50">{gettext("No reports.")}</p>
        </div>
        <button
          :if={@reports_cursor}
          type="button"
          phx-click="load-more-reports"
          class="btn btn-outline btn-sm mt-4"
        >
          {gettext("Load more")}
        </button>
      </section>

      <section :if={@evidence} class="mt-8 rounded-3xl bg-neutral p-6 text-neutral-content">
        <div class="flex items-center justify-between">
          <h2 class="text-xl font-bold">{gettext("Reported evidence")}</h2>
          <button phx-click="close-evidence" class="btn btn-sm">{gettext("Close")}</button>
        </div>
        <p class="mt-3 text-sm opacity-70">
          {gettext("Only the conversation linked to this report is displayed.")}
        </p>
        <div class="mt-4 space-y-2">
          <div :for={message <- @evidence.messages} class="rounded-xl bg-white/10 p-3 text-sm">
            <strong>{message.sender.display_name}:</strong> {message.body}
          </div>
          <div
            :for={message <- @evidence.activity_messages}
            class="rounded-xl bg-info/20 p-3 text-sm"
          >
            <strong>{message.sender.display_name}:</strong> {message.body}
          </div>
          <p
            :if={@evidence.messages == [] and @evidence.activity_messages == []}
            class="text-sm opacity-60"
          >
            {gettext("This report has no linked conversation.")}
          </p>
        </div>
        <button
          :if={@signals_cursor}
          type="button"
          phx-click="load-more-signals"
          class="btn btn-outline btn-sm mt-4"
        >
          {gettext("Load more")}
        </button>
      </section>

      <section class="mt-10">
        <h2 class="text-2xl font-bold">{gettext("Open abuse signals")}</h2>
        <div class="mt-4 space-y-3">
          <article :for={signal <- @signals} class="rounded-2xl bg-base-200 p-5">
            <% signal_form =
              scoped_form(
                %{"status" => "reviewed", "review_note" => ""},
                :moderation,
                "signal-#{signal.id}"
              ) %>
            <div class="font-semibold">
              {signal_label(signal.kind)} · {signal.subject.display_name}
            </div>
            <p class="mt-1 text-xs text-base-content/55">
              {gettext(
                "A signal requests human review; optional tracking absence is not proof of abuse."
              )}
            </p>
            <.form
              for={signal_form}
              phx-submit="moderate-signal"
              phx-value-id={signal.id}
              class="mt-3 grid gap-2 md:grid-cols-[12rem_1fr_auto]"
            >
              <.input
                field={signal_form[:status]}
                type="select"
                options={[
                  {gettext("Reviewed"), "reviewed"},
                  {gettext("Dismissed"), "dismissed"}
                ]}
              />
              <.input field={signal_form[:review_note]} placeholder={gettext("Review note")} />
              <.button class="btn btn-sm btn-primary self-end">{gettext("Save")}</.button>
            </.form>
          </article>
          <p :if={@signals == []} class="text-base-content/50">
            {gettext("No open signals.")}
          </p>
        </div>
      </section>

      <section class="mt-10">
        <h2 class="text-2xl font-bold">{gettext("Category proposals")}</h2>
        <div class="mt-4 space-y-4">
          <article :for={proposal <- @proposals} class="rounded-2xl border border-base-300 p-5">
            <% category_form =
              scoped_form(
                %{"structured_fields_json" => ~s({"fields":[]})},
                :category,
                "approve-#{proposal.id}"
              ) %>
            <% reject_form =
              scoped_form(%{"note" => ""}, :moderation, "reject-#{proposal.id}") %>
            <% merge_form =
              scoped_form(%{}, :moderation, "merge-#{proposal.id}") %>
            <h3 class="font-bold">
              {proposal.proposed_name}
              <span class="badge">{status_label(proposal.status)}</span>
            </h3>
            <p class="mt-1 text-sm">{proposal.reason}</p>
            <p class="mt-1 text-xs text-base-content/50">
              {gettext("%{count} community votes", count: proposal.vote_count)}
            </p>
            <div :if={proposal.status == :open} class="mt-4 grid gap-4 xl:grid-cols-2">
              <.form
                for={category_form}
                phx-submit="approve-proposal"
                phx-value-id={proposal.id}
                class="space-y-2 rounded-xl bg-base-200 p-4"
              >
                <.input field={category_form[:slug]} label={gettext("Slug")} />
                <.input field={category_form[:name_en]} label={gettext("English name")} />
                <.input
                  field={category_form[:name_uk]}
                  label={gettext("Ukrainian name (optional)")}
                />
                <.input
                  field={category_form[:name_ru]}
                  label={gettext("Russian name (optional)")}
                />
                <.input
                  field={category_form[:description_en]}
                  label={gettext("English description")}
                />
                <.input
                  field={category_form[:description_uk]}
                  label={gettext("Ukrainian description (optional)")}
                />
                <.input
                  field={category_form[:description_ru]}
                  label={gettext("Russian description (optional)")}
                />
                <.input
                  field={category_form[:structured_fields_json]}
                  type="textarea"
                  label={gettext("Structured fields JSON")}
                />
                <.input
                  field={category_form[:moderation_note]}
                  label={gettext("Decision note")}
                />
                <.button class="btn btn-success w-full">{gettext("Create category")}</.button>
              </.form>
              <div class="space-y-3">
                <.form
                  for={reject_form}
                  phx-submit="reject-proposal"
                  phx-value-id={proposal.id}
                  class="flex gap-2"
                >
                  <.input field={reject_form[:note]} placeholder={gettext("Rejection note")} />
                  <.button class="btn btn-error self-end">{gettext("Reject")}</.button>
                </.form>
                <.form
                  for={merge_form}
                  phx-submit="merge-proposal"
                  phx-value-id={proposal.id}
                  class="space-y-2"
                >
                  <.input
                    field={merge_form[:category_id]}
                    type="select"
                    label={gettext("Merge into")}
                    options={
                      Enum.map(@categories, &{WhoNeedHelp.Catalog.Category.name(&1, "en"), &1.id})
                    }
                  />
                  <.input field={merge_form[:note]} placeholder={gettext("Merge note")} />
                  <.button class="btn btn-outline w-full">{gettext("Merge")}</.button>
                </.form>
              </div>
            </div>
          </article>
        </div>
        <button
          :if={@proposals_cursor}
          type="button"
          phx-click="load-more-proposals"
          class="btn btn-outline btn-sm mt-4"
        >
          {gettext("Load more")}
        </button>
      </section>

      <section class="mt-10">
        <h2 class="text-2xl font-bold">{gettext("Accounts")}</h2>
        <div class="mt-4 overflow-x-auto rounded-2xl border border-base-300">
          <table class="table">
            <thead>
              <tr>
                <th>{gettext("User")}</th>
                <th>{gettext("Role")}</th>
                <th>{gettext("Status")}</th>
                <th>{gettext("Decision")}</th>
              </tr>
            </thead>
            <tbody>
              <tr :for={user <- @users}>
                <% user_form =
                  scoped_form(
                    %{
                      "moderation_status" => to_string(user.moderation_status),
                      "moderation_note" => user.moderation_note || ""
                    },
                    :moderation,
                    "user-status-#{user.id}"
                  ) %>
                <% role_form =
                  scoped_form(
                    %{"role" => to_string(user.role)},
                    :moderation,
                    "user-role-#{user.id}"
                  ) %>
                <td>
                  {user.display_name}
                  <div class="text-xs opacity-50">{user.email}</div>
                </td>
                <td>{role_label(user.role)}</td>
                <td>{status_label(user.moderation_status)}</td>
                <td>
                  <.form
                    for={user_form}
                    phx-submit="moderate-user"
                    phx-value-id={user.id}
                    class="flex min-w-[28rem] gap-2"
                  >
                    <.input
                      field={user_form[:moderation_status]}
                      type="select"
                      options={[
                        {gettext("Active"), "active"},
                        {gettext("Restricted"), "restricted"},
                        {gettext("Suspended"), "suspended"}
                      ]}
                    />
                    <.input
                      field={user_form[:moderation_note]}
                      placeholder={gettext("Internal note")}
                    />
                    <.button class="btn btn-sm btn-primary self-end">{gettext("Save")}</.button>
                  </.form>
                  <.form
                    :if={@current_scope.user.role == :admin}
                    for={role_form}
                    phx-submit="moderate-role"
                    phx-value-id={user.id}
                    class="mt-2 flex gap-2"
                  >
                    <.input
                      field={role_form[:role]}
                      type="select"
                      options={[
                        {gettext("User"), "user"},
                        {gettext("Moderator"), "moderator"},
                        {gettext("Administrator"), "admin"}
                      ]}
                    />
                    <.button class="btn btn-sm btn-warning self-end">
                      {gettext("Change role")}
                    </.button>
                  </.form>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <button
          :if={@users_cursor}
          type="button"
          phx-click="load-more-users"
          class="btn btn-outline btn-sm mt-4"
        >
          {gettext("Load more")}
        </button>
      </section>
    </Layouts.app>
    """
  end
end
