defmodule Towerops.Topology.DeviceLinkTest do
  use Towerops.DataCase

  alias Towerops.Topology.DeviceLink

  describe "changeset/2" do
    test "valid changeset with required fields" do
      source_device_id = Ecto.UUID.generate()

      attrs = %{
        source_device_id: source_device_id,
        link_type: "lldp",
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      assert changeset.valid?
    end

    test "invalid without source_device_id" do
      attrs = %{
        link_type: "lldp",
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).source_device_id
    end

    test "invalid without link_type" do
      attrs = %{
        source_device_id: Ecto.UUID.generate(),
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).link_type
    end

    test "invalid without last_confirmed_at" do
      attrs = %{
        source_device_id: Ecto.UUID.generate(),
        link_type: "lldp"
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).last_confirmed_at
    end

    test "validates link_type inclusion rejects invalid type" do
      attrs = %{
        source_device_id: Ecto.UUID.generate(),
        link_type: "invalid",
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).link_type
    end

    test "validates all valid link_types" do
      valid_types = ~w(lldp cdp mac_match wireless_association arp_inference manual)

      for link_type <- valid_types do
        attrs = %{
          source_device_id: Ecto.UUID.generate(),
          link_type: link_type,
          last_confirmed_at: DateTime.utc_now()
        }

        changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
        assert changeset.valid?, "expected #{link_type} to be a valid link_type"
      end
    end

    test "validates confidence rejects value above 1.0" do
      attrs = %{
        source_device_id: Ecto.UUID.generate(),
        link_type: "lldp",
        confidence: 1.5,
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      refute changeset.valid?
      assert errors_on(changeset).confidence != []
    end

    test "validates confidence rejects negative value" do
      attrs = %{
        source_device_id: Ecto.UUID.generate(),
        link_type: "lldp",
        confidence: -0.1,
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      refute changeset.valid?
      assert errors_on(changeset).confidence != []
    end

    test "accepts confidence at boundaries 0.0 and 1.0" do
      base_attrs = %{
        source_device_id: Ecto.UUID.generate(),
        link_type: "lldp",
        last_confirmed_at: DateTime.utc_now()
      }

      changeset_zero = DeviceLink.changeset(%DeviceLink{}, Map.put(base_attrs, :confidence, 0.0))
      assert changeset_zero.valid?

      changeset_one = DeviceLink.changeset(%DeviceLink{}, Map.put(base_attrs, :confidence, 1.0))
      assert changeset_one.valid?
    end

    test "defaults confidence to 0.5" do
      attrs = %{
        source_device_id: Ecto.UUID.generate(),
        link_type: "lldp",
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      assert changeset.valid?
      assert Ecto.Changeset.get_field(changeset, :confidence) == 0.5
    end

    test "valid changeset with all optional fields" do
      attrs = %{
        source_device_id: Ecto.UUID.generate(),
        target_device_id: Ecto.UUID.generate(),
        source_interface_id: Ecto.UUID.generate(),
        target_interface_id: Ecto.UUID.generate(),
        link_type: "cdp",
        confidence: 0.95,
        metadata: %{"vlan" => 100},
        discovered_remote_name: "switch-01.local",
        discovered_remote_ip: "10.0.0.1",
        discovered_remote_mac: "AA:BB:CC:DD:EE:FF",
        last_confirmed_at: DateTime.utc_now()
      }

      changeset = DeviceLink.changeset(%DeviceLink{}, attrs)
      assert changeset.valid?
    end
  end
end
