defmodule BerrypodWeb.FaviconController do
  use BerrypodWeb, :controller

  alias Berrypod.Media
  alias Berrypod.Settings

  @one_day 86_400

  def favicon_svg(conn, _params) do
    case Media.get_favicon_variants() do
      %{svg: svg} when is_binary(svg) ->
        conn
        |> maybe_not_modified("svg")
        |> serve("image/svg+xml", svg)

      _ ->
        send_resp(conn, 404, "")
    end
  end

  def favicon_32(conn, _params), do: serve_variant(conn, :png_32)
  def apple_touch_icon(conn, _params), do: serve_variant(conn, :png_180)
  def icon_192(conn, _params), do: serve_variant(conn, :png_192)
  def icon_512(conn, _params), do: serve_variant(conn, :png_512)

  def webmanifest(conn, _params) do
    settings = Settings.get_theme_settings()
    site_name = Settings.site_name()

    short_name =
      case settings.favicon_short_name do
        name when is_binary(name) and name != "" -> name
        _ -> String.slice(site_name, 0, 12)
      end

    manifest = %{
      name: site_name,
      short_name: short_name,
      theme_color: settings.accent_color || "#000000",
      background_color: settings.icon_background_color || "#ffffff",
      display: "minimal-ui",
      start_url: "/",
      icons: [
        %{src: "/icon-192.png", sizes: "192x192", type: "image/png"},
        %{src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "maskable"}
      ]
    }

    conn
    |> put_resp_content_type("application/manifest+json")
    |> put_resp_header("cache-control", "public, max-age=#{@one_day}")
    |> json(manifest)
  end

  defp serve_variant(conn, field) do
    case Media.get_favicon_variants() do
      nil ->
        send_resp(conn, 404, "")

      variants ->
        case Map.get(variants, field) do
          data when is_binary(data) ->
            conn
            |> maybe_not_modified(variants)
            |> serve("image/png", data)

          _ ->
            send_resp(conn, 404, "")
        end
    end
  end

  defp maybe_not_modified(%{halted: true} = conn, _), do: conn

  defp maybe_not_modified(conn, variants) do
    etag = build_etag(variants)

    case get_req_header(conn, "if-none-match") do
      [^etag] ->
        conn
        |> put_resp_header("cache-control", "public, max-age=#{@one_day}")
        |> put_resp_header("etag", etag)
        |> send_resp(304, "")
        |> halt()

      _ ->
        conn
        |> put_resp_header("cache-control", "public, max-age=#{@one_day}")
        |> put_resp_header("etag", etag)
    end
  end

  defp serve(%{halted: true} = conn, _content_type, _data), do: conn

  defp serve(conn, content_type, data) do
    conn
    |> put_resp_content_type(content_type)
    |> send_resp(200, data)
  end

  defp build_etag(%{generated_at: %DateTime{} = dt}),
    do: ~s("fav-#{DateTime.to_unix(dt)}")

  defp build_etag(_), do: ~s("fav-0")
end
