defmodule Towerops.Snmp.Processor do
  @moduledoc """
  SNMP processor schema for tracking CPU/processor information.

  Processors are discovered via:
  - HOST-RESOURCES-MIB hrProcessorTable
  - CISCO-PROCESS-MIB cpmCPUTotalTable
  - UCD-SNMP-MIB laTable (for system load averages)

  Processor types:
  - "hr_processor" - HOST-RESOURCES-MIB hrProcessorTable entry
  - "cisco_cpu" - Cisco CPU from CISCO-PROCESS-MIB
  - "ucd_cpu" - UCD-SNMP-MIB CPU statistics
  """
  use Ecto.Schema

  import Ecto.Changeset

  alias Ecto.Association.NotLoaded
  alias Towerops.Snmp.Device

  @valid_processor_types ~w(hr_processor cisco_cpu ucd_cpu)

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  schema "snmp_processors" do
    field :processor_index, :string
    field :description, :string
    field :processor_type, :string
    field :load_percent, :float
    field :last_checked_at, :utc_datetime
    field :metadata, :map, default: %{}

    belongs_to :snmp_device, Device

    timestamps(type: :utc_datetime)
  end

  @type t :: %__MODULE__{
          id: Ecto.UUID.t(),
          processor_index: String.t(),
          description: String.t() | nil,
          processor_type: String.t(),
          load_percent: float() | nil,
          last_checked_at: DateTime.t() | nil,
          metadata: map(),
          snmp_device_id: Ecto.UUID.t(),
          snmp_device: NotLoaded.t() | Device.t(),
          inserted_at: DateTime.t(),
          updated_at: DateTime.t()
        }

  @doc false
  def changeset(processor, attrs) do
    processor
    |> cast(attrs, [
      :snmp_device_id,
      :processor_index,
      :description,
      :processor_type,
      :load_percent,
      :last_checked_at,
      :metadata
    ])
    |> validate_required([:snmp_device_id, :processor_index, :processor_type])
    |> validate_inclusion(:processor_type, @valid_processor_types)
    |> validate_load_percent()
    |> unique_constraint([:snmp_device_id, :processor_index])
    |> foreign_key_constraint(:snmp_device_id, match: :suffix)
  end

  defp validate_load_percent(changeset) do
    case get_field(changeset, :load_percent) do
      nil -> changeset
      load when load >= 0 and load <= 100 -> changeset
      _ -> add_error(changeset, :load_percent, "must be between 0 and 100")
    end
  end
end
