defmodule Towerops.Topology.DeviceLink do
  @moduledoc """
  Schema for links between network devices discovered via topology protocols.

  Links represent physical or logical connections between devices, identified
  through LLDP, CDP, MAC table analysis, ARP inference, wireless associations,
  or manual configuration.
  """
  use Ecto.Schema

  import Ecto.Changeset

  alias Towerops.Devices.Device
  alias Towerops.Snmp.Interface
  alias Towerops.Topology.DeviceLinkEvidence

  @valid_link_types ~w(lldp cdp mac_match wireless_association arp_inference manual)

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  schema "device_links" do
    field :link_type, :string
    field :confidence, :float, default: 0.5
    field :metadata, :map, default: %{}
    field :discovered_remote_name, :string
    field :discovered_remote_ip, :string
    field :discovered_remote_mac, :string
    field :last_confirmed_at, :utc_datetime

    belongs_to :source_device, Device
    belongs_to :target_device, Device
    belongs_to :source_interface, Interface
    belongs_to :target_interface, Interface

    has_many :evidence, DeviceLinkEvidence

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(link, attrs) do
    link
    |> cast(attrs, [
      :source_device_id,
      :target_device_id,
      :source_interface_id,
      :target_interface_id,
      :link_type,
      :confidence,
      :metadata,
      :discovered_remote_name,
      :discovered_remote_ip,
      :discovered_remote_mac,
      :last_confirmed_at
    ])
    |> validate_required([:source_device_id, :link_type, :last_confirmed_at])
    |> validate_inclusion(:link_type, @valid_link_types)
    |> validate_number(:confidence,
      greater_than_or_equal_to: 0.0,
      less_than_or_equal_to: 1.0
    )
    |> foreign_key_constraint(:source_device_id)
    |> foreign_key_constraint(:target_device_id)
    |> unique_constraint([:source_device_id, :source_interface_id, :discovered_remote_mac],
      name: :device_links_src_iface_remote_mac
    )
  end
end
