defmodule Towerops.Devices.DeviceFirmwareHistoryTest do
  use Towerops.DataCase, async: true

  alias Towerops.Devices.DeviceFirmwareHistory
  alias Towerops.Snmp.Device

  describe "changeset/2" do
    test "valid changeset with all required fields" do
      device = insert_snmp_device()

      attrs = %{
        snmp_device_id: device.id,
        new_version: "7.14.1",
        detected_at: DateTime.utc_now()
      }

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

      assert changeset.valid?
    end

    test "valid changeset with old_version (upgrade scenario)" do
      device = insert_snmp_device()

      attrs = %{
        snmp_device_id: device.id,
        old_version: "7.13.5",
        new_version: "7.14.1",
        detected_at: DateTime.utc_now(),
        detection_method: "polling"
      }

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

      assert changeset.valid?
    end

    test "valid changeset with detection_method" do
      device = insert_snmp_device()

      attrs = %{
        snmp_device_id: device.id,
        new_version: "7.14.1",
        detected_at: DateTime.utc_now(),
        detection_method: "discovery"
      }

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

      assert changeset.valid?
    end

    test "requires snmp_device_id" do
      attrs = %{
        new_version: "7.14.1",
        detected_at: DateTime.utc_now()
      }

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

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

    test "requires new_version" do
      device = insert_snmp_device()

      attrs = %{
        snmp_device_id: device.id,
        detected_at: DateTime.utc_now()
      }

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

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

    test "requires detected_at" do
      device = insert_snmp_device()

      attrs = %{
        snmp_device_id: device.id,
        new_version: "7.14.1"
      }

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

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

    test "allows nil old_version (first discovery)" do
      device = insert_snmp_device()

      attrs = %{
        snmp_device_id: device.id,
        old_version: nil,
        new_version: "7.14.1",
        detected_at: DateTime.utc_now()
      }

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

      assert changeset.valid?
    end

    test "allows nil detection_method" do
      device = insert_snmp_device()

      attrs = %{
        snmp_device_id: device.id,
        new_version: "7.14.1",
        detected_at: DateTime.utc_now(),
        detection_method: nil
      }

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

      assert changeset.valid?
    end
  end

  describe "associations" do
    test "belongs_to snmp_device" do
      device = insert_snmp_device()

      {:ok, history} =
        %DeviceFirmwareHistory{}
        |> DeviceFirmwareHistory.changeset(%{
          snmp_device_id: device.id,
          new_version: "7.14.1",
          detected_at: DateTime.utc_now()
        })
        |> Repo.insert()

      # Load association
      history = Repo.preload(history, :snmp_device)

      assert history.snmp_device.id == device.id
    end

    test "cascades delete when snmp_device is deleted" do
      device = insert_snmp_device()

      {:ok, _history} =
        %DeviceFirmwareHistory{}
        |> DeviceFirmwareHistory.changeset(%{
          snmp_device_id: device.id,
          new_version: "7.14.1",
          detected_at: DateTime.utc_now()
        })
        |> Repo.insert()

      # Delete the device
      Repo.delete!(device)

      # History should be deleted too (on_delete: :delete_all)
      assert Repo.aggregate(DeviceFirmwareHistory, :count) == 0
    end
  end

  describe "database operations" do
    test "can insert multiple history entries for same device" do
      device = insert_snmp_device()
      now = DateTime.utc_now()

      {:ok, _history1} =
        %DeviceFirmwareHistory{}
        |> DeviceFirmwareHistory.changeset(%{
          snmp_device_id: device.id,
          new_version: "7.13.5",
          detected_at: DateTime.add(now, -3600, :second)
        })
        |> Repo.insert()

      {:ok, _history2} =
        %DeviceFirmwareHistory{}
        |> DeviceFirmwareHistory.changeset(%{
          snmp_device_id: device.id,
          old_version: "7.13.5",
          new_version: "7.14.1",
          detected_at: now
        })
        |> Repo.insert()

      assert Repo.aggregate(DeviceFirmwareHistory, :count) == 2
    end

    test "stores timestamps correctly" do
      device = insert_snmp_device()
      detected_time = DateTime.truncate(DateTime.utc_now(), :microsecond)

      {:ok, history} =
        %DeviceFirmwareHistory{}
        |> DeviceFirmwareHistory.changeset(%{
          snmp_device_id: device.id,
          new_version: "7.14.1",
          detected_at: detected_time
        })
        |> Repo.insert()

      # Reload from database
      reloaded = Repo.get!(DeviceFirmwareHistory, history.id)

      # Should have inserted_at but no updated_at
      assert reloaded.inserted_at
      refute Map.has_key?(reloaded, :updated_at)
      # Verify detected_at was stored and is close to the input time
      assert reloaded.detected_at
      diff = DateTime.diff(reloaded.detected_at, detected_time, :second)
      assert abs(diff) <= 1
    end
  end

  # Helper function to create a test SNMP device
  defp insert_snmp_device do
    import Towerops.AccountsFixtures
    import Towerops.OrganizationsFixtures

    alias Towerops.Devices
    alias Towerops.Sites

    # Create organization and site
    user = user_fixture()
    organization = organization_fixture(user.id)
    {:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})

    # Create equipment with SNMP enabled
    {:ok, device} =
      Devices.create_device(%{
        name: "Test Router",
        ip_address: "192.168.1.1",
        site_id: site.id,
        organization_id: organization.id,
        snmp_enabled: true,
        snmp_community: "public",
        snmp_version: "2c"
      })

    # Create SNMP device record
    snmp_device =
      %Device{}
      |> Device.changeset(%{
        device_id: device.id,
        sys_descr: "RouterOS v7.13.5",
        sys_object_id: "1.3.6.1.4.1.14988.1",
        sys_uptime: 0,
        sys_name: "test-router",
        sys_location: "Test Lab"
      })
      |> Repo.insert!()

    snmp_device
  end
end
