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

  import Towerops.AccountsFixtures

  alias Towerops.Snmp.Device
  alias Towerops.Snmp.Storage

  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,
        storage_index: 1,
        storage_type: "fixed_disk"
      }

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

    test "valid changeset with all fields", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        storage_index: 1,
        storage_type: "fixed_disk",
        description: "/dev/sda1",
        device_name: "/",
        total_bytes: 500_000_000_000,
        used_bytes: 250_000_000_000,
        allocation_units: 4096,
        last_checked_at: DateTime.utc_now(),
        metadata: %{"source" => "host-resources"}
      }

      changeset = Storage.changeset(%Storage{}, attrs)
      assert changeset.valid?
      assert get_field(changeset, :description) == "/dev/sda1"
      assert get_field(changeset, :total_bytes) == 500_000_000_000
      assert get_field(changeset, :used_bytes) == 250_000_000_000
    end

    test "invalid changeset without snmp_device_id" do
      attrs = %{
        storage_index: 1,
        storage_type: "disk"
      }

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

    test "invalid changeset without storage_index", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        storage_type: "disk"
      }

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

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

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

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

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

    test "valid storage_type values", %{snmp_device: snmp_device} do
      for {storage_type, idx} <-
            Enum.with_index(
              ~w(other ram virtual_memory fixed_disk removable_disk floppy_disk compact_disc ram_disk flash_memory network_disk)
            ) do
        attrs = %{
          snmp_device_id: snmp_device.id,
          storage_index: idx + 1,
          storage_type: storage_type
        }

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

  describe "unique constraint" do
    test "prevents duplicate storage_index on same device", %{snmp_device: snmp_device} do
      attrs = %{
        snmp_device_id: snmp_device.id,
        storage_index: 1,
        storage_type: "fixed_disk"
      }

      # Insert first storage
      changeset = Storage.changeset(%Storage{}, attrs)
      assert {:ok, _storage} = Repo.insert(changeset)

      # Attempt to insert duplicate
      changeset2 = Storage.changeset(%Storage{}, attrs)

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

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