defmodule Towerops.Snmp.MacAddressTest do
  use Towerops.DataCase, async: true

  import Towerops.AccountsFixtures

  alias Towerops.Snmp.Device
  alias Towerops.Snmp.Interface
  alias Towerops.Snmp.MacAddress

  setup do
    user = user_fixture()

    {:ok, organization} =
      Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)

    {:ok, site} =
      Towerops.Sites.create_site(%{
        name: "Test Site",
        organization_id: organization.id
      })

    {:ok, device_schema} =
      Towerops.Devices.create_device(%{
        name: "Test Switch",
        ip_address: "192.168.1.1",
        snmp_enabled: true,
        snmp_version: "2c",
        snmp_community: "public",
        snmp_port: 161,
        site_id: site.id,
        organization_id: organization.id
      })

    snmp_device =
      %Device{}
      |> Device.changeset(%{
        device_id: device_schema.id,
        sys_name: "test-switch",
        sys_descr: "Test Switch"
      })
      |> Repo.insert!()

    interface =
      %Interface{}
      |> Interface.changeset(%{
        snmp_device_id: snmp_device.id,
        if_index: 1,
        if_descr: "GigabitEthernet0/1",
        if_name: "Gi0/1",
        if_type: 6,
        if_oper_status: "up"
      })
      |> Repo.insert!()

    %{device: device_schema, snmp_device: snmp_device, interface: interface}
  end

  describe "changeset/2" do
    test "valid with required fields", %{device: device} do
      attrs = %{
        device_id: device.id,
        mac_address: "00:11:22:33:44:55",
        last_seen_at: DateTime.utc_now()
      }

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

    test "valid with all fields", %{device: device, interface: interface} do
      attrs = %{
        device_id: device.id,
        interface_id: interface.id,
        mac_address: "00:11:22:33:44:55",
        vlan_id: 100,
        port_index: 1,
        entry_status: "learned",
        last_seen_at: DateTime.utc_now()
      }

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

    test "invalid without device_id" do
      attrs = %{
        mac_address: "00:11:22:33:44:55",
        last_seen_at: DateTime.utc_now()
      }

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

    test "invalid without mac_address", %{device: device} do
      attrs = %{
        device_id: device.id,
        last_seen_at: DateTime.utc_now()
      }

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

    test "invalid without last_seen_at", %{device: device} do
      attrs = %{
        device_id: device.id,
        mac_address: "00:11:22:33:44:55"
      }

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

    test "enforces unique constraint on device_id, mac_address, vlan_id", %{device: device} do
      now = DateTime.truncate(DateTime.utc_now(), :second)

      {:ok, _} =
        %MacAddress{}
        |> MacAddress.changeset(%{
          device_id: device.id,
          mac_address: "00:11:22:33:44:55",
          vlan_id: 100,
          last_seen_at: now
        })
        |> Repo.insert()

      {:error, changeset} =
        %MacAddress{}
        |> MacAddress.changeset(%{
          device_id: device.id,
          mac_address: "00:11:22:33:44:55",
          vlan_id: 100,
          last_seen_at: now
        })
        |> Repo.insert()

      assert "has already been taken" in errors_on(changeset)[:device_id]
    end

    test "allows same mac_address on different VLANs", %{device: device} do
      now = DateTime.truncate(DateTime.utc_now(), :second)

      {:ok, _} =
        %MacAddress{}
        |> MacAddress.changeset(%{
          device_id: device.id,
          mac_address: "00:11:22:33:44:55",
          vlan_id: 100,
          last_seen_at: now
        })
        |> Repo.insert()

      {:ok, _} =
        %MacAddress{}
        |> MacAddress.changeset(%{
          device_id: device.id,
          mac_address: "00:11:22:33:44:55",
          vlan_id: 200,
          last_seen_at: now
        })
        |> Repo.insert()
    end
  end
end
