defmodule Tarakan.Fixes.FixPropagation do
  @moduledoc """
  One repository a fix template was carried to.

  Status tracks the carry, not the fix: `open` while the job is outstanding,
  `fixed` once that repository's own finding settles as fixed, and `stale` when
  the target finding was resolved some other way. The finding's own status
  stays the authority - a propagation never marks anything fixed by itself.
  """

  use Ecto.Schema
  import Ecto.Changeset

  alias Tarakan.Fixes.FixTemplate
  alias Tarakan.Repositories.Repository
  alias Tarakan.Scans.CanonicalFinding
  alias Tarakan.Work.ReviewTask

  @statuses ~w(open fixed stale)

  schema "fix_propagations" do
    field :status, :string, default: "open"

    belongs_to :fix_template, FixTemplate
    belongs_to :canonical_finding, CanonicalFinding
    belongs_to :repository, Repository
    belongs_to :review_task, ReviewTask

    timestamps(type: :utc_datetime_usec)
  end

  def statuses, do: @statuses

  @doc false
  def changeset(propagation, attrs) do
    propagation
    |> cast(attrs, [
      :fix_template_id,
      :canonical_finding_id,
      :repository_id,
      :review_task_id,
      :status
    ])
    |> validate_required([:fix_template_id, :canonical_finding_id, :repository_id])
    |> validate_inclusion(:status, @statuses)
    |> unique_constraint([:fix_template_id, :canonical_finding_id])
  end
end
