defmodule Core.Docker.VulnerabilitySource do
  defmacro __using__(_) do
    quote do
      import Core.Docker.VulnerabilitySource
      @fields ~w(
        vulnerability_id
        package
        installed_version
        fixed_version
        source
        url
        severity
        cvss
        layer
        score
        title
        description
      )a

      def to_vulnerability(payload) do
        Enum.into(@fields, %{}, fn field -> {field, __field__(payload, field)} end)
      end
    end
  end

  def truncate(str, len) do
    case str do
      str when byte_size(str) >= len -> "#{String.slice(str, 0..(len - 4))}..."
      _ -> str
    end
  end

  def default(nil, default), do: default
  def default(val, _), do: val

  def grade("LOW"), do: :low
  def grade("MEDIUM"), do: :medium
  def grade("HIGH"), do: :high
  def grade("CRITICAL"), do: :critical
  def grade("L"), do: :low
  def grade("M"), do: :medium
  def grade("H"), do: :high
  def grade(_), do: :none

  def vector("N"), do: :network
  def vector("A"), do: :adjacent
  def vector("L"), do: :local
  def vector("P"), do: :physical

  def requirement("N"), do: :none
  def requirement("R"), do: :required

  def parse_v3_vector("CVSS:" <> rest) do
    [_ | parts] = String.split(rest, "/")
    Enum.map(parts, fn part ->
      case String.split(part, ":") do
        ["AV", v]  -> {:attack_vector, vector(v)}
        ["AC", c]  -> {:attack_complexity, grade(c)}
        ["PR", p]  -> {:privileges_required, grade(p)}
        ["UI", ui] -> {:user_interaction, requirement(ui)}
        ["C", c]   -> {:confidentiality, grade(c)}
        ["I", i]   -> {:integrity, grade(i)}
        ["A", a]   -> {:availability, grade(a)}
        _ -> nil
      end
    end)
    |> Enum.filter(& &1)
    |> Enum.into(%{})
  end

  defmacro deffetch(source, target, opts \\ []) do
    quote do
      def __field__(payload, unquote(target)) do
        fun = Keyword.get(unquote(opts), :resolve, & &1)
        case payload do
          %{unquote(source) => val} -> fun.(val)
          _ -> nil
        end
      end
    end
  end
end
