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

  import Towerops.AccountsFixtures

  alias Towerops.Snmp.ProcessorReading

  describe "changeset/2" do
    setup do
      processor = insert_processor()
      %{processor: processor}
    end

    test "valid changeset with all fields", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        load_percent: 45.5,
        status: "ok",
        checked_at: DateTime.utc_now()
      }

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

      assert changeset.valid?
      assert get_change(changeset, :processor_id) == processor.id
      assert get_change(changeset, :load_percent) == 45.5
      assert get_change(changeset, :status) == "ok"
    end

    test "valid changeset without optional load_percent", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        status: "ok",
        checked_at: DateTime.utc_now()
      }

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

      assert changeset.valid?
      refute get_change(changeset, :load_percent)
    end

    test "valid changeset with different status values", %{processor: processor} do
      for status <- ["ok", "warning", "critical", "error"] do
        attrs = %{
          processor_id: processor.id,
          status: status,
          checked_at: DateTime.utc_now()
        }

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

        assert changeset.valid?, "Status #{status} should be valid"
      end
    end

    test "invalid changeset when processor_id is missing" do
      attrs = %{
        status: "ok",
        checked_at: DateTime.utc_now()
      }

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

      refute changeset.valid?
      assert %{processor_id: ["can't be blank"]} = errors_on(changeset)
    end

    test "invalid changeset when status is missing", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        checked_at: DateTime.utc_now()
      }

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

      refute changeset.valid?
      assert %{status: ["can't be blank"]} = errors_on(changeset)
    end

    test "invalid changeset when checked_at is missing", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        status: "ok"
      }

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

      refute changeset.valid?
      assert %{checked_at: ["can't be blank"]} = errors_on(changeset)
    end

    test "invalid changeset with invalid status value", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        status: "invalid_status",
        checked_at: DateTime.utc_now()
      }

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

      refute changeset.valid?
      assert %{status: ["is invalid"]} = errors_on(changeset)
    end

    test "invalid changeset with non-existent processor_id" do
      attrs = %{
        processor_id: Ecto.UUID.generate(),
        status: "ok",
        checked_at: DateTime.utc_now()
      }

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

      assert changeset.valid?
      # Foreign key constraint will be checked at insert time
      assert {:error, changeset} = Repo.insert(changeset)
      assert %{processor_id: ["does not exist"]} = errors_on(changeset)
    end
  end

  describe "database operations" do
    setup do
      processor = insert_processor()
      %{processor: processor}
    end

    test "inserts valid processor reading", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        load_percent: 75.5,
        status: "warning",
        checked_at: DateTime.utc_now()
      }

      changeset = ProcessorReading.changeset(%ProcessorReading{}, attrs)
      assert {:ok, reading} = Repo.insert(changeset)

      assert reading.processor_id == processor.id
      assert reading.load_percent == 75.5
      assert reading.status == "warning"
      assert reading.inserted_at
    end

    test "inserts reading without load_percent", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        status: "error",
        checked_at: DateTime.utc_now()
      }

      changeset = ProcessorReading.changeset(%ProcessorReading{}, attrs)
      assert {:ok, reading} = Repo.insert(changeset)

      assert is_nil(reading.load_percent)
      assert reading.status == "error"
    end

    test "timestamps are set automatically", %{processor: processor} do
      attrs = %{
        processor_id: processor.id,
        status: "ok",
        checked_at: DateTime.utc_now()
      }

      changeset = ProcessorReading.changeset(%ProcessorReading{}, attrs)
      assert {:ok, reading} = Repo.insert(changeset)

      assert reading.inserted_at
      # updated_at should be false according to schema
      refute Map.has_key?(reading, :updated_at)
    end
  end

  # Test helper to create a processor
  defp insert_processor do
    snmp_device = insert_snmp_device()

    Repo.insert!(%Towerops.Snmp.Processor{
      snmp_device_id: snmp_device.id,
      processor_index: "1",
      processor_type: "hr_processor",
      load_percent: 0.0
    })
  end

  defp insert_snmp_device do
    device = insert_device()

    Repo.insert!(%Towerops.Snmp.Device{
      device_id: device.id,
      sys_descr: "Test Device",
      sys_object_id: "1.3.6.1.4.1.9",
      sys_name: "test-device"
    })
  end

  defp insert_device do
    org = insert_organization()
    site = insert_site(org)

    {:ok, device} =
      Towerops.Devices.create_device(%{
        name: "Test Device",
        ip_address: "192.168.1.1",
        site_id: site.id,
        organization_id: org.id
      })

    device
  end

  defp insert_organization do
    user = insert_user()

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

    org
  end

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

    site
  end

  defp insert_user do
    user_fixture()
  end
end
