defmodule WhoNeedHelpWeb.ContentRemovalController do
  use WhoNeedHelpWeb, :controller

  alias WhoNeedHelp.ContentRemoval
  alias WhoNeedHelp.ContentRemoval.Notice

  def new(conn, params) do
    attrs =
      case internal_location(params["location"]) do
        nil -> %{}
        location -> %{"content_locations" => location}
      end

    render_form(conn, :general, %Notice{}, attrs)
  end

  def take_it_down(conn, _params), do: render_form(conn, :take_it_down, %Notice{}, %{})

  def create(conn, %{"notice" => params}) when is_map(params),
    do: create_notice(conn, :general, params)

  def create(conn, _params), do: send_resp(conn, :bad_request, "Bad Request")

  def create_take_it_down(conn, %{"notice" => params}) when is_map(params),
    do: create_notice(conn, :take_it_down, params)

  def create_take_it_down(conn, _params),
    do: send_resp(conn, :bad_request, "Bad Request")

  def show(conn, %{"id" => id, "token" => token}) do
    case ContentRemoval.get_by_access_token(id, token) do
      {:ok, notice} -> render(conn, :show, notice: notice)
      {:error, :not_found} -> send_resp(conn, :not_found, "Not found")
    end
  end

  def show(conn, _params), do: send_resp(conn, :not_found, "Not found")

  defp create_notice(conn, regime, params) do
    case ContentRemoval.create_notice(conn.assigns.current_scope, regime, params) do
      {:ok, notice} ->
        redirect(conn,
          to: ~p"/legal/content-removal/received?reference=#{notice.reference}"
        )

      {:error, %Ecto.Changeset{} = changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render_form(regime, changeset, params)

      {:error, :rate_limited} ->
        conn
        |> put_status(:too_many_requests)
        |> put_flash(:error, gettext("Too many removal notices. Please try again later."))
        |> render_form(regime, %Notice{}, params)
    end
  end

  def received(conn, %{"reference" => reference}) when is_binary(reference) do
    render(conn, :received, reference: String.slice(reference, 0, 64))
  end

  def received(conn, _params), do: redirect(conn, to: ~p"/legal/content-removal")

  defp render_form(conn, regime, %Ecto.Changeset{} = changeset, _attrs) do
    render(conn, form_template(regime),
      form: Phoenix.Component.to_form(changeset, as: :notice),
      contact_email: current_email(conn)
    )
  end

  defp render_form(conn, regime, %Notice{} = notice, attrs) do
    attrs = with_contact_email(conn, attrs)
    notice = %{notice | regime: regime}
    changeset = ContentRemoval.change_notice(notice, attrs)
    render_form(conn, regime, changeset, attrs)
  end

  defp form_template(:take_it_down), do: :take_it_down
  defp form_template(_regime), do: :new

  defp with_contact_email(conn, attrs) do
    case current_email(conn) do
      nil -> attrs
      email -> Map.put(attrs, "contact_email", email)
    end
  end

  defp current_email(%{assigns: %{current_scope: %{user: %{email: email}}}}), do: email
  defp current_email(_conn), do: nil

  defp internal_location(value) when is_binary(value) do
    uri = URI.parse(value)

    if uri.scheme == nil and uri.host == nil and is_binary(uri.path) and
         String.starts_with?(uri.path, "/") and not String.starts_with?(uri.path, "//") and
         not String.contains?(value, ["\r", "\n"]) do
      WhoNeedHelpWeb.Endpoint.url() <> value
    end
  end

  defp internal_location(_value), do: nil
end
