defmodule WhoNeedHelpWeb.MutualAidLiveTest do
  use WhoNeedHelpWeb.ConnCase, async: false

  import Phoenix.LiveViewTest
  import WhoNeedHelp.AccountsFixtures

  alias WhoNeedHelp.{Accounts, Activities, Catalog, Help, Messaging, Trust}
  alias WhoNeedHelp.Repo

  setup :register_and_log_in_user

  test "leaderboard is available to an authenticated user", %{conn: conn} do
    {:ok, _view, html} = live(conn, ~p"/leaderboard")
    assert html =~ "Helpers leaderboard"
    assert html =~ "Repeated help between the same pair"
  end

  test "authenticated LiveView honors the locale stored by the browser pipeline", %{conn: conn} do
    conn = get(conn, ~p"/?locale=uk")
    {:ok, _view, html} = live(conn, ~p"/requests")

    assert html =~ "Хто потребує допомоги?"
    assert html =~ "Створити термінову заявку"
  end

  test "profile social-link creation uses the shared action limiter", %{conn: conn} do
    previous = Application.get_env(:who_need_help, :rate_limit_policies)

    Application.put_env(:who_need_help, :rate_limit_policies, %{
      "add_social" => %{limit: 1, window_seconds: 60}
    })

    on_exit(fn -> Application.put_env(:who_need_help, :rate_limit_policies, previous) end)

    {:ok, view, _html} = live(conn, ~p"/profile")

    assert render_submit(view, "add-social", %{
             "social_identity" => %{
               "provider" => "telegram",
               "profile_url" => "https://t.me/rate_limit_first"
             }
           }) =~ "Social link added as unverified."

    assert render_submit(view, "add-social", %{
             "social_identity" => %{
               "provider" => "github",
               "profile_url" => "https://github.com/rate-limit-second"
             }
           }) =~ "Too many actions in the configured time window."
  end

  test "invalid request and activity identifiers redirect without crashing", %{conn: conn} do
    assert {:error, {:live_redirect, %{to: "/requests"}}} =
             live(conn, "/requests/not-a-uuid")

    assert {:error, {:live_redirect, %{to: "/activities"}}} =
             live(conn, "/activities/not-a-uuid")
  end

  test "request index applies PubSub updates without a full page reload" do
    category = Catalog.seed_defaults()
    requester = user_fixture(display_name: "Realtime requester")
    helper = user_fixture(display_name: "Realtime helper")
    {:ok, view, _html} = build_conn() |> log_in_user(helper) |> live(~p"/requests")

    {:ok, request} =
      Help.create_request(Accounts.Scope.for_user(requester), %{
        "title" => "Realtime medicine pickup",
        "description" => "The legal medicine is reserved and ready for collection.",
        "pickup_instructions" => "Use the order number from private coordination.",
        "location_label" => "Central district",
        "latitude" => "50.4501",
        "longitude" => "30.5234",
        "urgency" => "now",
        "location_visibility" => "approximate_public",
        "structured_data" => %{"pickup_status" => "reserved"},
        "expires_at" => DateTime.utc_now(:second) |> DateTime.add(2, :hour),
        "category_id" => category.id,
        "safety_confirmed" => true
      })

    assert render(view) =~ "Realtime medicine pickup"

    {:ok, _assignment} = Help.accept_request(Accounts.Scope.for_user(helper), request.id)
    refute render(view) =~ "Realtime medicine pickup"
  end

  test "request realtime updates keep a bounded window without skipping pagination" do
    category = Catalog.seed_defaults()
    requester = user_fixture(display_name: "Bounded realtime requester")
    viewer = user_fixture(display_name: "Bounded realtime viewer")
    {:ok, view, _html} = build_conn() |> log_in_user(viewer) |> live(~p"/requests")

    requests =
      for index <- 1..25 do
        attrs =
          category
          |> request_attrs()
          |> Map.put("title", "Bounded realtime request #{index}")
          |> Map.put(
            "expires_at",
            DateTime.utc_now(:second) |> DateTime.add(3 * 60 * 60 + index, :second)
          )

        {:ok, request} = Help.create_request(Accounts.Scope.for_user(requester), attrs)
        request
      end

    render(view)
    first = List.first(requests)
    overflow = List.last(requests)

    assert has_element?(view, "#open-request-#{first.id}")
    refute has_element?(view, "#open-request-#{overflow.id}")

    view |> element("button", "Load more") |> render_click()
    assert has_element?(view, "#open-request-#{overflow.id}")
  end

  test "activity index applies PubSub updates without a full page reload" do
    Catalog.seed_defaults()
    organizer = user_fixture(display_name: "Realtime organizer")
    viewer = user_fixture(display_name: "Realtime activity viewer")

    category =
      Catalog.list_categories(:activity)
      |> Enum.find(&(&1.slug == "coffee-meetup"))

    {:ok, view, _html} = build_conn() |> log_in_user(viewer) |> live(~p"/activities")

    {:ok, _activity} =
      Activities.create_activity(Accounts.Scope.for_user(organizer), %{
        "title" => "Realtime coffee meetup",
        "description" => "Meet at a public café for a short conversation.",
        "structured_data" => %{"setting" => "cafe"},
        "location_label" => "Central square",
        "latitude" => "50.4501",
        "longitude" => "30.5234",
        "location_visibility" => "approximate_public",
        "starts_at" => DateTime.utc_now(:second) |> DateTime.add(2, :hour),
        "join_deadline" => DateTime.utc_now(:second) |> DateTime.add(1, :hour),
        "capacity" => 3,
        "category_id" => category.id,
        "safety_confirmed" => true
      })

    assert render(view) =~ "Realtime coffee meetup"
  end

  test "activity realtime updates keep a bounded window without skipping pagination" do
    Catalog.seed_defaults()
    organizer = user_fixture(display_name: "Bounded realtime organizer")
    viewer = user_fixture(display_name: "Bounded realtime activity viewer")

    category =
      Catalog.list_categories(:activity)
      |> Enum.find(&(&1.slug == "coffee-meetup"))

    {:ok, view, _html} = build_conn() |> log_in_user(viewer) |> live(~p"/activities")

    activities =
      for index <- 1..25 do
        attrs =
          category
          |> activity_attrs()
          |> Map.put("title", "Bounded realtime activity #{index}")
          |> Map.put(
            "starts_at",
            DateTime.utc_now(:second) |> DateTime.add(3 * 60 * 60 + index, :second)
          )

        {:ok, activity} =
          Activities.create_activity(Accounts.Scope.for_user(organizer), attrs)

        activity
      end

    render(view)
    first = List.first(activities)
    overflow = List.last(activities)

    assert has_element?(view, "#open-activity-#{first.id}")
    refute has_element?(view, "#open-activity-#{overflow.id}")

    view |> element("button", "Load more") |> render_click()
    assert has_element?(view, "#open-activity-#{overflow.id}")
  end

  test "new request form is driven by category structured fields", %{conn: conn} do
    category = Catalog.seed_defaults()
    {:ok, view, html} = live(conn, ~p"/requests/new")
    assert html =~ "This is not an emergency service"

    html =
      render_change(view, "validate", %{
        "help_request" => %{"category_id" => category.id}
      })

    assert html =~ "Medicine pickup status"
    assert html =~ "Pickup and deliver a legal medicine"
    assert html =~ "help_request[structured_data][pickup_status]"

    html =
      render_change(view, "validate", %{
        "help_request" => %{
          "category_id" => category.id,
          "structured_data" => %{
            "_unused_pickup_status" => "",
            "pickup_status" => ""
          }
        }
      })

    assert html =~ "pickup_status is required"
    refute html =~ "_unused_pickup_status is not allowed"
  end

  test "category descriptions use the current Ukrainian locale", %{conn: conn} do
    category = Catalog.seed_defaults()
    conn = get(conn, ~p"/?locale=uk")
    {:ok, view, _html} = live(conn, ~p"/requests/new")

    html =
      render_change(view, "validate", %{
        "help_request" => %{"category_id" => category.id}
      })

    assert html =~ "Отримати й доставити дозволені ліки"
  end

  test "new request form renders hierarchical roadside fields and safety copy", %{conn: conn} do
    Catalog.seed_defaults()

    motorcycle =
      Catalog.list_categories()
      |> Enum.find(&(&1.slug == "motorcycle-roadside-help"))

    {:ok, view, html} = live(conn, ~p"/requests/new")
    assert html =~ "Roadside help — Motorcycle roadside help"

    html =
      render_change(view, "validate", %{
        "help_request" => %{"category_id" => motorcycle.id}
      })

    assert html =~ "Motorcycle problem"
    assert html =~ "Broken chain"
    assert html =~ "help_request[structured_data][motorcycle_issue]"
    assert html =~ "help_request[structured_data][vehicle_in_safe_place]"
    assert html =~ "people and vehicles are away from active traffic"
    refute html =~ "controlled substance"
  end

  test "new request form uses the profile location visibility default", %{
    conn: conn,
    user: user
  } do
    user
    |> Ecto.Changeset.change(location_visibility: :exact_for_active_match)
    |> Repo.update!()

    {:ok, view, _html} = live(conn, ~p"/requests/new")

    assert has_element?(
             view,
             "#help_request_location_visibility option[value='exact_for_active_match'][selected]"
           )
  end

  test "activity form is category-driven and separate from urgent help", %{conn: conn} do
    Catalog.seed_defaults()

    hiking =
      Catalog.list_categories(:activity)
      |> Enum.find(&(&1.slug == "hiking-group"))

    {:ok, view, html} = live(conn, ~p"/activities/new")
    assert html =~ "Social activity — Hiking"
    refute html =~ "Medicine pickup status"

    html =
      render_change(view, "validate", %{
        "activity" => %{"category_id" => hiking.id}
      })

    assert html =~ "Route difficulty"
    assert html =~ "activity[structured_data][route_plan]"
    assert html =~ "activity[structured_data][safety_plan_confirmed]"
    assert html =~ "approve participants individually"

    html =
      render_change(view, "validate", %{
        "activity" => %{
          "category_id" => hiking.id,
          "structured_data" => %{
            "_unused_route_plan" => "",
            "route_plan" => ""
          }
        }
      })

    assert html =~ "route_plan is required"
    refute html =~ "_unused_route_plan is not allowed"
  end

  test "organizer approves an activity participant and group chat updates live", %{
    conn: organizer_conn,
    scope: organizer_scope,
    user: organizer
  } do
    Catalog.seed_defaults()
    participant = user_fixture(display_name: "Activity participant")
    participant_conn = build_conn() |> log_in_user(participant)

    {:ok, _manual_link} =
      Accounts.add_social_identity(organizer, %{
        "provider" => "telegram",
        "profile_url" => "https://t.me/activity_organizer",
        "handle" => "@activity_organizer"
      })

    {:ok, _verified_link} =
      Accounts.upsert_verified_social_identity(participant, %{
        provider: :github,
        provider_uid: "activity-participant",
        profile_url: "https://github.com/activity-participant",
        handle: "@activity-participant",
        verified_at: ~U[2026-07-18 18:00:00Z]
      })

    coffee =
      Catalog.list_categories(:activity)
      |> Enum.find(&(&1.slug == "coffee-meetup"))

    attrs = %{
      "title" => "Public coffee meetup",
      "description" => "Meet at a public café for tea and conversation.",
      "structured_data" => %{"setting" => "cafe"},
      "location_label" => "Central square",
      "latitude" => "50.4501",
      "longitude" => "30.5234",
      "location_visibility" => "approximate_public",
      "starts_at" => DateTime.utc_now(:second) |> DateTime.add(2, :hour),
      "join_deadline" => DateTime.utc_now(:second) |> DateTime.add(1, :hour),
      "capacity" => 3,
      "category_id" => coffee.id,
      "safety_confirmed" => true
    }

    {:ok, activity} = Activities.create_activity(organizer_scope, attrs)
    {:ok, organizer_view, _html} = live(organizer_conn, ~p"/activities/#{activity.id}")
    {:ok, participant_view, html} = live(participant_conn, ~p"/activities/#{activity.id}")
    assert html =~ "Request to join"
    assert html =~ "Safety controls"
    assert html =~ "Block organizer"
    assert html =~ "Send report"
    assert html =~ "telegram · unverified"
    refute html =~ "Approved group chat"

    participant_view
    |> element("button[phx-click='join']")
    |> render_click()

    assert render(participant_view) =~ "Approval pending"
    organizer_html = render(organizer_view)
    assert organizer_html =~ "Activity participant"
    assert organizer_html =~ "github · verified"

    organizer_view
    |> element("button[phx-click='approve']")
    |> render_click()

    participant_html = render(participant_view)
    assert participant_html =~ "Approved group chat"
    assert participant_html =~ "github · verified"

    html =
      participant_view
      |> form("form[phx-submit='send-message']", %{
        "activity_message" => %{"body" => "I will be there."}
      })
      |> render_submit()

    assert html =~ "I will be there."
    assert_push_event(participant_view, "reset-message-form", %{id: "activity-message-form"})
    assert render(organizer_view) =~ "I will be there."

    participant_view
    |> form("#activity-report-form",
      report: %{reason: "harassment", details: "Review this activity context."}
    )
    |> render_submit()

    assert_push_event(participant_view, "reset-message-form", %{id: "activity-report-form"})

    now = DateTime.utc_now(:second)

    for index <- 1..51 do
      body =
        case index do
          1 -> "oldest-realtime-activity-message"
          51 -> "newest-realtime-activity-message"
          _ -> "bounded activity message #{index}"
        end

      send(
        organizer_view.pid,
        {:activity_message,
         %WhoNeedHelp.Activities.Message{
           id: Ecto.UUID.generate(),
           activity_id: activity.id,
           sender_id: participant.id,
           sender: participant,
           body: body,
           inserted_at: DateTime.add(now, index, :second)
         }}
      )
    end

    bounded_activity_html = render(organizer_view)
    refute bounded_activity_html =~ "oldest-realtime-activity-message"
    assert bounded_activity_html =~ "newest-realtime-activity-message"

    organizer_view
    |> element("button[phx-click='complete']")
    |> render_click()

    assert render(participant_view) =~ "Completed"
    refute has_element?(participant_view, "button[phx-click='leave']")

    stop_live_view(organizer_view)
    stop_live_view(participant_view)
  end

  test "views opened before matching subscribe to the private chat after assignment", %{
    conn: requester_conn,
    scope: requester_scope
  } do
    category = Catalog.seed_defaults()
    helper = user_fixture(display_name: "Helper")
    helper_conn = build_conn() |> log_in_user(helper)

    {:ok, request} = Help.create_request(requester_scope, request_attrs(category))
    {:ok, requester_view, _html} = live(requester_conn, ~p"/requests/#{request.id}")
    {:ok, helper_view, _html} = live(helper_conn, ~p"/requests/#{request.id}")

    helper_view
    |> element("button[phx-click='accept']")
    |> render_click()

    assert render(requester_view) =~ "Private match chat"
    assert render(helper_view) =~ "Private match chat"
    assert has_element?(helper_view, "#message-form")

    html =
      helper_view
      |> form("form[phx-submit='send-message']", %{
        "message" => %{"body" => "I am on my way to the pharmacy."}
      })
      |> render_submit()

    assert html =~ "I am on my way to the pharmacy."
    assert_push_event(helper_view, "reset-message-form", %{id: "message-form"})
    assert render(requester_view) =~ "I am on my way to the pharmacy."

    now = DateTime.utc_now(:second)
    assignment_id = Repo.get_by!(WhoNeedHelp.Help.Assignment, request_id: request.id).id

    for index <- 1..51 do
      body =
        case index do
          1 -> "oldest-realtime-help-message"
          51 -> "newest-realtime-help-message"
          _ -> "bounded help message #{index}"
        end

      send(
        requester_view.pid,
        {:new_message,
         %WhoNeedHelp.Messaging.Message{
           id: Ecto.UUID.generate(),
           assignment_id: assignment_id,
           sender_id: helper.id,
           sender: helper,
           body: body,
           inserted_at: DateTime.add(now, index, :second)
         }}
      )
    end

    bounded_help_html = render(requester_view)
    refute bounded_help_html =~ "oldest-realtime-help-message"
    assert bounded_help_html =~ "newest-realtime-help-message"

    send(
      requester_view.pid,
      {:position_updated, helper.id,
       %{latitude: 50.452, longitude: 30.526, accuracy: 6, captured_at: DateTime.utc_now()},
       %{
         helper_movement_observed_at: DateTime.utc_now(:second),
         proximity_observed_at: nil
       }}
    )

    assert render(requester_view) =~ "50.452"
    assert render(requester_view) =~ "30.526"
    assert render(requester_view) =~ "helper movement supported"
    refute render(requester_view) =~ "no movement evidence"

    send(requester_view.pid, {:tracking_stopped, helper.id})

    refute render(requester_view) =~ "50.452"

    stop_live_view(requester_view)
    stop_live_view(helper_view)
  end

  test "terminal request updates stop Android foreground tracking", _context do
    category = Catalog.seed_defaults()
    requester = user_fixture(display_name: "Tracking requester")
    helper = user_fixture(display_name: "Tracking helper")

    {:ok, request} =
      Help.create_request(user_scope_fixture(requester), request_attrs(category))

    {:ok, assignment} = Help.accept_request(user_scope_fixture(helper), request.id)

    helper_conn =
      build_conn()
      |> log_in_user(helper)
      |> put_connect_params(%{"client_type" => "android"})

    {:ok, helper_view, _html} = live(helper_conn, ~p"/requests/#{request.id}")

    helper_view
    |> element("button[phx-click='start-tracking']")
    |> render_click()

    assert_push_event(helper_view, "native-tracking-start", %{assignment_id: assignment_id})
    assert assignment_id == assignment.id

    assert {:ok, _cancelled} =
             Help.cancel_request(user_scope_fixture(requester), request.id)

    assert_push_event(helper_view, "native-tracking-stop", %{})
    refute has_element?(helper_view, "button[phx-click='stop-tracking']")

    stop_live_view(helper_view)
  end

  test "regular users cannot enter moderation", %{conn: conn} do
    assert {:error, {:redirect, %{to: "/requests"}}} = live(conn, ~p"/moderation")
  end

  test "moderator dashboard renders reports, signals, proposals, and accounts", %{
    conn: conn,
    user: user
  } do
    user |> Ecto.Changeset.change(role: :moderator) |> Repo.update!()
    category = Catalog.seed_defaults()
    requester = user_fixture(display_name: "Requester")
    helper = user_fixture(display_name: "Helper")

    attrs = request_attrs(category)
    {:ok, request} = Help.create_request(user_scope_fixture(requester), attrs)
    {:ok, assignment} = Help.accept_request(user_scope_fixture(helper), request.id)

    {:ok, _message} =
      Messaging.send_message(user_scope_fixture(helper), assignment, %{
        "body" => "Evidence message"
      })

    {:ok, _report} =
      Trust.report(user_scope_fixture(requester), %{
        "reason" => "harassment",
        "details" => "Please review the matched conversation.",
        "assignment_id" => assignment.id
      })

    {:ok, _proposal} =
      Catalog.propose(user_scope_fixture(requester), %{
        "proposed_name" => "Bicycle repair",
        "reason" => "Urgent roadside bicycle repairs need a reusable category."
      })

    {:ok, view, html} = live(conn, ~p"/moderation")
    assert html =~ "Moderation"
    assert html =~ "Reports"
    assert html =~ "Open abuse signals"
    assert html =~ "Category proposals"
    assert html =~ "Accounts"
    assert html =~ "Please review the matched conversation."
    assert html =~ "Bicycle repair"

    evidence_html =
      view
      |> element("button[phx-click='view-evidence']")
      |> render_click()

    assert evidence_html =~ "Evidence message"
  end

  test "request page exposes report and block controls for another user", %{conn: conn} do
    category = Catalog.seed_defaults()
    requester = user_fixture(display_name: "Requester")

    {:ok, request} = Help.create_request(user_scope_fixture(requester), request_attrs(category))
    {:ok, view, html} = live(conn, ~p"/requests/#{request.id}")

    assert html =~ "Safety controls"
    assert html =~ "Block this user"
    assert html =~ "Send report"
    assert html =~ "This is not an emergency service"

    view
    |> form("#request-report-form",
      report: %{reason: "dangerous_request", details: "Review the request context."}
    )
    |> render_submit()

    assert_push_event(view, "reset-message-form", %{id: "request-report-form"})
  end

  test "successful category proposal resets the browser form", %{conn: conn} do
    Catalog.seed_defaults()
    {:ok, view, _html} = live(conn, ~p"/categories/proposals")

    view
    |> form("#category-proposal-form",
      category_proposal: %{
        proposed_name: "Chain repair",
        mode: "help",
        reason: "A reusable category for urgent roadside bicycle chain repair."
      }
    )
    |> render_submit()

    assert_push_event(view, "reset-message-form", %{id: "category-proposal-form"})
    assert render(view) =~ "Chain repair"
  end

  test "review submission updates both participant pages and hides the submitted form" do
    category = Catalog.seed_defaults()
    requester = user_fixture(display_name: "Review requester")
    helper = user_fixture(display_name: "Review helper")
    requester_scope = user_scope_fixture(requester)
    helper_scope = user_scope_fixture(helper)

    {:ok, request} = Help.create_request(requester_scope, request_attrs(category))
    {:ok, assignment} = Help.accept_request(helper_scope, request.id)
    {:ok, _} = Help.confirm_completion(requester_scope, assignment.id)
    {:ok, _} = Help.confirm_completion(helper_scope, assignment.id)

    {:ok, _assignment} =
      Help.verify_handover(helper_scope, assignment.id, Help.handover_code(request.id))

    {:ok, requester_view, _html} =
      build_conn() |> log_in_user(requester) |> live(~p"/requests/#{request.id}")

    {:ok, helper_view, _html} =
      build_conn() |> log_in_user(helper) |> live(~p"/requests/#{request.id}")

    requester_view
    |> form("form[phx-submit='review']", review: %{rating: "5", comment: "Kind"})
    |> render_submit()

    refute has_element?(requester_view, "form[phx-submit='review']")
    assert has_element?(helper_view, "form[phx-submit='review']")

    helper_view
    |> form("form[phx-submit='review']", review: %{rating: "4", comment: "Clear"})
    |> render_submit()

    refute has_element?(helper_view, "form[phx-submit='review']")
    assert render(requester_view) =~ "rating 4.0"
    assert render(helper_view) =~ "rating 4.0"
    assert render(requester_view) =~ ">5.0<"
    assert render(helper_view) =~ ">5.0<"
  end

  defp request_attrs(category) do
    %{
      "title" => "Medicine is ready at the pharmacy",
      "description" => "The legal medicine is already reserved and ready for pickup.",
      "location_label" => "Central district",
      "latitude" => "50.4501",
      "longitude" => "30.5234",
      "urgency" => "now",
      "location_visibility" => "approximate_public",
      "structured_data" => %{"pickup_status" => "reserved"},
      "expires_at" => DateTime.utc_now(:second) |> DateTime.add(3, :hour),
      "category_id" => category.id,
      "safety_confirmed" => true
    }
  end

  defp activity_attrs(category) do
    %{
      "title" => "Public coffee meetup",
      "description" => "Meet at a public café for a short conversation.",
      "structured_data" => %{"setting" => "cafe"},
      "location_label" => "Central square",
      "latitude" => "50.4501",
      "longitude" => "30.5234",
      "location_visibility" => "approximate_public",
      "starts_at" => DateTime.utc_now(:second) |> DateTime.add(3, :hour),
      "join_deadline" => DateTime.utc_now(:second) |> DateTime.add(2, :hour),
      "capacity" => 3,
      "category_id" => category.id,
      "safety_confirmed" => true
    }
  end

  defp stop_live_view(%Phoenix.LiveViewTest.View{
         proxy: {_ref, _topic, proxy_pid}
       }) do
    Phoenix.LiveViewTest.ClientProxy.stop(proxy_pid, :shutdown)
  end
end
