defmodule Tarakan.Scans.FindingRegression do
  @moduledoc """
  A finding that was fixed and then seen again at a later commit.

  Scanners have no memory across time, so a reintroduced bug normally reads as
  a brand new finding. Because canonical findings are commit-pinned and carry a
  fix stamp, the same fingerprint reappearing after `fixed_at` is provably a
  regression rather than a discovery, and is recorded as one.

  One row per (finding, reappearance commit): rescanning the same commit does
  not stack duplicates, and re-fixing then regressing again records a new row.
  """

  use Ecto.Schema
  import Ecto.Changeset

  alias Tarakan.Repositories.Repository
  alias Tarakan.Scans.{CanonicalFinding, Scan}

  schema "finding_regressions" do
    field :fixed_commit_sha, :string
    field :fixed_at, :utc_datetime_usec
    field :detected_commit_sha, :string

    belongs_to :canonical_finding, CanonicalFinding
    belongs_to :repository, Repository
    belongs_to :detected_by_scan, Scan

    timestamps(type: :utc_datetime_usec)
  end

  @doc false
  def changeset(regression, attrs) do
    regression
    |> cast(attrs, [
      :canonical_finding_id,
      :repository_id,
      :detected_by_scan_id,
      :fixed_commit_sha,
      :fixed_at,
      :detected_commit_sha
    ])
    |> validate_required([:canonical_finding_id, :repository_id, :detected_commit_sha])
    |> unique_constraint([:canonical_finding_id, :detected_commit_sha],
      name: :finding_regressions_unique_detection_index
    )
  end
end
