defmodule TarakanWeb.BadgeController do
  @moduledoc """
  Embeddable SVG badges for a repository's security posture.

  Served without authentication so a README on any host can render one, and
  cached briefly so an embedded badge cannot be used to hammer the database.
  The SVG is self-contained (no external fonts or images) because most hosts
  proxy and sanitize badge images.
  """

  use TarakanWeb, :controller

  alias Tarakan.Repositories
  alias Tarakan.SecurityPosture

  @cache_seconds 900

  def show(conn, %{"host" => host, "owner" => owner, "name" => name}) do
    render_badge(conn, Repositories.get_repository(host, owner, name))
  end

  def show(conn, %{"owner" => owner, "name" => name}) do
    render_badge(conn, Repositories.get_repository_by_slug(owner, name))
  end

  defp render_badge(conn, repository) do
    badge =
      case repository do
        %{listing_status: "listed"} = repository ->
          SecurityPosture.cached_badge(repository)

        _unlisted_or_missing ->
          %{label: "security", message: "not listed", color: "#5a6a72"}
      end

    conn
    |> put_resp_content_type("image/svg+xml")
    |> put_resp_header("cache-control", "public, max-age=#{@cache_seconds}")
    |> send_resp(200, svg(badge))
  end

  # Flat two-part badge. Widths are estimated from character count because the
  # SVG carries no font metrics; 6.6px per character matches the 11px
  # sans-serif stack closely enough for the shape to stay tight.
  defp svg(%{label: label, message: message, color: color}) do
    label_width = text_width(label)
    message_width = text_width(message)
    total = label_width + message_width

    """
    <svg xmlns="http://www.w3.org/2000/svg" width="#{total}" height="20" role="img" aria-label="#{escape(label)}: #{escape(message)}">
      <title>#{escape(label)}: #{escape(message)}</title>
      <g shape-rendering="crispEdges">
        <rect width="#{label_width}" height="20" fill="#1c1f22"/>
        <rect x="#{label_width}" width="#{message_width}" height="20" fill="#{escape(color)}"/>
      </g>
      <g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,Geneva,sans-serif" font-size="11">
        <text x="#{div(label_width, 2)}" y="14">#{escape(label)}</text>
        <text x="#{label_width + div(message_width, 2)}" y="14">#{escape(message)}</text>
      </g>
    </svg>
    """
  end

  defp text_width(text), do: round(String.length(text) * 6.6) + 12

  defp escape(value) do
    value
    |> to_string()
    |> String.replace("&", "&amp;")
    |> String.replace("<", "&lt;")
    |> String.replace(">", "&gt;")
    |> String.replace("\"", "&quot;")
  end
end
