defmodule WhoNeedHelp.SupportAndContentRemovalTest do
  use WhoNeedHelp.DataCase, async: false

  import WhoNeedHelp.AccountsFixtures
  import Swoosh.TestAssertions

  alias WhoNeedHelp.{ContentRemoval, Repo, Support}
  alias WhoNeedHelp.ContentRemoval.Notice
  alias WhoNeedHelp.Support.SupportRequest
  alias WhoNeedHelp.Trust.AuditEvent

  test "public support request is persisted, acknowledged, and verified only through its email link" do
    assert {:ok, request} =
             Support.create_request(nil, %{
               "kind" => "technical_issue",
               "contact_email" => " Person@Example.com ",
               "subject" => "Map does not open",
               "details" => "The request map remains unavailable after reloading the page."
             })

    assert request.reference =~ "SUP-"
    assert request.contact_email == "person@example.com"
    assert is_nil(request.contact_verified_at)

    assert_email_sent(fn email ->
      email.subject =~ request.reference and email.text_body =~ Support.status_url(request)
    end)

    assert {:error, :not_found} = Support.get_by_access_token(request.id, "invalid")

    assert {:ok, verified} =
             Support.get_by_access_token(request.id, Support.access_token(request))

    assert verified.contact_verified_at

    assert Repo.exists?(
             from event in AuditEvent,
               where:
                 event.action == "support_request.created" and
                   event.target_id == ^request.id
           )
  end

  test "configured support inbox receives a metadata-only operator alert and Reply-To" do
    previous = Application.get_env(:who_need_help, :support_inbox_address)
    Application.put_env(:who_need_help, :support_inbox_address, "support@example.com")
    on_exit(fn -> Application.put_env(:who_need_help, :support_inbox_address, previous) end)

    assert {:ok, request} =
             Support.create_request(nil, %{
               "kind" => "moderation_appeal",
               "contact_email" => "appeal@example.com",
               "subject" => "Appeal moderation decision",
               "details" =>
                 "Please review the moderation decision identified in my account notice."
             })

    assert_email_sent(fn email ->
      email.to == [{"", "appeal@example.com"}] and
        email.reply_to == {"", "support@example.com"}
    end)

    assert_email_sent(fn email ->
      email.to == [{"", "support@example.com"}] and email.subject =~ request.reference and
        email.text_body =~ "/support/operations" and
        not String.contains?(email.text_body, request.details)
    end)
  end

  test "authenticated support uses the account email and staff moderation is audited" do
    user = user_fixture()
    assert_email_sent()
    scope = user_scope_fixture(user)

    assert {:ok, request} =
             Support.create_request(scope, %{
               "kind" => "data_export",
               "contact_email" => "forged@example.com",
               "subject" => "Export my data",
               "details" =>
                 "Please prepare an export of the personal data associated with my account."
             })

    assert request.contact_email == user.email
    assert request.contact_verified_at
    assert_email_sent()

    moderator =
      user_fixture()
      |> Ecto.Changeset.change(role: :moderator)
      |> Repo.update!()

    assert_email_sent()

    assert {:error, :forbidden} =
             Support.moderate(scope, request.id, %{
               "status" => "resolved",
               "resolution_note" => "Not allowed"
             })

    assert {:ok, resolved} =
             Support.moderate(user_scope_fixture(moderator), request.id, %{
               "status" => "resolved",
               "resolution_note" => "The export is ready for the verified account owner."
             })

    assert resolved.status == :resolved
    assert resolved.response_sent_at

    assert_email_sent(fn email ->
      email.subject =~ "support update" and email.text_body =~ "export is ready"
    end)
  end

  test "TAKE IT DOWN notices use a separate urgent queue and never accept an unrelated category" do
    attrs = %{
      "category" => "non_consensual_intimate_media",
      "submitter_name" => "Affected Person",
      "contact_email" => "affected@example.com",
      "relationship" => "self",
      "content_locations" => "https://example.test/requests/one",
      "explanation" => "An intimate visual depiction of me was posted without my consent.",
      "electronic_signature" => "Affected Person",
      "good_faith" => "true",
      "accurate_complete" => "true"
    }

    assert {:ok, notice} = ContentRemoval.create_notice(nil, :take_it_down, attrs)
    assert notice.regime == :take_it_down
    assert notice.status == :urgent_review
    assert notice.response_due_at
    assert is_nil(notice.contact_verified_at)

    assert_email_sent(fn email ->
      email.subject =~ notice.reference and
        email.text_body =~ "Do not reply with or upload intimate visual material"
    end)

    assert {:error, changeset} =
             ContentRemoval.create_notice(nil, :take_it_down, %{
               attrs
               | "category" => "copyright",
                 "contact_email" => "copyright@example.com"
             })

    assert "must describe intimate visual material" in errors_on(changeset).category
  end

  test "general notice validates exact URLs and permits anonymous child-safety reporting" do
    assert {:error, changeset} =
             ContentRemoval.create_notice(nil, :general, %{
               "category" => "illegal_content",
               "submitter_name" => "Reporter",
               "contact_email" => "reporter@example.com",
               "relationship" => "other",
               "content_locations" => "not a URL",
               "explanation" =>
                 "This is a sufficiently long explanation of the reported content.",
               "electronic_signature" => "Reporter",
               "good_faith" => "true",
               "accurate_complete" => "true"
             })

    assert "must contain one full HTTP or HTTPS URL per line" in errors_on(changeset).content_locations

    assert {:ok, notice} =
             ContentRemoval.create_notice(nil, :general, %{
               "category" => "child_sexual_abuse_material",
               "content_locations" => "https://example.test/messages/safety-reference",
               "explanation" =>
                 "The referenced location appears to contain sexual material involving a minor.",
               "good_faith" => "true",
               "accurate_complete" => "true"
             })

    assert notice.status == :urgent_review
    assert is_nil(notice.contact_email)
    assert is_nil(notice.acknowledgement_sent_at)
    assert [%Notice{id: id}] = ContentRemoval.paginate_for_staff(moderator_scope()).entries
    assert id == notice.id
  end

  test "account deletion is represented as a dedicated verified account-lifecycle request" do
    user = user_fixture()

    assert {:ok, request} =
             user
             |> user_scope_fixture()
             |> Support.create_account_deletion_request()

    assert %SupportRequest{kind: :account_deletion, requester_id: requester_id} = request
    assert requester_id == user.id
    assert request.contact_verified_at
  end

  defp moderator_scope do
    user_fixture()
    |> Ecto.Changeset.change(role: :moderator)
    |> Repo.update!()
    |> user_scope_fixture()
  end
end
