defmodule Core.Schema.Vulnerability do
  use Piazza.Ecto.Schema
  alias Core.Schema.{DockerImage}

  defenum Grade, low: 0, medium: 1, high: 2, critical: 3, none: 4
  defenum Vector, network: 0, adjacent: 1, local: 2, physical: 3
  defenum Requirement, none: 0, required: 1

  schema "vulnerabilities" do
    field :title,             :string
    field :description,       :string
    field :vulnerability_id,  :string
    field :package,           :string
    field :installed_version, :string
    field :fixed_version,     :string
    field :source,            :string
    field :url,               :string
    field :severity,          Grade
    field :score,             :float

    embeds_one :cvss, CVSS do
      field :attack_vector,       Vector
      field :attack_complexity,   Grade
      field :privileges_required, Grade
      field :user_interaction,    Requirement
      field :confidentiality,     Grade
      field :integrity,           Grade
      field :availability,        Grade
    end

    embeds_one :layer, Layer do
      field :digest,  :string
      field :diff_id, :string
    end

    belongs_to :image, DockerImage

    timestamps()
  end

  @valid ~w(
    vulnerability_id
    package
    installed_version
    fixed_version
    source
    url
    severity
    score
    title
    description
  )a


  def changeset(schema, attrs \\ %{}) do
    schema
    |> cast(attrs, @valid)
    |> cast_embed(:layer, with: &layer_changeset/2)
    |> cast_embed(:cvss, with: &cvss_changeset/2)
    |> foreign_key_constraint(:image_id)
  end

  @cvss_valid ~w(
    attack_vector
    attack_complexity
    privileges_required
    user_interaction
    confidentiality
    integrity
    availability
  )a

  def cvss_changeset(schema, attrs \\ %{}) do
    schema
    |> cast(attrs, @cvss_valid)
  end

  @layer_valid ~w(digest diff_id)a

  def layer_changeset(schema, attrs \\ %{}) do
    schema
    |> cast(attrs, @layer_valid)
  end
end
