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

  alias Towerops.Snmp.Processor

  describe "changeset/2" do
    setup do
      # Create a valid snmp_device_id for testing
      snmp_device_id = Ecto.UUID.generate()
      {:ok, snmp_device_id: snmp_device_id}
    end

    test "valid changeset with required fields", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "hr_1",
        processor_type: "hr_processor"
      }

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

      assert changeset.valid?
      assert get_change(changeset, :processor_index) == "hr_1"
      assert get_change(changeset, :processor_type) == "hr_processor"
    end

    test "valid changeset with all fields", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "cisco_1",
        description: "Cisco CPU Core 1",
        processor_type: "cisco_cpu",
        load_percent: 45.5,
        last_checked_at: DateTime.utc_now(),
        metadata: %{cores: 4}
      }

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

      assert changeset.valid?
      assert get_change(changeset, :description) == "Cisco CPU Core 1"
      assert get_change(changeset, :load_percent) == 45.5
      assert get_change(changeset, :metadata) == %{cores: 4}
    end

    test "requires snmp_device_id" do
      attrs = %{
        processor_index: "hr_1",
        processor_type: "hr_processor"
      }

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

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).snmp_device_id
    end

    test "requires processor_index", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_type: "hr_processor"
      }

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

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).processor_index
    end

    test "requires processor_type", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "hr_1"
      }

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

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).processor_type
    end

    test "validates processor_type inclusion", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "invalid_1",
        processor_type: "invalid_type"
      }

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

      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).processor_type
    end

    test "accepts all valid processor types", %{snmp_device_id: snmp_device_id} do
      for processor_type <- ~w(hr_processor cisco_cpu ucd_cpu) do
        attrs = %{
          snmp_device_id: snmp_device_id,
          processor_index: "test_1",
          processor_type: processor_type
        }

        changeset = Processor.changeset(%Processor{}, attrs)
        assert changeset.valid?, "Expected #{processor_type} to be valid"
      end
    end

    test "validates load_percent is between 0 and 100", %{snmp_device_id: snmp_device_id} do
      # Valid at 0
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "hr_1",
        processor_type: "hr_processor",
        load_percent: 0.0
      }

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

      # Valid at 100
      attrs = %{attrs | load_percent: 100.0}
      changeset = Processor.changeset(%Processor{}, attrs)
      assert changeset.valid?

      # Valid in middle
      attrs = %{attrs | load_percent: 50.5}
      changeset = Processor.changeset(%Processor{}, attrs)
      assert changeset.valid?

      # Invalid above 100
      attrs = %{attrs | load_percent: 100.1}
      changeset = Processor.changeset(%Processor{}, attrs)
      refute changeset.valid?
      assert "must be between 0 and 100" in errors_on(changeset).load_percent

      # Invalid below 0
      attrs = %{attrs | load_percent: -1.0}
      changeset = Processor.changeset(%Processor{}, attrs)
      refute changeset.valid?
      assert "must be between 0 and 100" in errors_on(changeset).load_percent
    end

    test "allows nil load_percent", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "hr_1",
        processor_type: "hr_processor",
        load_percent: nil
      }

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

    test "description can be nil", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "hr_1",
        processor_type: "hr_processor",
        description: nil
      }

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

    test "metadata defaults to empty map", %{snmp_device_id: snmp_device_id} do
      attrs = %{
        snmp_device_id: snmp_device_id,
        processor_index: "ucd_0",
        processor_type: "ucd_cpu"
      }

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

      # Default comes from schema, not changeset
      processor = %Processor{}
      assert processor.metadata == %{}
    end
  end

  describe "schema" do
    test "has correct fields" do
      fields = Processor.__schema__(:fields)
      assert :id in fields
      assert :snmp_device_id in fields
      assert :processor_index in fields
      assert :description in fields
      assert :processor_type in fields
      assert :load_percent in fields
      assert :last_checked_at in fields
      assert :metadata in fields
      assert :inserted_at in fields
      assert :updated_at in fields
    end

    test "belongs to snmp_device" do
      assocs = Processor.__schema__(:associations)
      assert :snmp_device in assocs

      assoc = Processor.__schema__(:association, :snmp_device)
      assert assoc.queryable == Towerops.Snmp.Device
    end

    test "uses binary_id for primary key" do
      assert Processor.__schema__(:type, :id) == :binary_id
    end

    test "uses binary_id for foreign keys" do
      assert Processor.__schema__(:type, :snmp_device_id) == :binary_id
    end
  end
end
