defmodule PhilomenaWeb.ImageView do
  use PhilomenaWeb, :view

  alias Philomena.Tags.Tag

  def show_vote_counts?(%{hide_vote_counts: true}), do: false
  def show_vote_counts?(_user), do: true

  # this is a bit ridculous
  def render_intent(_conn, %{thumbnails_generated: false}, _size), do: :not_rendered
  def render_intent(conn, image, size) do
    uris = thumb_urls(image, false)
    vid? = image.image_mime_type == "video/webm"
    gif? = image.image_mime_type == "image/gif"
    tags = Tag.display_order(image.tags) |> Enum.map_join(", ", & &1.name)
    alt = "Size: #{image.image_width}x#{image.image_height} | Tagged: #{tags}"

    hidpi? = conn.cookies["hidpi"] == "true"
    webm? = conn.cookies["webm"] == "true"
    use_gif? = vid? and not webm? and size in ~W(thumb thumb_small thumb_tiny)a
    filtered? = filter_or_spoiler_hits?(conn, image)

    cond do
      filtered? and vid? ->
        {:filtered_video, alt}

      filtered? and not vid? ->
        {:filtered_image, alt}

      hidpi? and not (gif? or vid?) ->
        {:hidpi, uris[size], uris[:medium], alt}

      not vid? or use_gif? ->
        {:image, String.replace(uris[size], ".webm", ".gif"), alt}

      true ->
        {:video, uris[size], String.replace(uris[size], ".webm", ".mp4"), alt}
    end
  end

  def thumb_urls(image, show_hidden) do
    %{
      thumb_tiny: thumb_url(image, show_hidden, :thumb_tiny),
      thumb_small: thumb_url(image, show_hidden, :thumb_small),
      thumb: thumb_url(image, show_hidden, :thumb),
      small: thumb_url(image, show_hidden, :small),
      medium: thumb_url(image, show_hidden, :medium),
      large: thumb_url(image, show_hidden, :large),
      tall: thumb_url(image, show_hidden, :tall),
      full: pretty_url(image, true, false)
    }
    |> append_gif_urls(image, show_hidden)
  end

  defp append_gif_urls(urls, %{image_mime_type: "image/gif"} = image, show_hidden) do
    full_url = thumb_url(image, show_hidden, :full)

    Map.merge(
      urls,
      %{
        webm: String.replace(full_url, ".gif", ".webm"),
        mp4: String.replace(full_url, ".gif", ".mp4")
      }
    )
  end
  defp append_gif_urls(urls, _image, _show_hidden), do: urls

  def thumb_url(image, show_hidden, name) do
    %{year: year, month: month, day: day} = image.created_at
    deleted = image.hidden_from_users
    root = image_url_root()
    format =
      image.image_format
      |> String.downcase()
      |> thumb_format(name)

    id_fragment =
      if deleted and show_hidden do
        "#{image.id}-#{image.hidden_image_key}"
      else
        "#{image.id}"
      end

    "#{root}/#{year}/#{month}/#{day}/#{id_fragment}/#{name}.#{format}"
  end

  def pretty_url(image, short, download) do
    %{year: year, month: month, day: day} = image.created_at
    root = image_url_root()

    view = if download, do: "download", else: "view"
    filename = if short, do: image.id, else: image.file_name_cache
    format =
      image.image_format
      |> String.downcase()
      |> thumb_format(nil)

    "#{root}/#{view}/#{year}/#{month}/#{day}/#{filename}.#{format}"
  end

  def image_url_root do
    Application.get_env(:philomena, :image_url_root)
  end

  def image_container_data(image, size) do
    [
      image_id: image.id,
      image_tags: Jason.encode!(Enum.map(image.tags, & &1.id)),
      image_tag_aliases: image.tag_list_plus_alias_cache,
      score: image.score,
      faves: image.faves_count,
      upvotes: image.upvotes_count,
      downvotes: image.downvotes_count,
      comment_count: image.comments_count,
      created_at: NaiveDateTime.to_iso8601(image.created_at),
      source_url: image.source_url,
      uris: Jason.encode!(thumb_urls(image, false)),
      width: image.image_width,
      height: image.image_height,
      aspect_ratio: image.image_aspect_ratio,
      size: size
    ]
  end

  def image_container(image, size, block) do
    content_tag(:div, block.(), class: "image-container #{size}", data: image_container_data(image, size))
  end

  def display_order(tags) do
    Tag.display_order(tags)
  end

  def scope(conn), do: Philomena.ImageScope.scope(conn)

  def anonymous_by_default?(conn) do
    conn.assigns.current_user.anonymous_by_default
  end

  def info_row(_conn, []), do: []
  def info_row(conn, [{tag, description, dnp_entries}]) do
    render PhilomenaWeb.TagView, "_tag_info_row.html", conn: conn, tag: tag, body: description, dnp_entries: dnp_entries
  end
  def info_row(conn, tags) do
    render PhilomenaWeb.TagView, "_tags_row.html", conn: conn, tags: tags
  end

  def deleter(%{deleter: %{name: name}}), do: name
  def deleter(_image), do: "System"

  defp thumb_format("svg", _name), do: "png"
  defp thumb_format(_, :rendered), do: "png"
  defp thumb_format(format, _name), do: format

  defp image_filter_data(image) do
    %{
      id:                     image.id,
      "namespaced_tags.name": String.split(image.tag_list_plus_alias_cache, ", "),
      score:                  image.score,
      faves:                  image.faves_count,
      upvotes:                image.upvotes_count,
      downvotes:              image.downvotes_count,
      comment_count:          image.comments_count,
      created_at:             image.created_at,
      first_seen_at:          image.first_seen_at,
      source_url:             image.source_url,
      width:                  image.image_width,
      height:                 image.image_height,
      aspect_ratio:           image.image_aspect_ratio,
      sha512_hash:            image.image_sha512_hash,
      orig_sha512_hash:       image.image_orig_sha512_hash
    }
  end

  def filter_or_spoiler_hits?(conn, image) do
    tag_filter_or_spoiler_hits?(conn, image) or complex_filter_or_spoiler_hits?(conn, image)
  end

  defp tag_filter_or_spoiler_hits?(conn, image) do
    filter = conn.assigns.current_filter
    filtered_tag_ids = MapSet.new(filter.spoilered_tag_ids ++ filter.hidden_tag_ids)
    image_tag_ids = MapSet.new(image.tags, & &1.id)

    MapSet.size(MapSet.intersection(filtered_tag_ids, image_tag_ids)) > 0
  end

  defp complex_filter_or_spoiler_hits?(conn, image) do
    doc = image_filter_data(image)
    complex_filter = conn.assigns.compiled_complex_filter
    complex_spoiler = conn.assigns.compiled_complex_spoiler
    query = %{
      bool: %{
        should: [complex_filter, complex_spoiler]
      }
    }

    Search.Evaluator.hits?(doc, query)
  end
end