defmodule Microwaveprop.Pskr.RecalibrationRun do
  @moduledoc """
  One execution of the PSKR-driven recalibration analysis.

  A row is written every time `Pskr.Recalibrator.run/0` fires
  (weekly via `Workers.PskrRecalibrationWorker`). The status field
  separates an actually-completed analysis from runs that bailed
  early because the corpus was too thin to produce useful bins.

  Status values:
    * `"completed"` — analysis ran and `pskr_feature_bins` rows
      were written for at least one (band, feature)
    * `"skipped_insufficient_data"` — corpus had < threshold
      samples, no bins emitted
    * `"failed"` — exception during analysis (notes carries
      reason)
  """
  use Ecto.Schema

  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id

  schema "pskr_recalibration_runs" do
    field :run_at, :utc_datetime
    field :status, :string
    field :sample_count, :integer, default: 0
    field :band_count, :integer, default: 0
    field :earliest_sample, :utc_datetime
    field :latest_sample, :utc_datetime
    field :avg_spots_per_sample, :float
    field :notes, :string

    has_many :feature_bins, Microwaveprop.Pskr.FeatureBin, foreign_key: :run_id

    timestamps(type: :utc_datetime)
  end

  @type t :: %__MODULE__{}

  @cast_fields ~w(run_at status sample_count band_count earliest_sample latest_sample
                  avg_spots_per_sample notes)a

  @required_fields ~w(run_at status)a

  @spec changeset(t(), map()) :: Ecto.Changeset.t()
  def changeset(record, attrs) do
    record
    |> cast(attrs, @cast_fields)
    |> validate_required(@required_fields)
    |> validate_inclusion(:status, ~w(completed skipped_insufficient_data failed))
  end
end
