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

  import Towerops.AccountsFixtures

  alias Towerops.Snmp.Device
  alias Towerops.Snmp.MemoryPool

  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 Router",
        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-router",
        sys_descr: "Test Router"
      })
      |> Repo.insert!()

    %{device: device_schema, snmp_device: snmp_device}
  end

  describe "changeset/2" do
    test "valid changeset with all required fields", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        pool_index: "1",
        pool_type: "ram"
      }

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

    test "valid changeset with all fields", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        pool_index: "1",
        pool_name: "Physical Memory",
        pool_type: "ram",
        total_bytes: 8_589_934_592,
        used_bytes: 4_294_967_296,
        last_checked_at: DateTime.utc_now(),
        metadata: %{"source" => "host-resources"}
      }

      changeset = MemoryPool.changeset(%MemoryPool{}, attrs)
      assert changeset.valid?
      assert get_field(changeset, :pool_name) == "Physical Memory"
      assert get_field(changeset, :total_bytes) == 8_589_934_592
      assert get_field(changeset, :used_bytes) == 4_294_967_296
    end

    test "invalid changeset without snmp_device_id" do
      attrs = %{
        pool_index: "1",
        pool_type: "ram"
      }

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

    test "invalid changeset without pool_index", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        pool_type: "ram"
      }

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

    test "invalid changeset without pool_type", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        pool_index: "1"
      }

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

    test "invalid pool_type value", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        pool_index: "1",
        pool_type: "invalid"
      }

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

    test "valid pool_type values", %{snmp_device: snmp_device} do
      for pool_type <- ~w(ram swap cache buffer other) do
        attrs = %{
          snmp_device_id: snmp_device.id,
          pool_index: pool_type,
          pool_type: pool_type
        }

        changeset = MemoryPool.changeset(%MemoryPool{}, attrs)
        assert changeset.valid?, "pool_type '#{pool_type}' should be valid"
      end
    end
  end

  describe "unique constraint" do
    test "prevents duplicate pool_index on same device", %{snmp_device: snmp_device} do
      # First pool - use Repo.insert_all for speed
      now = DateTime.truncate(DateTime.utc_now(), :second)

      pool1 = %{
        id: Ecto.UUID.generate(),
        snmp_device_id: snmp_device.id,
        pool_index: "1",
        pool_type: "ram",
        inserted_at: now,
        updated_at: now
      }

      Repo.insert_all(MemoryPool, [pool1])

      # Attempt to insert duplicate using changeset (to test constraint)
      attrs = %{
        snmp_device_id: snmp_device.id,
        pool_index: "1",
        pool_type: "ram"
      }

      changeset = MemoryPool.changeset(%MemoryPool{}, attrs)

      assert {:error, changeset} = Repo.insert(changeset)
      errors = errors_on(changeset)

      assert "has already been taken" in Map.get(errors, :pool_index, []) or
               "has already been taken" in Map.get(errors, :snmp_device_id, [])
    end
  end
end
