# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.Feed.FeedView do
  use Phoenix.HTML
  use Pleroma.Web, :view

  alias Pleroma.Object
  alias Pleroma.User
  alias Pleroma.Web.Gettext
  alias Pleroma.Web.MediaProxy

  require Pleroma.Constants

  @days ~w(Mon Tue Wed Thu Fri Sat Sun)
  @months ~w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)

  def prepare_activity(activity, opts \\ []) do
    object = Object.normalize(activity, fetch: false)

    actor =
      if opts[:actor] do
        Pleroma.User.get_cached_by_ap_id(activity.actor)
      end

    %{
      activity: activity,
      object: object,
      data: Map.get(object, :data),
      actor: actor
    }
  end

  def most_recent_update(activities) do
    with %{updated_at: updated_at} <- List.first(activities) do
      to_rfc3339(updated_at)
    end
  end

  def most_recent_update(activities, user, :atom) do
    (List.first(activities) || user).updated_at
    |> to_rfc3339()
  end

  def most_recent_update(activities, user, :rss) do
    (List.first(activities) || user).updated_at
    |> to_rfc2822()
  end

  def feed_logo do
    case Pleroma.Config.get([:feed, :logo]) do
      nil ->
        "#{Pleroma.Web.Endpoint.url()}/static/logo.svg"

      logo ->
        "#{Pleroma.Web.Endpoint.url()}#{logo}"
    end
    |> MediaProxy.url()
  end

  def email(user) do
    user.nickname <> "@" <> Pleroma.Web.Endpoint.host()
  end

  def logo(user) do
    user
    |> User.avatar_url()
    |> MediaProxy.url()
  end

  def last_activity(activities), do: List.last(activities)

  def activity_title(%{"content" => content} = data, opts \\ %{}) do
    summary = Map.get(data, "summary", "")

    title =
      cond do
        summary != "" -> summary
        content != "" -> activity_content(data)
        true -> "a post"
      end

    title
    |> Pleroma.Web.Metadata.Utils.scrub_html_and_truncate(opts[:max_length], opts[:omission])
    |> HtmlEntities.encode()
  end

  def activity_description(data) do
    content = activity_content(data)
    summary = data["summary"]

    cond do
      content != "" -> escape(content)
      summary != "" -> escape(summary)
      true -> escape(data["type"])
    end
  end

  def activity_content(%{"content" => content}) do
    content
    |> String.replace(~r/[\n\r]/, "")
  end

  def activity_content(_), do: ""

  def activity_context(activity), do: escape(activity.data["context"])

  def attachment_href(attachment) do
    attachment["url"]
    |> hd()
    |> Map.get("href")
  end

  def attachment_type(attachment) do
    attachment["url"]
    |> hd()
    |> Map.get("mediaType")
  end

  def get_href(id) do
    with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do
      external_url
    else
      _e -> id
    end
  end

  def escape(html) do
    html
    |> html_escape()
    |> safe_to_string()
  end

  @spec to_rfc3339(String.t() | NativeDateTime.t()) :: String.t()
  def to_rfc3339(date) when is_binary(date) do
    date
    |> Timex.parse!("{ISO:Extended}")
    |> to_rfc3339()
  end

  def to_rfc3339(nd) do
    nd
    |> Timex.to_datetime()
    |> Timex.format!("{RFC3339}")
  end

  @spec to_rfc2822(String.t() | DateTime.t() | NativeDateTime.t()) :: String.t()
  def to_rfc2822(datestr) when is_binary(datestr) do
    datestr
    |> Timex.parse!("{ISO:Extended}")
    |> to_rfc2822()
  end

  def to_rfc2822(%DateTime{} = date) do
    date
    |> DateTime.to_naive()
    |> NaiveDateTime.to_erl()
    |> rfc2822_from_erl()
  end

  def to_rfc2822(nd) do
    nd
    |> Timex.to_datetime()
    |> DateTime.to_naive()
    |> NaiveDateTime.to_erl()
    |> rfc2822_from_erl()
  end

  @doc """
  Builds a RFC2822 timestamp from an Erlang timestamp
  [RFC2822 3.3 - Date and Time Specification](https://tools.ietf.org/html/rfc2822#section-3.3)
  This function always assumes the Erlang timestamp is in Universal time, not Local time
  """
  def rfc2822_from_erl({{year, month, day} = date, {hour, minute, second}}) do
    day_name = Enum.at(@days, :calendar.day_of_the_week(date) - 1)
    month_name = Enum.at(@months, month - 1)

    date_part = "#{day_name}, #{day} #{month_name} #{year}"
    time_part = "#{pad(hour)}:#{pad(minute)}:#{pad(second)}"

    date_part <> " " <> time_part <> " +0000"
  end

  defp pad(num) do
    num
    |> Integer.to_string()
    |> String.pad_leading(2, "0")
  end
end
